• + The pilot: GPSS-Plus

    GPSS-Plus: Motor de Simulación por Eventos Discretos y Sistemas Continuos

    GPSS-Plus es un motor de simulación por eventos discretos y sistemas continuos orientado a modelar y analizar el comportamiento de sistemas reales a lo largo del tiempo. Su propósito no es programar aplicaciones, sino describir cómo funciona un sistema y observar su evolución bajo distintas condiciones.

    En esencia, opera como un laboratorio digital donde fluye un conjunto de entidades (clientes, productos, vehículos, señales o paquetes de datos) a través de una red de procesos y recursos con capacidades y demoras definidas.

    El objetivo es transformar una descripción real del sistema en un modelo ejecutable: desde una línea de producción industrial o una cadena logística, hasta el tráfico de datos en una red o un protocolo de emergencia. Al simular miles de escenarios en minutos, permite realizar previsiones, optimizar decisiones y diseñar protocolos de contingencia dentro de un entorno de simulación generalista.

    Existen diferentes enfoques para abordar el modelado de sistemas tanto por su interfaz como por su objetivo.

    Basados en Drag & Drop discretos y/o continuos:

    • DES: Se construye conectando bloques gráficos.
    • Modelado continuo: Sistemas de ecuaciones diferenciales.
    • Híbridos gráficos + scripting: Combinan DES con código externo.

    Discretos basados en lenguajes propios o generalistas:

    • Con DSL propio: Íntegramente en un lenguaje textual como GPSS clásico.
    • Sobre lenguajes generalistas: Mediante librerías de simulación.

    Gracias a la herencia de los conceptos clave de GPSS clásico, GPSS-Plus se sitúa en ambos mundos, utilizando un lenguaje propio para definir el modelo, en lugar de construirlo sobre lenguajes generalistas, y reforzando el papel de la descripción textual como representación del sistema frente a los esquemas gráficos.

    El objetivo último es que todo pueda comprenderse:

    GENERATE 5
    ADVANCE 10 {From:"Madrid", To:"Paris"}
    TERMINATE

     

    Las principales innovaciones introducidas son:

    1. La sintaxis: compatibilidad extendida

    El cambio más significativo está en el manejo de los parámetros de los bloques y comandos. En GPSS clásico, los parámetros A, B, C... ofrecían una sintaxis fija y limitada.

    Ejemplo clásico:

    ADVANCE 30,10 ; Espera entre 30 y 40 tiempos

    En GPSS-Plus, se mantiene esa estructura, pero se extiende con un formato JSON-like, que permite especificar más detalles sin romper la simplicidad:

    ADVANCE 30,10 {From:"Madrid", To:"Paris"}

    Esto hace posible integrar parámetros gráficos, de comportamiento, o lógicos, sin inventar nuevas variantes sintácticas para cada caso.

    2. Visualización gráfica

    GPSS-Plus representa visualmente las entidades y los bloques mediante elementos gráficos simples: “bolitas” que se desplazan por un canvas siguiendo exactamente el flujo del modelo. Esta representación permite observar el sistema en tiempo real, comprender su dinámica de forma inmediata y facilitar la detección de cuellos de botella o comportamientos inesperados durante la ejecución.

    Esta visualización, inexistente en GPSS clásico, introduce una nueva forma de interactuar con el modelo, donde la observación directa se convierte en una herramienta clave de validación y comprensión.

    3. Entidades virtuales (VE)

    Las entidades virtuales son la ayuda para describir lo invisible del modelo. Para quien venga de otros paradigmas, recogen lo que antes se describía fuera en otro lenguaje. Para quien se inicia, simplemente son otra entidad más.

    En GPSS-Plus, todo lo que ocurre en el sistema se desarrolla en el tiempo y puede observarse mientras sucede. Para que eso sea posible todos los comportamientos se tratan de la misma forma: Como entidades que se mueven, abstractas, visibles o un simple volumen.

    Las entidades virtuales no se visualizan, pero son entidades completas que ejecutan lógica interna o eventos del sistema. Nacen para describir ese comportamiento con ON_ENTER, un TIMER o TIMEOUT, y viven en el propio modelo.

    Según su ciclo de vida, existen tres tipos principales:

    • Reactores, que atienden eventos concretos y desaparecen.

    • Agentes, que no mueren y gestionan el universo del modelo.

    • Componentes, entidades vivas que existen como parte de otra entidad y mueren con ella.

    Con este enfoque, el modelo se convierte en un ecosistema de ejecuciones concurrentes. Una entidad puede esperar un autobús mientras atiende una llamada; un agente puede controlar el tráfico; el sol puede salir y ponerse sin que ninguna entidad principal tenga que ocuparse de ello.

    Se observa que muchos conceptos habituales de la Programación Procedural o la Orientada a Objetos se reinterpretan: el comportamiento no se encapsula en objetos jerárquicos, sino que se modela explícitamente como entidades que coexisten y actúan en paralelo dentro del sistema. 

    No hay this, ni parent, ni child: todos son brothers. Las funciones dejan de ser llamadas puntuales y pasan a convertirse en procedimientos vivos. No existe “this.respirar()” que siempre respira igual, sino una persona que camina y, al mismo tiempo, respira un aire diferente.
     

    4. Auditoría del modelo y V&V.

    Al describirse íntegramente como un modelo explícito, GPSS-Plus permite que su comportamiento pueda ser analizado, interpretado y contrastado de forma automática. Una inteligencia artificial o auditor externo puede reconstruir qué hace el sistema, detectar incoherencias internas y contrastar la intención del modelador con la ejecución real del modelo.

    Esta capacidad introduce una evolución natural de los procesos clásicos de verificación y validación (V&V), que dejan de basarse únicamente en revisión manual y análisis estadístico. Se incorpora así la coherencia entre historia (H), modelo (M) y ejecución (E) sentando las bases de un nuevo enfoque de validación.
     

    5. Variables estructuradas

    En GPSS-Plus, las variables (SAVEVALUE, ASSIGN) pueden contener números, strings, arrays u objetos.
    Esto permite trabajar con estructuras de datos complejas directamente en el modelo, sin recurrir a lenguajes externos ni perder legibilidad. Por ejemplo:

    ASSIGN DATOS, {tipo:"Estudiante", Nombre:"Antonio", Edad: 22, Calificaciones: [8,5.8,6,8]}

    6. Sistema híbrido discreto - continuo

    El modelado continuo en GPSS-Plus se apoya en una idea simple: dividir el tiempo en pequeños fotogramas. Al ejecutar estos pasos de forma sucesiva dentro de la cola de eventos, el comportamiento continuo emerge de manera natural.

    INTEGRATE, DYNAMIC, SOLVE son las herramientas básicas que permiten definir estructuras de sistema continuo integrada en el mismo corazón de lo discreto. RK4, matrices Jacobianas y Newton Raphson permiten simular fluidos, voltajes o velocidades con el mismo DSL. 

    7. Sandbox, gemelos digitales y edge software

    Los modelos de simulación pueden perseguir tres objetivos principales:
     
    - Sandbox, orientado a la simulación acotada y a la obtención de resultados, con o sin precarga de datos históricos.
    - Gemelo digital, donde el modelo acompaña a un sistema real para su verificación, control u operación predictiva.
    - Edge software, en el que el modelo constituye el propio sistema, ejecutándose de forma permanente como lógica operativa.

    En los dos últimos casos, el modelo debe interactuar con el entorno externo. GPSS-Plus permite este acoplamiento mediante el BRIDGER, que actúa como un canal bidireccional genérico entre el modelo y sistemas físicos reales.

    Desde el punto de vista del lenguaje, el acceso a recursos externos es uniforme: una base de datos, un fichero o un sensor se manejan con la misma sintaxis. Abrir un relé o almacenar un dato se expresa mediante BRIDGE_WRITE, y sustituir un GENERATE que simula la entrada de un paquete por una suscripción a un sensor real de movimiento es prácticamente inmediato. La transición entre sandbox a gemelo digital o edge software resulta así transparente.

    Gracias a la Rehidratación, el modelo no solo se conecta al mundo real, sino que se sincroniza con su estado en curso, permitiendo que el simulador 'despierte' en mitad de un proceso sin perder la continuidad operativa tras el último dato almacenado incluso tras la modificación del modelo.

     

    Otras extensiones destacadas:

    Además de estas innovaciones, GPSS-Plus incorpora múltiples extensiones que amplían su expresividad, modularidad y potencia:

    • Nuevos recursos: bloques nuevos como STOCK, RESTROOM, CONDITIONS, máquinas de estados finitos que amplían la paleta clásica (STORAGE, FACILITY...).

    • Creación dinámica de recursos: Lo que en motores Drag&Drop exigiría arrastrar cientos de bloques manualmente, en GPSS-Plus se logra con un simple bucle FOREACH y un NEWFACILITY. Esta es la diferencia clave entre un lenguaje textual y las interfaces gráficas clásicas.

    • Estadísticas: permite recolectar estadísticas automáticas sobre cualquier recurso o secuencia, y visualizarlas gráficamente.
    • Behavior functions: a partir de datos reales o simulados, crea interpolaciones automáticas que encapsulan comportamientos complejos (por ejemplo, distintos tramos de una carretera o máquina).

    • Funciones nativas: herramientas como CONCAT, MERGE , PUSH extienden el lenguaje sin depender de código externo.

    • Gráficas automáticas: basta con tabular datos para generar gráficos dentro del entorno de simulación.

    • Contextos (CX$): cada modelo puede definir contextos independientes, ideales para crear librerías, módulos o componentes reutilizables.

    • Depuración total: el sistema permite el seguimiento detallado de la cola de eventos, de cada entidad, recurso o variable, facilitando el análisis y la corrección de modelos.

       

     

    • + Season 1: Hola mundo

      Para entender GPSS ó GPSS-Plus y cualquier motor de simulación, primero debemos entender qué significa un sistema de eventos discretos (DES).

      Un Sistema de Eventos Discretos (DES) modela procesos complejos basándose en la idea de que la actividad del sistema es una secuencia de eventos discretos.


      ¿Y qué es un evento discreto? Un evento discreto es una acción o cambio que ocurre en un momento determinado y finaliza en otro:

      • Evento 1: Coger un coche
      • Evento 2: Conducir 2 horas
      • Evento 3: Aparcar.

      El sistema completo se representa como la combinación de estos eventos puntuales que, al ejecutarse en el tiempo, simulan un proceso de la vida real.

      Motores de simulación como GPSS y GPSS-Plus son las herramientas clave para este análisis. Su función es modelar y analizar cómo los recursos (máquinas, estaciones de trabajo, ventanillas, o vehículos) son gestionados en conjunción con las entidades (clientes, productos) y sus respectivas colas de espera.

      Este enfoque genera informes y estadísticas que, entre muchas:

      1. Identifican cuellos de botella y minimizan tiempos de espera.

      2. Optimizan el uso de los recursos de manera eficiente.

      3. Estudian y protocolizan acciones y contingencias ante cualquier eventualidad o fallo en el sistema.

      • + Un ejemplo simple

        Simularemos un sistema básico donde las entidades (clientes) llegan a un recurso (ventanilla), esperan si está ocupada, son atendidas y luego se retiran. En este modelo:

        • Llegadas: Los clientes llegan cada 60 a 70 tiempos.
        • Atención: El tiempo de servicio en la ventanilla es de 60 a 90 tiempos.
        • Colas: Si la ventanilla está ocupada, los clientes esperan.
        • Salida: Los clientes tardan 10 tiempos en colocarse en la cola y otros 10 en retirarse después del servicio.

        Para los que conozcan GPSS clásico tendríamos:

        GENERATE 60,10   ; Entidades generadas cada 60 a 70 unidades de tiempo.  
        ADVANCE 10       ; Tarda 10 tiempos en colocarse en la cola de la ventanilla.  
        SEIZE VENTANILLA ; La entidad llega a la cola de la ventanilla.  
        ADVANCE 60,30    ; Tiempo de atención: 60 a 90 tiempos.  
        RELEASE VENTANILLA ; Abandona la ventanilla.  
        ADVANCE 10       ; Tarda 10 segundos en irse.  
        TERMINATE 1      ; La entidad finaliza. 
        START 100        ; Ejecuta esta acción hasta que 100 entidades hayan sido atendidas.

        En GPSS-Plus, es muy similar, pero con representación gráfica. Para ello, configuramos recursos y parámetros adicionales:

        FACILITY {NAME:VENTANILLA, X:300, Y:300} ; Definimos la ventanilla en el espacio. 
        POSITION {NAME:SALIDA, X:500, Y:300}    ; Definimos la salida en el espacio.  
        
        GENERATE 60,10 {NAME:GEN1, X:100, Y:300} ; Posicionamos el generador de entidades. 
        ADVANCE 10 {TO:VENTANILLA}               ; Avanza hasta la ventanilla. 
        SEIZE VENTANILLA                        ; Asignación del recurso "VENTANILLA". 
        ADVANCE 60,30                           ; Tiempo de atención: 60 a 90 tiempos. 
        RELEASE VENTANILLA                      ; Liberación del recurso. 
        ADVANCE 10 {TO:SALIDA}                  ; Avanza hasta la salida. 
        TERMINATE 1                             ; La entidad finaliza. 
        START 100                               ; Ejecuta esta acción hasta que 100 entidades hayan sido atendidas.

        Paso final: Pulsa Play y observa el resultado. La primera entidad saldrá del "Generate" entre los momentos 60 y 70.

        Como se puede observar, el programa no ha cambiado demasiado para añadir la parte gráfica. Básicamente, hemos definido la situación espacial de los elementos.

        • + Sintaxis: Los comandos y los bloques

          En una simulación, hay dos elementos principales en juego:

          1. Las ENTIDADES o transacciones son los elementos que que cambian su estado con la simulación, pueden representar personas, cajas o cualquier elemento. En GPSS-Plus se representan con bolitas de colores. Estas entidades interactúan con el entorno. Por ejemplo, entran en juego a través del bloque GENERATE, avanzan por un circuito con ADVANCE y mueren en un TERMINATE.

          2. Los RECURSOS, que conforman el entorno y son utilizados por las entidades, como una ventanilla de atención o una caja más grande que la que represente una entidad.

          Por ejemplo, una persona (entidad) se acerca a una ventanilla (recurso). Esta ventanilla tendrá un COMANDO que la define, y una serie de BLOQUES que describen qué hace la persona en relación con la ventanilla.

          Un ejemplo de definición de un recurso como ventanilla es el COMANDO:

          FACILITY {name:Ventanilla1, x:100, y:100}

          Un ejemplo de instrucción para que una entidad ocupe esa ventanilla es el BLOQUE:

          SEIZE Ventanilla1

          En resumen, nos basamos en dos elementos principales: bloques y comandos.

          1. Bloques ligados a las entidades

          Los bloques son instrucciones que las ENTIDADES ejecutan directamente durante la simulación. Cada bloque define una acción específica que afecta al flujo o estado de esa entidad.

          Sintaxis general:

          BLOQUE [PARÁMETROS A,B,C..] {OPCIONES JSON-LIKE}

          Ejemplos de bloque:

          ASSIGN nombreVariable,10
          TERMINATE 1

          Los parámetros se denominan por letras (A, B, C, ...). Dependiendo del bloque, también pueden incluir parámetros en formato JSON-like, que usualmente definen aspectos gráficos o avanzados.
          Ejemplo:

          MOD {COLOR:#FF0000} ; pone de color rojo una entidad.

          Otro ejemplo:

          ADVANCE 10,5 {TO:Ventanilla1} ; la entidad avanza en tiempo hacia "Ventanilla1"

          2. Comandos ligados a los recursos y entorno del motor

          Los comandos configuran el entorno de la simulación. A diferencia de los bloques, no son ejecutados directamente por las entidades. En cambio, definen recursos, posiciones gráficas y reglas del sistema.

          Sintaxis general:

          COMANDO {NAME:theName, OPCIONES JSON-LIKE}

          Ejemplo para definir un almacén:

          STORAGE {NAME:ALMACEN1, CAPACITY:10, X:270, Y:200}
          

          Este formato permite agregar información adicional como la posición (X, Y).

          • + Variables: Savevalues y Assigns

            En GPSS-Plus, las variables permiten almacenar información que puede ser utilizada por las entidades o para configurar elementos del entorno. Estas variables se dividen principalmente en dos tipos:

            1. SAVEVALUE (variables globales):Las variables globales son accesibles por cualquier entidad y se mantienen durante toda la simulación. Son útiles para almacenar datos compartidos, como contadores, acumuladores o estados globales.

            Sintaxis:
            SAVEVALUE variable, valor

            Ejemplo:

            SAVEVALUE TiempoEspera, 8
            

            Este comando establece la variable global "TiempoEspera" con un valor de 8.

            Una vez definido, el valor puede actualizarse en cualquier momento:

            SAVEVALUE TiempoEspera, 10
            

            El valor de "TiempoEspera" ahora es 10.

            Para recuperar el valor de una variable global se utilizan los SNA que tienen un formato particular:

            X$nombreSavevalue ó X$(nombreSavevalue)

            ; X$TiempoEspera ó X$(TiempoEspera)
            
            IF (X$TiempoEspera>10)
            ...

            Por ejemplo, incrementar el valor de un savevalue se puede hacer mediante:

            SAVEVALUE TiempoEspera, X$TiempoEspera + 1

            O a través del método:

             SAVEVALUE.inc TiempoEspera

            Particularmente, y debido a que estas variables son usadas por más de una entidad, tiene un COMANDO asociado para su creación e inicialización llamado INITIAL.

            Su sintaxix es simple:

            INITIAL TiempoEspera,10

            Recuerda que INITIAL es un comando. Se usa al comienzo del programa para inicializar.

            2. ASSIGN (variables locales de la entidad):
            Las variables locales pertenecen a una entidad específica. Cada entidad puede tener su propio valor para una misma variable, lo que las hace útiles para gestionar datos exclusivos de cada entidad.

            Sintaxis:
            ASSIGN variable, valor

            Ejemplo:

            ASSIGN Identificador, 1
            

            Este bloque asigna el valor 1 a la variable "Identificador" de la entidad actual que invoca el ASSIGN.

            Para recuperar el valor de una variable local con su SNA de formato:

            P$nombreAssign ó P$(nombreAssign)

            ; P$Identificador ó P$(Identificador)
            if(P$Identificador>5) ...

            P$Identificador es la forma que permite obtener el valor de una variable local de la entidad.

            Diferencias clave entre SAVEVALUE y ASSIGN:

            • Alcance: Las variables SAVEVALUE son globales, mientras que las ASSIGN son locales a la entidad.
            • Persistencia: Las SAVEVALUE mantienen su valor en todo momento, incluso si ninguna entidad las usa. Las ASSIGN solo existen mientras la entidad está activa.
            • Uso: Utiliza SAVEVALUE para datos compartidos, y ASSIGN para datos específicos de una entidad.

            Ejemplo combinado:

            En este ejemplo, combinamos SAVEVALUE y ASSIGN para calcular el tiempo promedio de espera en una ventanilla. Si el tiempo está definido por un ADVANCE 15,10 significa que estarán entre 15 y 25 tiempos. Eso debería ir aproximando el promedio a 20.

            INITIAL TiempoTotal, 0      
            INITIAL NumEntidades, 0     
            Facility {NAME:Ventanilla,X:354,Y:204}  
            Graphic {NAME:Text1,Type:TEXT,X:358,Y:321,Text:"Promedio"}  
            Position {NAME:Salida,X:577,Y:201}   
            START 50   
            ;---------------------------------
            
            Generate 20,0 {NAME:GEN1,X:100,Y:207}   
            ADVANCE 10 {to:Ventanilla}           
            SEIZE Ventanilla     
            ASSIGN TiempoInicio, AC1$  
            ADVANCE 15,10       
            ASSIGN TiempoFinal, AC1$      
            RELEASE Ventanilla       
            ADVANCE 15 {to:Salida} 
            ASSIGN TiempoEntidad, (P$TiempoFinal - P$TiempoInicio)  
            SAVEVALUE NumEntidades, X$NumEntidades + 1  
            SAVEVALUE TiempoTotal, X$TiempoTotal + P$TiempoEntidad  
            SAVEVALUE promedio, round(X$TiempoTotal / X$NumEntidades,3)   
            MOVE {NAME:Text1,text:"Promedio: X$promedio"}  
            TERMINATE 1    

            En este código:
            - Cada entidad calcula su tiempo de espera en la ventanilla.
            - Los tiempos se acumulan en la variable global "TiempoTotal".
            - El número de entidades atendidas se cuenta en "NumEntidades".
            - Al final de la simulación, puedes calcular el tiempo promedio de espera:
            TiempoPromedio = X$TiempoTotal / X$NumEntidades

            De esta manera, SAVEVALUE y ASSIGN trabajan juntos para ofrecer un manejo eficiente de datos en la simulación.

            Más adelante veremos como ambos BLOQUES admiten mucho más que variables numerales.

             

            • + Estructura: IF, CALL, PROCEDURE

              En este ejemplo, simulamos un sistema donde cada entidad elige aleatoriamente una de tres ventanillas para ser atendida. Cada ventanilla tiene su propia cola, y el flujo se distribuye de forma desigual en función del tiempo de servicio.

              La lógica de decisión se resuelve exclusivamente mediante procedimientos estructurados.

              FACILITY {NAME:VENTANILLA1,X:320,Y:450}
              FACILITY {NAME:VENTANILLA2,X:320,Y:300}
              FACILITY {NAME:VENTANILLA3,X:320,Y:150}
              
              POSITION {NAME:POS1,X:160,Y:300}
              POSITION {NAME:POS2,X:497,Y:300}
              
              START 30 ; Se ejecutará hasta que se completen 30 entidades
              
              GENERATE 10,0 {NAME:GEN1,X:60,Y:300}
              ADVANCE 20,0 {TO:POS1}
              ASSIGN ALEATORIO,RANDOM
              IF (P$ALEATORIO<0.3)
                  CALL CAMINO1
                  ELSE 
                     IF (P$ALEATORIO<0.6)
                        CALL CAMINO2
                        ELSE
                        CALL CAMINO3
                     ENDIF
              ENDIF
              ADVANCE 20 {TO:POS2}
              TERMINATE 1
              
              ;---------------------------------------------
              
              PROCEDURE CAMINO1
              ADVANCE 20  {TO:VENTANILLA1}
              SEIZE VENTANILLA1
              ADVANCE 25,10
              RELEASE VENTANILLA1
              ENDPROCEDURE 
              
              PROCEDURE CAMINO2
              ADVANCE 20 {TO:VENTANILLA2}
              SEIZE VENTANILLA2
              ADVANCE 10,2
              RELEASE VENTANILLA2
              ENDPROCEDURE 
              
              PROCEDURE CAMINO3
              ADVANCE 20 {TO:VENTANILLA3}
              SEIZE VENTANILLA3
              ADVANCE 10,2
              RELEASE VENTANILLA3
              ENDPROCEDURE 

              Con respecto al anterior sólo tenemos unos pocos BLOQUES nuevos:

              ASSIGN ALEATORIO,RANDOM
              

              Este bloque crea una variable local en la entidad (llamada ALEATORIO) y le asigna un número aleatorio entre 0 y 1, usando la función RANDOM.

              Estas variables locales también se llaman parámetros y se accede a ellas con la notación SNA P$Nombre

              P$ALEATORIO

              • "P" porque es un parámetro,
              • "$" es el separador estandar
              • "ALEATORIO" es su nombre
              IF (P$ALEATORIO<0.3)

              Este es un bloque de estructura de control "IF".

              Como se puede ver, es un "if" igual al de cualquier otro lenguaje. Comparamos una variable con un número.

              Y este otro bloque de estructura de control no requiere tampoco muchas presentaciones.
              Llama a un "PROCEDURE".

              CALL CAMINO1
              


              Por último

              PROCEDURE CAMINO1
              ...
              ENDPROCEDURE 100 ; produciría un assign en la entidad invocadora llamado CAMINO1 de valor 100

              Que tampoco requieren mucha presentación. Encapsulan un conjunto de acciones (PROCEDURE).

              ENDPROCEDURE Puede tener de un valor en el parámetro A que será retornado del mismo modo que si hubieramos generado una instrucción ASSIGN. Recuperable como P$NombredelProcedure (P.E. P$CAMINO1). En este caso, no se utiliza.

               

              • + Recursos: Storage

                Uno de los recursos clásicos es el STORAGE.

                FACILITY representa cosas como una ventanilla, una máquina o un operario: solo puede atender a una entidad a la vez y según su capacidad (con CAPACITY:3 la FACILITY atendería 3 entidades). STORAGE, en cambio, representa algo como un almacén, un tanque, o una red que puede usarse parcialmente.

                ¿Qué es un STORAGE? Un STORAGE es un tipo de recurso que no se ocupa por entidad, sino por cantidad. Se usa cuando una entidad debe reservar parte de un recurso sin ocuparlo por completo. Por ejemplo, un camión puede dejar 10 cajas en un almacén que admite hasta 40.

                ¿Cómo funciona? Se define con una capacidad total, por ejemplo 40 unidades.

                STORAGE {NAME:almacen1, X:300, Y:200, capacity:40}

                Las entidades usan el bloque ENTER para ocupar cierta cantidad, y LEAVE para devolverla.

                ENTER almacen1,5
                LEAVE almacen1 

                Si una entidad quiere ocupar más de lo que hay disponible, esperará automáticamente en una cola interna  hasta que el STORAGE tenga suficiente capacidad libre. No necesitas programar nada adicional para gestionar esa espera.

                En el ejemplo: Almacén de cajas definido (STORAGE) con 40 unidades de capacidad. Los camiones llegan cada 10 unidades de tiempo y depositan entre 1 y 16 cajas. Después de un tiempo, se retiran liberando el espacio ocupado. Verás en el texto de pantalla cuántas cajas hay almacenadas en cada momento.

                Además de realizarse el cálculo a través de sumas y restas, se puede hacer directamente consultando el SNA correspondiente a la ocupación del STORAGE:

                R$(almacen1,OCCUPIED)  ; Unidades actualmente ocupadas
                R$(almacen1,LEFT)      ; Unidades disponibles

                Example:
                /* Almacenes: STORAGE
                Gestión de capacidad y uso de un almacén.
                */
                
                INITIAL capacidad,40 ; define un savevalue llamado capacidad de valor 40
                
                STORAGE {NAME:almacen1, X:300, Y:200, capacity:X$capacidad}
                
                POSITION {NAME:SALIDA,  X:500, Y:200}
                
                ; Texto que muestra el contenido actual
                GRAPHIC {NAME:texto1, Type:TEXT, X:320, Y:240, Text:"Cajas actuales: 0"}
                
                START 100
                
                ; Camiones llegando cada 10 unidades de tiempo
                GENERATE 10,0 {NAME:Camion, X:100, Y:200}
                
                ADVANCE 15 {TO:almacen1}
                ENTER almacen1,(random*15)+1 ; Ocupa entre 1 y 16 unidades del storage
                savevalue uso,X$capacidad - R$(almacen1,LEFT)
                MOVE {name:texto1,text:"Cajas actuales X$uso : R$(almacen1,OCCUPIED)"}
                ADVANCE 40,10
                LEAVE almacen1 ; Libera las unidades ocupadas
                savevalue uso,X$capacidad - R$(almacen1,LEFT)
                MOVE {name:texto1,text:"Cajas actuales X$uso : R$(almacen1,OCCUPIED)"}
                ADVANCE 15 {TO:SALIDA}
                TERMINATE 1
                
                • + La cola de eventos: el motor de la simulación

                  La cola de eventos:

                  En el corazón de cualquier simulación de eventos discretos se encuentra la cola de eventos, una estructura que define cómo y cuándo se ejecutan las acciones de las entidades en el modelo. Este enfoque de eventos discretos es lo que lo distingue de otros lenguajes de programación.

                  Hay que pensar que es como si cada entidad tuviese su propia programación, como se haría en C o Python, pero saltando entre entidades según el orden temporal que marca esta cola. Si simulamos 10 entidades, hay 10 programas corriendo “a la vez” (o casi).

                  ¿Qué es la cola de eventos?

                  Es una lista ordenada de acciones programadas, cada una asociada a un instante de tiempo simulado (AC1). La cola garantiza que las acciones se ejecuten en el orden correcto según el tiempo en que deben ocurrir.

                  Lo más importante: está ordenada por tiempo. Por ejemplo:

                  Tiempo 15 - Atender a la entidad 14 en el paso 88
                  Tiempo 45 - Atender a la entidad 22 en el paso 16
                  Tiempo 77 - Atender a la entidad 16 en el paso 25

                  Ejemplo: Evolución de la cola de eventos

                  Supongamos el caso clásico de un banco donde los clientes entran por la puerta y son atendidos en una ventanilla. Las entidades son generadas con GENERATE y pasan un tiempo siendo atendidas mediante ADVANCE.

                  Imaginemos que estamos en el instante T = 5, con la siguiente cola:

                  T 5: Entidad 5: Esperando en la cola de la ventanilla
                  T 7: Generate: Generar elemento, cliente que va a entrar por la puerta
                  T 8: Entidad 9: Dirigiéndose a la cola de la ventanilla
                  T 16: Entidad 3: Dirigiéndose a la cola de la ventanilla
                  

                  Ahora mismo, tenemos una entidad, la 5, que es la siguiente en la cola. Extraeremos ese elemento de la cola para procesarlo. ¡Ahora sabemos que el tiempo es 5! No porque el tiempo haya pasado, sino porque nuestra siguiente tarea está programada para T=5.

                  Vamos a pensar que, por ejemplo, la Entidad 5 se encuentra en el "paso" (posición de su programación) que indica un ADVANCE 10 para ser atendida.
                  Ante esto, la solución es colocarse en la cola en T=15 (5+10) y cesar en su ejecución mientras es atendida.
                  Ahora la cola de eventos tendrá esta situación:

                  T 7: Generate: Generar elemento
                  T 8: Entidad 9: Dirigiendose a la cola de la ventanilla
                  T 15: Entidad 5: Ir a la salida
                  T 16: Entidad 3: Dirigiendose a la cola de la ventanilla
                  

                  Ya estamos en el evento siguiente. Toca actualizar el tiempo del sistema de nuevo: "AC1" que ahora toma el valor 7. Como decíamos, no es que el tiempo haya pasado, es que la cola de eventos hace saltar el tiempo porque no tiene nada que hacer hasta T=7.

                  Extraemos el elemento de la cola y nos encontramos con un GENERATE 20,5. Por lo tanto, debemos calcular qué tiempo será ese definido por 20,5; algo entre 20 y 25. Supongamos el resultado de 22. Es decir, entrará otra persona dentro de 22 tiempos. La cola pasa ahora a tener este otro aspecto después de moverse el GENERATE y realizar 2 tareas:

                  1.- En propio GENERATE avanza 20 (parámetro A) (7+20=27).
                  2.- Crear una nueva entidad (la número 10) para que pueda llevar a cabo sus tareas en 22 momentos después (7+22=29).

                  T 8: Entidad 9: Dirigiendose a la cola de la ventanilla
                  T 15: Entidad 5: Ir a la salida
                  T 16: Entidad 3: Dirigiendose a la cola de la ventanilla
                  T 27: Generate: Generar elemento
                  T 29: Entidad 10: Dirigiendose a la cola de la ventanilla

                  Ahora sería el momento de volver a mover el tiempo del sistema y atender el siguiente elemento: la Entidad 9. 

                  Así seguirá viva la cola de eventos hasta que se cumpla alguna de las opciones que finalicen el programa.
                  Esta cola de eventos en GPSS-Plus tiene, además, otras características para poder realizar las tareas en el mundo gráfico y añadir los eventos tipo "ON_*".

                  En esta cola GPSS-Plus, tenemos 5 tipos de elementos:

                  • GENERATE – Introducen nuevas entidades en el sistema.
                  • Entidades – Las que se mueven por el modelo y consumen recursos.
                  • Tiempos muertos – Pausas visibles que permiten simular paso a paso o ajustar la velocidad.
                  • Entidades virtuales – No visibles, ejecutan eventos como ON_ENTER, ON_TIMER, etc.
                  • TIMER del sistema – Como un generate, pero genera entidades virtuales a intervalos fijos.

                  Las colas de los recursos:

                  Otras colas existentes son las de los recursos que cada uno de ellos puede tener una o dos según el caso.

                  Estas listas están ordenadas por orden de llegada y su gestión depende exclusivamente del propio recurso.

                  Por ejemplo, una facility tiene dos listas, la de entidades ocupantes y la de entidades en cola.

                  Cuando una entidad intenta hacer SEIZE, si el recurso está ocupado, pasará a la lista de espera. Si no hay entidades en el recurso, lo ocupará ingresando en la lista de ocupantes.

                  Cuando realize RELEASE, abandonará esta lista y forzará al recurso a buscar alguna entidad en su cola pendiente de entrar. Si hay alguna, la incorporará a la lista de ocupantes y planificará su entrada en la cola de eventos en ese mismo momento, por lo que esa entidad tomará el control del motor en cuanto la saliente cese su actividad.

                  Conclusión

                  La cola de eventos no solo define el orden y el tiempo en que ocurren las acciones, sino que también actúa como el núcleo del sistema. Garantiza que los bloques se ejecuten correctamente y que la simulación sea consistente y precisa. Comprender este concepto es esencial para diseñar modelos eficientes y aprovechar al máximo las capacidades de GPSS-Plus.

                  En GPSS-Plus es sencillo ver esta cola, sólo debes activar la ventana de menú y pulsar sobre el botón "Event Queue". 

                  • + Conditions: Esperar una condición.

                    Imagina un grupo de personas esperando a que se abra una puerta automática. No están haciendo cola ni ocupando nada, simplemente están esperando que ocurra algo.

                    Eso es lo que permite el recurso CONDITIONS: detener entidades hasta que una condición lógica se cumpla.

                    Algo parecido existía en GPSS clásico con el bloque ASSEMBLE para este propósito. En GPSS-Plus, este comportamiento se sustituye por este recurso más general y flexible.

                    El recurso CONDITIONS permite retener entidades hasta que se cumpla una condición. Esta condición se define como una expresión evaluada cada vez que una entidad intenta pasar.

                    CONDITIONS {NAME:Conditions1,X:208,Y:317,expression:(X$contador>=5)}

                    CONDITIONStiene más opciones que se verán más adelante.

                    Su bloque asociado WAITUNTIL se encarga de validar para esta entidad la condición, en este caso, si el contador es mayor o igual a 5.

                    Llegada la 5ª entidad, la condición se cumplirá y seguirá adelante sin ser retenida. Ahora solo falta liberar al resto chequeando a todas las retenidas con otro bloque asociado: WAITCHECK.

                    Resulta interesante observar que salen todas juntas del CONDITIONS en el primer tramo. En el segundo, la aleatoriedad del ADVANCE 20,40 hace que se separen.

                    También empezamos a utilizar el BLOQUE ENDGENERATE más acorde con el método estructurado que TERMINATE. Son equivalentes pero sintácticamente es más adecuado estructurar el bloque con GENERATE/ENDGENERATE. Permite identificar claramente qué bloque fue generado, especialmente si hay múltiples GENERATE en el programa.


                    Example:
                    /* Sincronización: CONDITIONS / WAITUNTIL
                    Las entidades esperan en grupo y avanzan juntas.
                    */
                    
                    POSITION {NAME:POS1,X:208,Y:222}
                    POSITION {NAME:POS2,X:452,Y:215}
                    POSITION {NAME:POS3,X:752,Y:215} 
                    
                    CONDITIONS {NAME:Conditions1,X:208,Y:317,expression:(X$contador>=5)} ; Recurso retenedor
                    INITIAL contador,0
                    
                    START 100 
                    ;*****************************************************
                    
                    GENERATE 10,2 {NAME:GEN1,X:86,Y:228,ERADIO:10,ECOLOR:#FF9900} 
                    
                    ADVANCE 16,0 {TO:POS1} 
                    savevalue contador,X$contador + 1
                    WAITUNTIL Conditions1
                    WAITCHECK Conditions1
                    savevalue contador,0
                    ADVANCE 20,0 {TO:POS2} ; Avanzan hacia la segunda posición TODOS JUNTOS
                    ADVANCE 20,40 {TO:POS3} ; Avanzan hacia la tercera posición con un tiempo aleatorio entre 20 y 40 pero esta vez SEPARÁNDOSE
                    
                    ENDGENERATE 1 ; Finaliza la vida de la entidad ES IDENTICO A TERMINATE 1
                    
                    • + Segundo paso de estructura

                      Ahora que ya sabes usar procedimientos y decisiones condicionales simples (IF), vamos a ver un caso donde una entidad elige entre varias rutas posibles.

                      Para eso, usamos la estructura SWITCH, muy parecida a un “menú de decisiones”.

                      ¿Qué hace este ejemplo?

                      • Genera una entidad cada 10 unidades de tiempo.
                      • Le asigna un número aleatorio (P$ALEATORIO).
                      • Según ese número, elige una de cuatro rutas (CALL CAMINO1, ..., CALL CAMINO4).
                      • En dos de esas rutas (CAMINO3 y CAMINO4), se activa un bloque visual que muestra "Lock" o "Unlock", y se bloquean o desbloquean recursos. Si una entidad entra en el camino 3 se bloqueará el uso de los recursos 1, 2 y 3 aunque las que estén dentro podrán finalizar su tarea. Cuando una entidad tome el camino 4, volverán a usar los recursos sin dilación. El bloqueo no expulsa a las entidades que ya están usando los recursos, pero impide que nuevas los tomen hasta que sean desbloqueados.

                      Además definimos cierto aspecto puramente visual, "el flow".
                      Activándolo en los ADVANCE veremos una línea de ruta que comenzará siendo vertical u horizontal según el LAYOUT, podrá pasar por un cierto VIA y VIA2, además de poder converger o divergir según MERGE o DECISION.


                      Example:
                      /* Bloques: SWITCH y LOCK/UNLOCK
                      Enrutamiento condicional y gestión de recursos bloqueables.
                      */
                      
                      FACILITY {NAME:VENTANILLA1,X:320,Y:450,capacity:3}
                      FACILITY {NAME:VENTANILLA2,X:320,Y:300,capacity:3}
                      FACILITY {NAME:VENTANILLA3,X:320,Y:150}
                      FACILITY {NAME:VENTANILLA4,X:320,Y:50}
                      
                      POSITION {NAME:POS1,X:158,Y:301}
                      POSITION {NAME:POS2,X:497,Y:300}
                      POSITION {NAME:POS3,X:627,Y:300,TYPE:TERMINATE,TITLE:END}
                      
                      Graphic {NAME:Text1,Type:TEXT,X:320,Y:104,Text:"Unlock"}
                      
                      
                      START 30
                      
                      GENERATE 10,0 {NAME:GEN1,X:57,Y:300}
                      ADVANCE 20,0 {TO:POS1,flow:1}
                      
                      ASSIGN ALEATORIO,(RANDOM)
                      
                      
                      SWITCH P$ALEATORIO
                          CASE <,0.4
                              CALL CAMINO1
                          ENDCASE
                      
                          CASE <,0.8
                              CALL CAMINO2
                          ENDCASE
                      
                          CASE <,0.92
                              CALL CAMINO3
                              MOVE {NAME:Text1,text:"Lock"}
                              LOCK VENTANILLA1
                              LOCK VENTANILLA2
                              LOCK VENTANILLA3
                              
                          ENDCASE
                      
                          DEFAULT
                              CALL CAMINO4
                              MOVE {NAME:Text1,text:"Unlock"}
                              UNLOCK VENTANILLA1
                              UNLOCK VENTANILLA2
                              UNLOCK VENTANILLA3
                          ENDCASE
                      ENDSWITCH
                      
                      ADVANCE 20,10 {TO:POS2,flow:1,MERGE:"salida"}
                      ADVANCE 20,0 {TO:POS3,flow:1}
                      
                      ENDGENERATE 1
                      ;****************************************
                      
                      PROCEDURE CAMINO1
                          ADVANCE 20  {TO:VENTANILLA1,flow:1,DECISION:"inicio"}
                          SEIZE VENTANILLA1
                          ADVANCE 45,10
                          RELEASE VENTANILLA1
                      ENDPROCEDURE
                      
                      PROCEDURE CAMINO2
                          ADVANCE 20 {TO:VENTANILLA2,flow:1,DECISION:"inicio"}
                          SEIZE VENTANILLA2
                          ADVANCE 40,10
                          RELEASE VENTANILLA2
                      ENDPROCEDURE
                      
                      PROCEDURE CAMINO3
                          ADVANCE 20 {TO:VENTANILLA3,flow:1,DECISION:"inicio"}
                          SEIZE VENTANILLA3
                          ADVANCE 40,20
                          RELEASE VENTANILLA3
                      ENDPROCEDURE
                      
                      PROCEDURE CAMINO4
                          ADVANCE 20 {TO:VENTANILLA4,flow:1,DECISION:"inicio"}
                          SEIZE VENTANILLA4
                          ADVANCE 10,2
                          RELEASE VENTANILLA4
                      ENDPROCEDURE
                      
                      
                      ;***************************************************************
                      • + Un modelo completo

                        Ahora que ya conoces los fundamentos de GPSS-Plus, veamos cómo se integran todos en un ejemplo unificado.

                        Vamos a ver un código bastante más largo de lo que hemos visto hasta ahora, casi 100 líneas de código.

                        Este modelo representa un sistema con vehículos de distintos tipos que recorren una zona de trabajo durante varios ciclos. Cada uno decide su ruta, ejecuta tareas, y finaliza tras completar un número de ciclos entrando en el aparcamiento de utilitarios o de camiones según el caso ocupando la facility o el storage dependiendo del tamaño del camión.

                        Este código tiene varias novedades:

                        Lo primero que observamos es que hay a nivel general una estructura con dos GENERATES varios PROCEDURES.

                        Los generates se separan visualmente para que cada uno tenga su propia actividad y marcan las entidades nacidas de ellos de forma diferenciada.

                        GENERATE ... NAME:G_UTILITARIOSG_UTILITARIOS ; genera los utilitarios
                           ....
                           assign ...
                           ....
                           CALL PROC.MAIN
                        ENDGENERATE 1

                        Y después tenemos la serie de PROCEDURES:

                        Procedures de proceso:

                        PROC.MAIN         ; Proceso principal
                        PROC.UTILITARIOS
                        PROC.CAMIONES
                        

                        Procedures de decisión:

                        DECIDE.RUTA
                        DECIDE.LOOP_OR_END
                        

                        PROC.MAIN:

                        Toda la lógica principal estará concentrada en el proceso PROC.MAIN que normalmente estará formado por un bucle while que encerrará las entidades mientras no cumplan la condición de finalizar todas sus tareas.

                        WHILE ("P$estado"!="EXIT")
                            ...
                            ...
                            ASSIGN estado,????
                        ENDWHILE
                        

                        Dentro de éste se situará la decisión sobre las rutas principales que pude tomar cada entidad que suele ser un SWITCH de opciones o varios IFs anidados. Todas estas decisiones conviene colocarlas en PROCEDURES con el nombre: DECIDE.* de forma que sea sencillo reconocerlos y saber de antemano que lo normal es que retornen, directamente el nombre del PROCEDURE a ser invocado por la entidad:

                        ENDPROCEDURE "P$VALUE" ; Retornará 'PROC.UTILITARIOS' ó 'PROC.CAMIONES'

                        Es decir: CALL DECIDE.AAA devolverá en P$AAA (lo anterior al punto se obvia) el valor: 'PROC.BBB' que podrá usarse directamente como CALL P$AAA.

                        CALL DECIDE.RUTA           ; Ejecuta lógica de decisión
                        CALL "P$(RUTA)"            ; Llama al procedimiento retornado por la decisión

                         Muy parecido a la decisión de salir o continuar dando vueltas al ciclo.

                        Otros elementos nuevos son:

                        ADVANCE0  {TO:POS_ENTRADA,flow:1,layout:H}

                        Que en este caso es un ADVANCE0 y no ADVANCE puesto que no queremos más que dar un salto visual sin que la entidad pierda tiempo entrando y saliendo de la cola de eventos.

                        Y la última novedad es el uso de otro recurso que tiene una utilidad de agrupamiento y estadística: QUEUER

                         Tiene capacidad ilimitada y con él se peude saber de forma sencilla cuántas y cuáles son las entidades que están en una determinada zona.

                        QUEUE EN_APARCAMIENTO  ; entran
                        ...
                        ...
                        DEPART EN_APARCAMIENTO ; salen

                        Resumidamente se puede observar en el ejemplo:

                        • Cómo usar varios GENERATE con lógica diferenciada (camiones y utilitarios).
                        • Cómo las entidades pueden decidir rutas dinámicamente (SWITCH, CALL, PROCEDURE).
                        • Cómo se usa un bucle interno por entidad (WHILE) para simular iteración.
                        • Cómo visualizar el flujo con posiciones, subtítulos y estilos.
                        • Cómo combinar distintos recursos (FACILITY, STORAGE, QUEUE).
                        • Cómo trabajar con nombres dinámicos de procedimientos (P$(RUTA)).

                         


                        Example:
                        QUEUER {NAME:EN_APARCAMIENTO,X:413,Y:497}
                        
                        FACILITY {NAME:UTILITARIOS, capacity:4,X:413,Y:390}
                        STORAGE {name: CAMIONES , capacity:5, X:412,Y:120}
                        
                        POSITION {NAME:POS_ENTRADA,X:240,Y:270}
                        POSITION {NAME:POS_SALIDA,X:557,Y:316,type:decision,title:"FIN?"}
                        POSITION {NAME:POS_LOOP,X:225,Y:549}
                        POSITION {NAME:FIN,X:730,Y:324,type:terminate}
                        
                        START 20
                        
                        ;************************************************************************
                        GENERATE 20,10,0,10 {NAME:G_UTILITARIOS,X:80,Y:363,ESUBTITLE:"P$ciclos/3"}
                        assign TIPO_VEHICULO,1
                        mod {RADIO:5,color:#000066}
                        call PROC.MAIN
                        ENDGENERATE 1 ; ALIAS DE TERMINATE
                        ;----------------------------
                        GENERATE 20,10,0,10 {NAME:G_CAMIONES,X:80,Y:199,ESUBTITLE:"P$ciclos/3"}
                        
                        ASSIGN TIPO_VEHICULO,floor(RANDOM * 2 + 2)
                        mod {RADIO:10,color:#006600}
                        call PROC.MAIN
                        ENDGENERATE 1  ; ALIAS DE TERMINATE
                        ;************************************************************************
                        
                        PROCEDURE PROC.MAIN
                        
                        ASSIGN ciclos,1
                        
                        ADVANCE0  {TO:POS_ENTRADA,flow:1,layout:H}
                        ASSIGN estado, "loop"
                        
                        WHILE ("P$estado"!="EXIT")
                        
                        	QUEUE EN_APARCAMIENTO
                        
                        	CALL DECIDE.RUTA ; return assign:RUTA
                            CALL "P$(RUTA)"
                            ADVANCE 20,18 {TO:POS_SALIDA,flow:1,MERGE:"salida"}
                        
                        	DEPART EN_APARCAMIENTO
                            
                            CALL DECIDE.LOOP_OR_END
                            ASSIGN estado,"P$(LOOP_OR_END)"
                        ENDWHILE
                        
                        ADVANCE 20,0 {TO:FIN,flow:1}
                        
                        ENDPROCEDURE
                        
                        ;*****************************************************
                        
                        PROCEDURE DECIDE.RUTA
                            ASSIGN VALUE,""
                            SWITCH P$TIPO_VEHICULO
                            CASE <=,1
                        	    ASSIGN VALUE,"PROC.UTILITARIOS"
                            ENDCASE
                            DEFAULT
                            	ASSIGN VALUE,"PROC.CAMIONES"
                            ENDCASE
                            ENDSWITCH
                        	
                        ENDPROCEDURE "P$VALUE"
                        ;---------------------------------------------
                        PROCEDURE DECIDE.LOOP_OR_END
                            assign value,"loop"
                            assign ciclos,P$ciclos + 1
                        	if (P$ciclos > 3)
                            	assign value,"EXIT"
                                mod {SUBTITLE:"-EXIT-"}
                            else
                                advance 30 {to:POS_ENTRADA,flow:1,via:POS_LOOP,layout:V}
                        	endif
                        ENDPROCEDURE "P$value"
                        
                        ;---------------------------------------------
                        PROCEDURE PROC.UTILITARIOS
                            ADVANCE 20  {TO:UTILITARIOS,flow:1,DECISION:"recurso"}
                            SEIZE UTILITARIOS
                            ADVANCE 55,40
                            RELEASE UTILITARIOS
                        ENDPROCEDURE 
                        
                        PROCEDURE PROC.CAMIONES
                            ADVANCE 20 {TO:CAMIONES,flow:1,DECISION:"recurso" }
                            ENTER CAMIONES  ,P$TIPO_VEHICULO
                            ADVANCE 40,20
                            LEAVE CAMIONES 
                        ENDPROCEDURE 
                        ;----------------------------------
                        
                      • + Season 2: Entidades virtuales (VE)
                        • + Entidades y entidades virtuales VE

                          Ya hemos visto sobradamente qué es una entidad: Una bolita que se mueve por la pantalla. Sabemos que nace de un GENERATE, que recorre bloques, avanza por posiciones y puede entrar en recursos.

                          ¿Y qué es una entidad virtual? Pues una entidad que no se mueve por la pantalla.

                          Parece muy simple la diferencia pero es básicamente esa aunque conlleva muchas implicaciones. Imagina una bolita invisible que no ocupa espacio pero sí ejecuta instrucciones.

                          Vamos a definirlas formalmente:

                          • Las entidades virtuales no nacen de un GENERATE. En su lugar nacen de los COMANDOS: TIMER, PRE_RUN, TRIGGER (ON_ENTER, ON_QUEUE, ON_*...) O del BLOQUE: TIMEOUT
                          • Su número identificativo (D$N) no es secuencial positivo (1,2,... n) sino justo del revés: (-1, -2,... -n)
                          • Su TERMINATE no resta sucesos. De hecho, veremos que se usa TERMINATE_VE que solo termina con las virtuales.
                          • No tienen representación visual directa pero pueden interactuar como cualquiera con la parte gráfica.

                          ¿Y para qué sirven?

                          Pues tienen infinidad de utilidades.

                          Aunque no lo parezca, son el motivo por el que GPSS-Plus no utiliza lenguajes secundarios para programar elementos complementarios como pueda ser el Python o JAVA.

                          Se programan en GPSS-Plus como cualquier subprograma. Se encargan de todas las tareas accesorias a los ciclos de vida de las entidades, por ejemplo, si queremos montar un reloj, tiene poco sentido que sea el nacimiento o movimiento de las entidades que seguro que tienen un montón de cosas de las que preocuparse entrando y saliendo de recursos.
                          En su lugar, un TIMER que se lanzará automáticamente cada N instantes puede hacer estas tareas.
                          Si queremos tener un cierto control de múltiples recursos y el estado de sus colas, podemos tener una entidad virtual que cada vez que alguna entidad ingrese en una cola haga de controlador.

                          Las entidades virtuales trabajan entre bastidores. No se ven moverse, pero sin ellas, muchos procesos simplemente no ocurrirían.

                          En el ejemplo:

                          Una entidad normal, la número 1 (D$N==1), crea una entidad virtual que nacerá dentro de 30 instantes para ejecutar un procedure:

                          timeout actualizarContador,30

                          No recorre posiciones del flujo ni usa recursos. Solo aparece tras ser reclamada por una entidad y, desde entonces, la entidad virtual llama a una nueva entidad virtual cada 3 instantes.
                          Su única tarea: contar.
                          La primera llama a la segunda, que llama a la tercera.... todo a través de un BLOQUE TIMEOUT que sólo requiere el nombre del procedimiento a ejecutar por la entidad virtual y el tiempo en el que ocurrirá.
                          Se puede observar cómo el texto cambia aunque no hay entidades visibles que lo actualicen. Eso es porque lo hace una entidad virtual, que trabaja en segundo plano, como un pequeño proceso autónomo.

                          Este ejemplo demuestra cómo una VE puede actuar como proceso recurrente sin necesidad de ninguna intervención humana ni entidad física. A partir de ahora, verás que muchas de las decisiones del sistema, alarmas o estadísticas estarán gestionadas por este tipo de entidades invisibles.


                          Example:
                          /* Entidades Virtuales:
                          Procesos invisibles que operan en segundo plano.
                          La primera entidad creará con un retraso de 30 momentos una VE.
                          Esta VE creará su siguiente cada 3 momentos.
                          Solo son visibles las entidades, no las VE.
                          */
                          
                          
                          POSITION {NAME:POS1,X:321,Y:332}
                          POSITION {NAME:POS2,X:517,Y:326}
                          Graphic {NAME:Text1,Type:TEXT,X:366,Y:454,Text:"Contador: 0"}
                          
                          INITIAL contador,0
                          
                          START 200
                          
                          ;*****************************************************
                          GENERATE 20,0 {NAME:GEN1,x:101,y:332}
                          ADVANCE 10 {TO:POS1}
                          ; Lanzamos una entidad virtual que actualizará el contador
                          if (D$N==1)
                          	timeout actualizarContador,30
                          endif
                          ADVANCE 10 {TO:POS2}
                          
                          ENDGENERATE 1
                          
                          ;*****************************************************
                          PROCEDURE actualizarContador
                              SAVEVALUE contador, X$contador + 1
                              MOVE {name:Text1, text:"Soy la Entidad Virtual D$N. Contador: X$contador"}
                              TIMEOUT actualizarContador, 3
                              TERMINATE_VE
                          ENDPROCEDURE
                          
                          • + Creación de las VE

                            Ya hemos visto qué es una entidad virtual: una entidad que no se ve moverse por la pantalla pero que ejecuta instrucciones y participa del motor de simulación.
                            Ahora veremos cómo nacen estas entidades virtuales y quién puede crearlas.

                            ¿Quién crea una VE?

                            Las VE no nacen de un GENERATE, como las entidades normales. En su lugar, pueden ser creadas por cuatro mecanismos distintos:

                            • El sistema, usando:

                              • PRE_RUN – al comienzo de la simulación, una sola vez.

                              • TIMER – en intervalos fijos definidos por INTERVAL.

                            • Una entidad, usando:

                              • TIMEOUT – programa la ejecución de una VE para dentro de X instantes.

                            •  Un recurso, usando eventos ON_* como:

                              • ON_SEIZE, ON_RELEASE, ON_LEAVE, etc.

                            ¿Qué tienen en común?

                            Todas las VEs ejecutan un PROCEDURE, que puede venir indicado como TRIGGER o como nombre de procedimiento.
                            Ese PROCEDURE es el "código" que ejecuta la VE. Debe terminar con un TERMINATE o TERMINATE_VE, igual que cualquier entidad normal.

                            Sobre los parámetros

                            Cuando usamos NEW, TIMEOUT o CALL, podemos pasar parámetros adicionales.
                            Estos estarán disponibles dentro del procedimiento como P$PARAM_A, P$PARAM_B, etc., igual que ya hemos visto con CALL.

                            Esto permite que una VE "sepa" quién la ha llamado o por qué.

                            En el ejemplo:

                            • PRE_RUN crea una entidad al inicio (color magenta).

                            • TIMER1 crea una cada 53 unidades (color azul).

                            • TIMEOUT se lanza justo antes de entrar al recurso y deja un mensaje.

                            • ON_RELEASE se dispara al liberar la FACILITY1.

                            Cada vez que una de estas VEs se crea, aparece un texto en pantalla con su identificador y lo que está haciendo.


                            Example:
                            /* Creación de las VE */
                            
                            
                            
                            SYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 53}
                            SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                            
                            POSITION {NAME:POS1,X:311,Y:497}
                            POSITION {NAME:POS2,X:571,Y:147,type:terminate,title:END}
                            
                            Facility {NAME:Facility1,X:573,Y:483,ON_RELEASE:on_RELEASE_Facility1}
                            
                            
                            
                            Graphic {NAME:Text_Pre_run,Type:TEXT,X:253,Y:196}
                            Graphic {NAME:Text_TIMER1,Type:TEXT,X:253,Y:158}
                            Graphic {NAME:Text_Timeout1,Type:TEXT,X:254,Y:252}
                            Graphic {NAME:Text_on_release,Type:TEXT,X:575,Y:544}
                            
                            
                            START 40
                            
                            GENERATE 30,0 {NAME:GEN1,X:110,Y:510, ECOLOR:#FF3333, ERADIO:8} 
                            	if ("P$PARAM_A"=="PRE_RUN")
                                	MOD {COLOR:magenta,subtitle:pre_run}
                                ENDIF
                            	if ("P$PARAM_A"=="TIMER1")
                                	MOD {COLOR:blue,subtitle:timer1}
                                ENDIF
                                ADVANCE 30,2 {TO:POS1,flow:1}
                                ADVANCE 30,2 {TO:Facility1,flow:1}
                                TIMEOUT Timeout1,0,D$N ; le pasamos como parámetro el número de entidad.
                                seize Facility1
                                advance 30,30
                                release Facility1
                                ADVANCE 30,2 {TO:POS2,flow:1}
                            ENDGENERATE 1
                            
                            ;******************************************
                            PROCEDURE PRE_RUN
                            	MOVE {name:Text_Pre_run,Text:"Soy [D$N] PRE_RUN
                             Creo una nueva entidad."}
                                NEW GEN1,0,"pre_run" 
                                TERMINATE_VE
                            ENDPROCEDURE
                            ;---------------------------------
                            PROCEDURE TIMER1
                            	MOVE {name:Text_TIMER1,Text:"Soy [D$N] Timer1.
                             Creo una nueva entidad."}
                                NEW GEN1,0,"timer1" ; Generate, tiempo, PARAM_A, PARAM_B...
                            	TERMINATE_VE 
                            ENDPROCEDURE
                            ;---------------------------------
                            PROCEDURE Timeout1
                            	MOVE {name:Text_Timeout1,Text:"Soy  [D$N] Timeout1.
                             La entidad [P$PARAM_A] va a entrar"}
                            	TERMINATE_VE 
                            ENDPROCEDURE
                            
                            PROCEDURE on_RELEASE_Facility1
                            	MOVE {name:Text_on_release,Text:"Soy [D$N] on_RELEASE.
                             La entidad [P$ENTITYNUMBER] abandona la Facility1"}
                            	TERMINATE_VE 
                            ENDPROCEDURE
                            
                            
                            ;******************************************
                            
                            
                            • + Las VE permanentes, los agentes

                              Un tipo especial de entidad virtual es aquella que no muere al final de su procedimiento.
                              Estas VE que permanecen activas durante toda la simulación (o parte de ella) las llamamos agentes.

                              • Son como procesos en segundo plano que nunca terminan.
                              • Se lanzan normalmente desde PRE_RUN mediante un TIMEOUT.
                              • Guardan su número de entidad (D$N) en un SAVEVALUE, para poder interactuar o consultar su estado.

                              Más adelante veremos que estos agentes se encargan de tareas de control, monitorización o coordinación entre recursos.

                              En resumen, un agente es una VE que no hace TERMINATE y permanece viva en bucle.
                              Eso le permite estar atento, esperando, o vigilando el entorno.


                              Example:
                              /* Agentes: Entidades Virtuales Permanentes
                              Monitorización de recursos con un bucle infinito.
                              */
                              
                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                              
                              POSITION {NAME:POS1,X:311,Y:497}
                              POSITION {NAME:POS2,X:571,Y:147,type:terminate,title:END}
                              
                              Facility {NAME:Facility1,X:573,Y:483}
                              
                              Graphic {NAME:Text_agente,Type:TEXT,X:229,Y:305}
                              
                              
                              START 40
                              
                              GENERATE 30,0 {NAME:GEN1,X:110,Y:510, ECOLOR:#FF3333, ERADIO:8} 
                                  ADVANCE 30,2 {TO:POS1,flow:1}
                                  ADVANCE 30,2 {TO:Facility1,flow:1}
                                  seize Facility1
                                  advance 30,30
                                  release Facility1
                                  ADVANCE 30,2 {TO:POS2,flow:1}
                              ENDGENERATE 1
                              
                              ;******************************************
                              PROCEDURE PRE_RUN
                                  TIMEOUT AGENTE.INIT,0
                                  TERMINATE_VE
                              ENDPROCEDURE
                              ;---------------------------------
                              PROCEDURE AGENTE.INIT
                              	savevalue nAgente,D$N
                              	WHILE (1==1)
                               	   MOVE {name:Text_agente,Text:"Soy el agente [D$N].
                              Tiempo AC1$
                               Entidades en Facility1: R$(Facility1,IN)
                              Entidades en cola: R$(Facility1,QUEUE)"}
                                	  ADVANCE 10
                                  ENDWHILE
                              	TERMINATE_VE 
                              ENDPROCEDURE
                              
                              • + Entidades componente

                                Además de las entidades normales y de los agentes, el modelo admite un tercer tipo de entidad: las entidades componente.
                                Una entidad componente es una entidad que existe para ejecutar una función en nombre de otra entidad. No es un objeto físico ni un recurso externo, sino una parte activa de su comportamiento: algo que la entidad “hace” de manera continua o paralela.

                                Este concepto permite modelar situaciones muy naturales:
                                un buzo que respira y consume oxígeno mientras avanza, un vehículo cuyas ruedas se desgastan mientras circula, o una persona que lee una noticia y cambia de decisión mientras espera en la cola del autobús.

                                Más adelante veremos cómo se crean y cómo trabajan junto a la entidad principal, pero por ahora basta con entender que permiten que una entidad no sea un único hilo secuencial, sino un pequeño sistema formado por varias entidades colaborando entre sí.

                                 

                              • + Season 3: DSL (Domain-Specific Language)
                                • + Filosofía del DSL

                                  Filosofía del DSL de GPSS-Plus

                                  El DSL de GPSS-Plus no está pensado como un lenguaje de programación general, ni como una notación alternativa para dibujar bloques. Está diseñado como el lenguaje operativo del motor de simulación.

                                  Cuando se trabaja con el DSL, el usuario no “programa”: define comportamientos, recursos y eventos que el motor ejecuta en el tiempo: MODELA.

                                  Para entenderlo correctamente, conviene pensar el DSL como un conjunto de herramientas organizadas en cuatro niveles.


                                  1. Entidades visibles o físicas: comportamiento de las “bolitas”

                                  El primer nivel del DSL está orientado a definir qué hacen las entidades que recorren el modelo.

                                  Estas entidades:

                                  • nacen (GENERATE),

                                  • avanzan en el tiempo (ADVANCE),

                                  • interactúan con recursos (SEIZE, ENTER, REST…),

                                  • toman decisiones (IF, SWITCH),

                                  • y finalmente mueren (TERMINATE).

                                  Todo su comportamiento se describe paso a paso, pero ese paso a paso no es secuencial real: es ejecución sobre una cola de eventos.

                                  El DSL proporciona aquí:

                                  • bloques de flujo,

                                  • control temporal,

                                  • estructuras condicionales y bucles,

                                  • llamadas a procedimientos.

                                  Este es el nivel más visible del modelo y el más cercano a la intuición del usuario.


                                  2. Entidades virtuales (VE): comportamiento paralelo

                                  No todo comportamiento pertenece a una “bolita”.

                                  GPSS-Plus introduce entidades virtuales (VE) para modelar lógica que:

                                  • no tiene representación gráfica,

                                  • no recorre el circuito,

                                  • pero vive dentro del motor de simulación.

                                  Las VE permiten:

                                  • reaccionar a eventos (ON_SEIZE, ON_RELEASE, SIGNAL),

                                  • ejecutar lógica periódica (TIMEOUT),

                                  • actuar como agentes, controladores, incluso como comonentes,

                                  • coordinar otras entidades sin bloquearlas.

                                  Desde el punto de vista del DSL, una VE es una entidad completa, con ciclo de vida propio, pero orientada al control del sistema o entidades.


                                  3. Recursos: el entorno con el que interactúan las entidades

                                  El tercer nivel del DSL define el entorno del modelo.

                                  Los recursos representan aquello que las entidades usan, ocupan, esperan o consultan. Pueden ser:

                                  • físicos: FACILITY, STORAGE, RESTROOM, STOCK

                                  • lógicos: QUEUER, CONDITIONS, FSM

                                  • externos: BRIDGER, FILE, TABLE, PLOTTER

                                  Cada recurso sigue el mismo patrón conceptual:

                                  1. se define mediante un COMANDO,

                                  2. se usa mediante BLOQUES emparejados,

                                  3. se observa mediante SNAs.

                                  El DSL no oculta el estado de los recursos: todo puede ser leído, consultado y usado en decisiones.


                                  4. El motor: tiempo, eventos y sistema

                                  Por debajo de entidades y recursos existe un cuarto nivel, menos visible pero fundamental: el motor de simulación.

                                  El DSL permite interactuar con él de forma explícita:

                                  • controlando el tiempo simulado,

                                  • programando eventos futuros (TIMEOUT),

                                  • configurando el sistema (SYSTEM), 

                                  • accediendo a la cola de eventos y al estado interno mediante SNAs.

                                  Esto permite modelos donde:

                                  • el comportamiento no es solo “flujo”,

                                  • sino reacción, sincronización y planificación

                                   


                                  Example:
                                  /* Season 3: DSL
                                  Ejemplo de modelo simple con el DSL de GPSS-Plus.
                                  */
                                  
                                  FACILITY {NAME:VENTANILLA, X:300, Y:300} ; Definimos la ventanilla en el espacio. 
                                  POSITION {NAME:SALIDA, X:500, Y:300}    ; Definimos la salida en el espacio.  
                                  
                                  START 100                               ; Ejecuta esta acción hasta que 100 entidades hayan sido atendidas
                                  ;------------------------------------------------------------------------------------
                                  GENERATE 60,10 {NAME:GEN1, X:100, Y:300} ; Posicionamos el generador de entidades. 
                                      ADVANCE 10 {TO:VENTANILLA}               ; Avanza hasta la ventanilla. 
                                      SEIZE VENTANILLA                        ; Asignación del recurso "VENTANILLA". 
                                      ADVANCE 60,30                           ; Tiempo de atención: 60 a 90 segundos. 
                                      RELEASE VENTANILLA                      ; Liberación del recurso. 
                                      ADVANCE 10 {TO:SALIDA}                  ; Avanza hasta la salida. 
                                  TERMINATE 1                             ; La entidad finaliza. 
                                  
                                  • + Objetos y arrays en ASSIGN y SAVEVALUE

                                    En cuanto al lenjuage, la evolución de GPSS clásico a GPSS-Plus tiene dos grandes puntos de inflexión:

                                    • La introducción de los STACKS, que permiten bucles, llamadas anidadas y estructuras de control complejas.
                                    • La ampliación del sistema de variables SAVEVALUE y ASSIGN, que ahora no solo almacenan números, sino también cadenas, arrays y objetos y permiten ejecutar funciones sobre ellos.

                                    Este segundo avance convierte en obsoletos elementos antiguos como MATRIX y permite que GPSS-Plus incorpore funciones nativas de cualquier lenguaje moderno como push, concat, merge o split, todo dentro de la propia lógica del simulador y sin recurrir a programación externa. Lo que antes se resolvía con herramientas rígidas, ahora puede hacerse de forma expresiva, estructurada y mantenible. GPSS-Plus deja de ser un lenguaje de tarjetas con números y pasa a ser un DSL de propósito completo, orientado a flujos de eventos y manipulación estructurada de datos.

                                    Ya sabemos que GPSS-Plus permite declarar variables con dos niveles de ámbito:

                                    • ASSIGN: variable local a la entidad.
                                    • SAVEVALUE: variable global, compartida entre entidades.

                                    Su funcionamiento es idéntico salvo por su visibilidad. En ambos casos, las variables pueden contener diferentes tipos de datos:

                                    • Números: su escritura es la habitual: 123.4
                                    • Cadenas: siempre entre comillas dobles: "Un texto"
                                    • Arrays: su notación es entre corchetes y sus elementos separados por comas: [10,20,"un texto","otro texto",30]
                                    • Objetos: su declaración es entre llaves y dentro de ellos con pares clave:valor: {clave1:"Un texto",clave2:123.4,clave3:[10,20,30]}

                                    Nótese que no existe booleano, que se usará 0 para falso y 1 para verdadero. Ejemplos:

                                    ASSIGN miNumero, 10 
                                    ASSIGN miTexto, "hola" 
                                    ASSIGN miLista, [10, 20, 30] 
                                    ASSIGN miObjeto, {clave1: 20, clave2: "hola"} 
                                    ASSIGN mixto, [10, {clave: "dato"}, 30]
                                    

                                    Acceso mediante rutas en sus SNA
                                    Los SNA asociados a estas variables son P$ (para ASSIGN) y X$ (para SAVEVALUE). Pero dado que las variables ahora pueden ser algo más que números, los SNA serán más versátiles para poder acceder a cualquier parte de la variable. Su acceso será guiado por la ruta de claves separadas por puntos tanto para arrays como para objetos:

                                    ASSIGN notas, [8,4,5]
                                    ASSIGN media, (P$(notas.0) +P$(notas.1) +P$(notas.2))/3 
                                    ASSIGN notas, {notaExamen1:8,notaExamen2:4,notaExamen3:5}
                                    ASSIGN media, (P$(notas.notaExamen1) +P$(notas.notaExamen2) +P$(notas.notaExamen3))/3 

                                    Y todo esto puede hacerse tan complejo como sea necesario introduciendo los SNA en cualquier parte de la notación para construir la ruta adecuada:

                                    ASSIGN numero_0,0
                                    ASSIGN numero_1,1
                                    ASSIGN numero_2,2
                                    
                                    ASSIGN notas, {notaExamen_0:8,notaExamen_1:4,notaExamen_2:5}
                                    ASSIGN media, (P$(notas.notaExamen_P$numero_0) + P$(notas.notaExamen_P$numero_1) + P$(notas.notaExamen_P$numero_2)) / 3

                                    O el acceso a un array dentro de un objeto o un objeto dentro de un array siguiendo el mismo proceder:

                                    ASSIGN grupo, [{nombre: "Luis"}, {nombre: "Marta"}]
                                    ASSIGN nombrePrimero, P$(grupo.0.nombre)
                                    

                                    Existe una única palabra reservada para estas rutas: LENGTH

                                    ASSIGN longitud, P$(unArray.LENGTH) 

                                    Modificación parcial mediante rutas
                                    También puede modificarse una parte de una estructura accediendo a ella directamente:

                                     ASSIGN unArray.0, {clave1: "texto1"} 

                                    convirtiendo el primer elemento del array en un objeto

                                    SNA de acceso sin evaluación:V$
                                    Mientras que P$ y X$ evalúan los contenidos para ser mostrados, V$ devuelve el contenido bruto (número, cadena, objeto o array) sin tratamiento alguno. Esto es necesario para transmitir un array o un objeto completo a través de un parámetro o hacer copias perfectas.

                                    ;realizar una copia:
                                    ASSIGN alumnos,[{nombre:"Ana",edad:20},{nombre:"Alberto",edad:22},{nombre:"Antonio",edad:19}]
                                    ASSIGN copiaAlumnos, V$(alumnos) 
                                    
                                    ;obtener un elemento concreto 
                                    ASSIGN elemento, V$(unArray.1) 
                                    
                                    ; o pasarlo como parámetro
                                    CALL irAClase, V$(unArray.1)
                                    

                                    Es importante diferenciar P$unTexto de V$unTexto.

                                    assign miNombre:"Antonio"
                                    move {name:text1,text:"Mi nombre es P$miNombre"} ; se muestra 'Mi nombre es Antonio'
                                    move {name:text1,text:"Mi nombre es V$miNombre"} ; se muestra 'Mi nombre es "Antonio"'

                                    Esta diferencia viene, precisamente, de que V$ es la obtención del dato bruto, que incluye la declaración del tipo de dato que es, en este caso, un string.
                                    V$ no debe usarse dentro de cadenas directamente. Se reserva para contextos donde se espera un valor estructurado.

                                    Como última nota, la característica de nulo es se trata con el operador "?" de tal forma que se puede asignar un valor si antes no ha sido asignado con:

                                    ASSIGN max_value,P$max_value ? P$max_value : 100

                                     

                                     


                                    Example:
                                    /* Variables Estructuradas: Arrays y Objetos
                                    Declaración, acceso y modificación de datos complejos.
                                    */
                                    
                                    POSITION {NAME:POS1,X:300,Y:100}
                                    
                                    initial posY,400
                                    Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                    Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                    Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                    Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                    Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                    Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}
                                    Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}
                                    Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}
                                    Graphic {NAME:Text9,Type:TEXT,X:324,Y:X$posY - 160}
                                    
                                    START 1 
                                    
                                    ;*****************************************************************
                                    
                                    GENERATE 1,0,0,1 {NAME:GEN1,X:100,Y:100}
                                    
                                    ; --- Declaraciones de todos los tipos ---
                                    ASSIGN miNumero, 10 * 2
                                    ASSIGN miTexto, "Resultado"
                                    ASSIGN miArray, [10, 20, 30]
                                    ASSIGN miObjeto, {clave1: 222, clave2: 333}
                                    ASSIGN miArrayDeObjetos, [10, {clave1: 555, clave2: (111 * 6)}, 30]
                                    
                                    ; --- Accesos por rutas ---
                                    ASSIGN suma, P$(miArray.0) + P$(miArray.1)
                                    
                                    ; --- Modificaciones por ruta ---
                                    ASSIGN miArray.2, 777
                                    ASSIGN miObjeto.clave1, {profundidad: "valor interno"}
                                    ASSIGN miObjeto.clave2, V$(miArray.2)
                                    
                                    ; --- Copias por valor bruto (estructuras completas) ---
                                    ASSIGN copiaObjeto, V$(miObjeto)
                                    ASSIGN elementoCopiado, V$(miArrayDeObjetos.1)
                                    
                                    ; --- Evaluación de longitud ---
                                    ASSIGN tamArray, P$(miArrayDeObjetos.LENGTH)
                                    
                                    ; --- Visualización ---
                                    move {name:Text1, text: "miNumero = P$miNumero"}
                                    move {name:Text2, text: "miTexto = P$miTexto"}
                                    move {name:Text3, text: "miArray.2 = P$(miArray.2)"}
                                    move {name:Text4, text: "miObjeto.clave1.profundidad = P$(miObjeto.clave1.profundidad)"}
                                    move {name:Text5, text: "miObjeto.clave2 (copiado) = P$(miObjeto.clave2)"}
                                    move {name:Text6, text: "miArrayDeObjetos.1.clave2 = P$(miArrayDeObjetos.1.clave2)"}
                                    move {name:Text7, text: "copiaObjeto.clave2 = P$(copiaObjeto.clave2)"}
                                    move {name:Text8, text: "elementoCopiado.clave1 = P$(elementoCopiado.clave1)"}
                                    move {name:Text9, text: "Longitud array = P$tamArray"}
                                    
                                    ADVANCE 100,0 {TO:POS1}
                                    ENDGENERATE 1
                                    
                                    • + SNA (String Numeric Accessor)

                                      Ya sabemos que un ASSIGN crea una característica (variable) asociada a cada entidad virtual y que un SAVEVALUE es un valor global del sistema.
                                      También hemos visto que se accede a ellos a través de expresiones como P$nombre o X$nombre.
                                      Estas expresiones se llaman SNA, String/Numeric Accessors, y son un mecanismo fundamental del lenguaje para acceder dinámicamente a cualquier valor del modelo.

                                      • P$nombre — Devuelve el contenido como texto. Es útil para trazas, etiquetas gráficas, o concatenaciones.
                                      • V$nombre — Devuelve el valor bruto (string, número, objeto o array), tal como fue asignado.
                                      • X$nombre — Devuelve el contenido de un SAVEVALUE (valor global).
                                      assign unaVariable,{nombre:"Antonio",edad:30}
                                      call fun, V$(unaVariable) ;envía el objeto como tal

                                       

                                      Además, existen SNA específicos para acceder a datos del sistema:

                                      • AC1$ — Tiempo actual del sistema.
                                      • TG1$ — Número de entidades pendientes de procesar.

                                      Y también a propiedades de recursos:

                                      • R$(recurso, propiedad) — Devuelve el valor de la propiedad solicitada.
                                      • R$(almacen1, IN) — Entradas al STORAGE.
                                      • R$(facility3, ENTRIES) — Número de usos de la FACILITY.
                                      • R$(market1, QUEUE) — Longitud actual de la cola.

                                      Los SNA no son instrucciones: son expresiones.
                                      Se evalúan en cualquier parte donde se espere un valor. Esto los convierte en una herramienta esencial para tomar decisiones, construir mensajes, hacer cálculos o acceder a estructuras complejas.

                                      En el ejemplo, se usan dos SNA muy representativos:

                                      • AC1$ el tiempo actual del sistema.
                                      • R$(VENTANILLA,QUEUE): estado actual de la cola del recurso.
                                      • D$N: devuelve el número identificador de la entidad virtual actual. En este caso, lo almacenamos en una variable numero mediante un ASSIGN, y luego accedemos a su valor con P$numero.

                                      Esto muestra cómo los SNA pueden ser usados tanto para obtener datos del sistema como para manipular variables propias de cada entidad, y cómo se integran fácilmente con los comandos visuales como move.

                                      Por último tenemos el SNA SYS$ que contiene variables básicas del sistema en un objeto como los datos de fecha y hora:

                                      ASSIGN sys, SYS$
                                        MOVE {name: REALTIME1, text:"P$(sys.date.year)/P$(sys.date.month)/P$(sys.date.day)"}
                                        MOVE {name: REALTIME2, text:"P$(sys.date.hour):P$(sys.date.min):P$(sys.date.sec)"}

                                      Acceso directo a datos de entidades:

                                      El SNA D$ permite consultar propiedades internas de cualquier entidad del sistema.
                                      Su sintaxis general es:

                                      D$(propiedad)              ; entidad actual
                                      D$(propiedad, numeroEntidad)   ; entidad específica

                                      Si se omite el segundo parámetro, se asume D$N (la entidad actual).

                                      Todas estas propiedades se aplican tanto a la entidad actual como a cualquier otra entidad si se indica su número:

                                      Propiedad Descripción
                                      N, ID Identificador de la entidad
                                      M0, M1 Parámetros M0 y M1
                                      BLOCK Índice del bloque actual
                                      STEP Número de paso ejecutado
                                      RESOURCETIME Tiempo dentro de la FACILITY/STORAGE actual
                                      RESOURCENAME Nombre del recurso que ocupa
                                      ADVANCESTART Tiempo en que comenzó el ADVANCE actual
                                      ADVANCELAPSE Duración restante del ADVANCE
                                      X, Y, Z Coordenadas visuales
                                      T Tiempo absoluto de creación
                                      CX Contexto de la entidad

                                      De forma especial se puede verificar la existencia de una entidad dentro del mismo SNA D$:

                                      if (D$(EXIST,1000)==1)
                                          ; la entidad 1000 está activa
                                      endif

                                       

                                       


                                      Example:
                                      /* SNA: Atributos del Sistema y Recursos
                                      Entidad virtual monitoriza el estado de la cola de un recurso.
                                      */
                                      
                                      SYSTEM {TYPE:ON_TIMER, TRIGGER:Timer1, INTERVAL: 5}
                                      
                                      FACILITY {NAME:Ventanilla,X:470,Y:308}
                                      Graphic {NAME:Text1,Type:TEXT,X:471,Y:254}
                                      Graphic {NAME:Text2,Type:TEXT,X:471,Y:234}
                                      
                                      START 100
                                      
                                      GENERATE 10,0 {name:gen1,X:115,Y:307}
                                      	ADVANCE 20 {to:Ventanilla}
                                          SEIZE Ventanilla
                                          ADVANCE 20 
                                          RELEASE Ventanilla
                                      ENDGENERATE 1
                                      
                                      ; Mostrar la cola actual cada cierto tiempo
                                      
                                      PROCEDURE Timer1
                                      	assign numero,D$N
                                      	move {name:Text1,text:"Soy la entidad virtual número: P$numero"}
                                      	move {name:Text2,text:"T: AC1$       Entidades en cola: R$(Ventanilla,QUEUE)"}
                                      	TERMINATE 
                                      ENDPROCEDURE 1
                                      • + Funciones nativas

                                        Las funciones nativas permiten operar directamente sobre el contenido de una variable, sea numérica, de texto, array u objeto. Algunas funciones modifican su contenido.

                                        El formato es siempre el mismo:

                                        ASSIGN.<FUNCION> nombreVariableDestino, parámetro [,número entidad destino]
                                        SAVEVALUE.<FUNCION> nombreVariableDestino, parámetro

                                        Si la variable no existe, se crea.

                                        Es muy importante decir que los objetos JSON "array" y "object" solo se pueden crear bajo estos dos bloques y el comando INITIAL. Cualquier otro COMANDO o BLOQUE que requiera parámetros como objeto o array recibirá el objeto ya creado bajo el SNA V$(variable).

                                        CALL fun, V$(estructura)
                                        CALL fun, P$(variable)
                                        CALL fun, {nombre_"Antonio"}  ; no se interpretan los objetos fuera de ASSIGN, SAVEVALUE ó INITIAL
                                        CALL fun, [1,2,V$(otraLista)]  ; no se interpretan los objetos fuera de ASSIGN, SAVEVALUE ó INITIAL

                                        Funciones:

                                        1. Variables numéricas

                                        • .INC valor – incrementa el valor actual, uno por defecto. Modifica su contenido
                                          Ejemplos:
                                          ASSIGN.INC contador
                                          ASSIGN.INC contador,10
                                          ASSIGN.INC miObjeto.puntuacion 

                                        2. Variables string

                                        • .TRIM – elimina espacios al principio y al final Modifica su contenido
                                          ASSIGN.TRIM miCadena 
                                        • .LENGTH – devuelve la longitud del string Devuelve el resultado en otro assign
                                           ASSIGN.LENGTH miCadena, longitud 
                                        • .JOIN – une los elementos en un string Devuelven un nuevo valor
                                          ASSIGN.JOIN cadenaFinal, {DATA:["uno","dos","tres"], SEP:" - "} 
                                          ASSIGN.JOIN cadenaFinal, {DATA:V$miArray, SEP:" - "} 

                                        3. Variables array

                                        • .PUSH valor – añade al final
                                          .UNSHIFT valor
                                          – añade al inicio
                                          .EXTEND array
                                          – añade los elementos de otro array
                                          ASSIGN.PUSH miArray, 45 ; [45]
                                          ASSIGN.PUSH miArray, {edad:45} ; [45,{edad:45}]
                                          ASSIGN.UNSHIFT miArray, "inicio" ; ["inicio",45,{edad:45}]
                                          ASSIGN.EXTEND miArray, V$otroArray ; ["inicio",45,{edad:45},"otroArray_a","otroArray_b"]
                                        • .SPLIT – divide un string por separador y lo convierte en array 
                                          ASSIGN.SPLIT resultado, {DATA:"uno,dos,tres", SEP:","} 
                                        • .SLICE– tona una sección de un array entre START y END (no incluido) 
                                          ASSIGN myArray [0,1,2,3,4]
                                          ASSIGN.SLICE myArray, {START:1,END:3} ; [1,2]

                                        4. Variables objeto

                                        • .MERGE objeto – fusiona claves desde otro objeto
                                          .DELETE ruta
                                          – elimina una propiedad del objeto Modifican su contenido
                                          ASSIGN.MERGE datos, V$nuevosDatos 
                                          ASSIGN.DELETE datos, direccion.calle 
                                        • .KEYS – obtiene una lista con los nombres de clave Devuelve el resultado en otra variable
                                          ASSIGN.KEYS datos, claves 

                                        Acceso por rutas

                                        Además de asignar un valor completo a una variable, el DSL permite asignar únicamente a una parte interna de un objeto o un array mediante rutas.
                                        Una ruta es una secuencia de claves o índices separada por puntos:

                                         

                                        ASSIGN variable.clave , 3
                                        ASSIGN variable.clave.subclave , 3
                                        ASSIGN variable.3 , 3
                                        ASSIGN variable.3.otroNivel , 3
                                        
                                        

                                        Rutas en objetos

                                        Las rutas en objetos no crean niveles intermedios automáticamente.
                                        Si una ruta no existe, se genera un error y la simulación se detiene:

                                        ASSIGN miObjeto, {}
                                        ASSIGN miObjeto.clave1, 123        ; OK
                                        ASSIGN miObjeto.clave2.otroNivel, 999  ; ERROR → clave2 no existe

                                        Los objetos deben existir previamente en cada nivel de la ruta:

                                        ASSIGN miObjeto, {clave2:{}}       ; Ahora sí existe clave2
                                        ASSIGN miObjeto.clave2.otroNivel, 999

                                        Rutas en arrays

                                        Los arrays sí permiten acceder directamente a cualquier índice, incluso si está vacío:

                                        ASSIGN miArray, []
                                        ASSIGN miArray.2, 99     ; Crea MIARRAY como [ , , 99 ]

                                        Además, es posible operar sobre una ruta interna con funciones nativas:

                                        ASSIGN miArray.3, []
                                        ASSIGN.PUSH miArray.3, 199

                                        Los SNA asociados VD$

                                        Algunos datos son de acceso directo a través del SNA asociado para, por ejemplo, validar la existencia de un elemento extraido de .POP antes de continuar

                                        • VD$(ruta) – Devuelve el valor bruto sin procesar. Similar a V$ pero dentro de una expresión.
                                        • VD$(ruta,LENGTH) – Longitud del contenido:
                                          • Cadenas o arrays: número de elementos.
                                          • Objetos: número de claves.
                                          • Números: número de dígitos.
                                        • VD$(ruta,TYPEOF) – Tipo de dato en forma de texto: "STRING", "NUMBER", "ARRAY", "OBJECT", etc.
                                        • VD$(ruta,ISEMPTY) – Devuelve "1" si el valor está vacío:
                                          • "" (cadena vacía), array vacío, objeto sin claves, número 0.
                                          • Devuelve "0" en cualquier otro caso.
                                        • VD$(ruta,EXIST) – Comprueba si la ruta existe completamente sin errores.
                                          • Devuelve "1" si es válida, "0" si no lo es.
                                        • VD$(ruta,HASKEY,clave) – Comprueba si un objeto tiene una determinada clave. Devuelve "1" o "0".
                                        • VD$(ruta,INCLUDES,valor) – Comprueba si un array o cadena incluye un valor dado. Devuelve "1" o "0".

                                         


                                        Example:
                                        POSITION {NAME:POS1,X:300,Y:100}
                                        
                                        initial posY,500
                                        Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                        Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                        Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                        Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                        Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                        Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}
                                        Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}
                                        Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}
                                        Graphic {NAME:Text9,Type:TEXT,X:324,Y:X$posY - 160}
                                        Graphic {NAME:Text10,Type:TEXT,X:324,Y:X$posY - 180}
                                        Graphic {NAME:Text11,Type:TEXT,X:324,Y:X$posY - 200}
                                        Graphic {NAME:Text12,Type:TEXT,X:324,Y:X$posY - 220}
                                        Graphic {NAME:Text13,Type:TEXT,X:324,Y:X$posY - 240}
                                        Graphic {NAME:Text14,Type:TEXT,X:324,Y:X$posY - 260}
                                        
                                        START 1
                                        
                                        GENERATE 1,0,0,1 {NAME:GEN1,X:100,Y:100}
                                        
                                        ; Variables base
                                        ASSIGN saludo, "Hola"
                                        ASSIGN complemento, "mundo"
                                        ASSIGN mensajeOriginal, "   Hola Mundo GPSS+   "
                                        ASSIGN nombres, ["Ana", "Luis", "Eva"]
                                        ASSIGN otrosNombres, ["Sonia", "David"]
                                        ASSIGN textoConSeparador, "uno,dos,tres,cuatro,cinco"
                                        
                                        
                                        
                                        ; Métodos de arrays
                                        assign.push nombres, "Ricardo" ; ["Ana", "Luis", "Eva" "Ricardo"]
                                        assign.unshift nombres, "Laura" ; ["laura", "Ana", "Luis", "Eva" "Ricardo"]
                                        assign.extend nombres, V$otrosNombres ; ["laura", "Ana", "Luis", "Eva", "Ricardo","Sonia", "David"]
                                        assign.slice nombres, {START:1,END:6} ; ["Ana", "Luis", "Eva", "Ricardo","Sonia"]
                                        
                                        ; String methods
                                        assign.join mensajeFinal, {DATA:["P$saludo", " ", "P$complemento", "!"]} ; "Hola Mundo!"
                                        assign.join nombresTexto, {DATA:V$nombres,SEP:" - "} ; "Ana - Luis - Eva"
                                        assign.split palabras, {DATA:V$textoConSeparador, SEP: ","}; ["uno","dos","tres","cuatro","cinco"]
                                        assign.trim mensajeOriginal ; "Hola Mundo GPSS+"
                                        assign.LENGTH mensajeOriginal, largoMensaje ; 16
                                        
                                        ; Objetos
                                        ASSIGN datos, {nombre:"Juan", edad:25}
                                        ASSIGN nuevosDatos, {ciudad:"Madrid", edad:30}
                                        assign.merge datos, V$nuevosDatos
                                        assign.delete datos, ciudad
                                        
                                        ; Numérico
                                        ASSIGN contador, 10
                                        assign.inc contador,-5
                                        assign.inc contador
                                        
                                        ; Claves
                                        ASSIGN alumnos, {ana:{edad:20}, luis:{edad:25}}
                                        assign.keys alumnos, clavesAlumnos
                                        
                                        ; Rutas de acceso
                                        ASSIGN miObjeto, {}
                                        ASSIGN miObjeto.clave1, 123
                                        ;ASSIGN miObjeto.clave2.otroNivel, "error" -> error por no existir
                                        ASSIGN miArray, []
                                        ASSIGN miArray.2, 99
                                        ASSIGN miArray.3, []
                                        ASSIGN.push miArray.3, 199
                                        
                                        
                                        ; Mostrar resultados
                                        move {name:Text1, text:"Mensaje final: P$mensajeFinal"}
                                        move {name:Text2, text:"Nombres (2): P$(nombres.2) LENGTH: VD$(nombres,LENGTH)"}
                                        move {name:Text3, text:"Nombres texto: P$nombresTexto"}
                                        move {name:Text4, text:"Palabras[1]: P$(palabras.1)"}
                                        move {name:Text5, text:"Texto trimmed: P$mensajeOriginal"}
                                        move {name:Text6, text:"Largo mensaje: P$largoMensaje"}
                                        move {name:Text7, text:"datos.edad: P$(datos.edad)"}
                                        move {name:Text8, text:"datos.ciudad: P$(datos.ciudad)"} ; debería estar vacío
                                        move {name:Text9, text:"Contador final: P$contador"}
                                        move {name:Text10, text:"clavesAlumnos[1]: P$(clavesAlumnos.1)"}
                                        move {name:Text11, text:"Tipo de nombres: VD$(nombres,TYPEOF)"}
                                        move {name:Text12, text:"Alumnos tiene luis?: VD$(alumnos,HASKEY,luis)"}
                                        move {name:Text13, text:"miObjeto.clave1: P$(miObjeto.clave1)"}
                                        move {name:Text14, text:"miArray.3.0: P$(miArray.3.0)"}
                                        
                                        
                                        
                                        ADVANCE 100,0 {TO:POS1}
                                        ENDGENERATE 1
                                        • + Acceso a variables de otras entidades

                                          Acceso a variables de otras entidades

                                          Además de trabajar con variables propias o globales, GPSS-Plus permite leer y escribir variables ASSIGN de cualquier entidad viva del sistema. 

                                          Lectura a través del SNA:

                                          El SNA habitual P$ admite un segundo parámetro que indica el número de entidad objetivo:

                                           P$(nombreVariable, númeroEntidad) 

                                          Esto permite consultar valores internos de otra entidad sin necesidad de copiarlos a un SAVEVALUE.

                                           move {name:text5, text:"La entidad 2 tiene: P$(unNumeroPrivado,2)"} 

                                          Esto accede directamente a la variable unNumeroPrivado activa en la entidad número 2.

                                          Escritura en otra entidad:

                                          Mediante un tercer parámetro en la instrucción ASSIGN, podemos guardar información en la entidad deseada:

                                           ASSIGN unNumero, 123, 1
                                          

                                          Comprobación de la existencia de la entidad:

                                          Se puede consultar la existencia con el SNA específico:

                                          if (D$(EXIST,1000)==1)

                                           


                                          Example:
                                          /* Acceso cruzado entre entidades usando P$ y ASSIGN destino */
                                          
                                          POSITION {NAME:POS_MOD,   X:286, Y:244}
                                          POSITION {NAME:POS_END,   X:741, Y:233, type:terminate}
                                          
                                          Graphic {NAME:T1, type:TEXT, X:455, Y:300}
                                          Graphic {NAME:T2, type:TEXT, X:455, Y:188}
                                          
                                          START 60   ; habrá 6 entidades (3 de cada tipo)
                                          
                                          /* --- ENTIDAD TIPO A --- */
                                          GENERATE 30,0,15 {NAME:GA, X:100, Y:70, ECOLOR:#ff3333, subtitle:"A"}
                                              ASSIGN miValor, 100
                                              mod {subtitle:"Mi valor P$miValor"}
                                              
                                          
                                              ADVANCE 20 {to:POS_MOD}
                                          
                                              ; Si existe la entidad B (2, 4, 6...)
                                              IF (D$(EXIST, D$N+1)==1)
                                              	ASSIGN nEntidadDestino,D$N+1
                                                  ASSIGN miValor, P$(miValor)+10, P$nEntidadDestino   ; A incrementa el valor de B
                                                  MOVE {name:T1, text:"Cambio el valor de P$nEntidadDestino a: [P$(miValor,P$nEntidadDestino)]"}
                                              ENDIF
                                          
                                              ADVANCE 50 {to:POS_END}
                                          ENDGENERATE 3
                                          
                                          /* --- ENTIDAD TIPO B --- */
                                          GENERATE 30,0 {NAME:GB, X:103, Y:407, ECOLOR:#3366ff, subtitle:"B"}
                                              ASSIGN miValor, 200
                                              mod {subtitle:"Mi valor P$miValor"}
                                          
                                              ADVANCE 20 {to:POS_MOD}
                                          
                                              ; Si existe la entidad A (1, 3, 5...)
                                              IF (D$(EXIST, D$N-1)==1)
                                              	ASSIGN nEntidadDestino,D$N-1
                                                  ASSIGN miValor, P$(miValor)-5, P$nEntidadDestino   ; B reduce el valor de A
                                                  MOVE {name:T2, text:"Cambio el valor de P$nEntidadDestino a: [P$(miValor,P$nEntidadDestino)]"}
                                              ENDIF
                                          
                                              ADVANCE 50 {to:POS_END}
                                          ENDGENERATE 3
                                          
                                          • + Estructuras de control

                                            Las estructuras de control permiten a las entidades tomar decisiones, repetir bloques de código o recorrer colecciones. GPSS-Plus incorpora estas estructuras con una sintaxis simple y adaptada al paradigma declarativo del lenguaje.

                                            1. IF / ELSE / ENDIF

                                            Permite evaluar una condición y ejecutar bloques alternativos según el resultado.

                                            IF (P$edad > 18 || "P$nombre"=="Eva")
                                                move {name:text1,text:"Mayor de edad o es Eva"}
                                            ELSE
                                                move {name:text1,text:"Menor de edad o no es Eva"}
                                            ENDIF
                                            
                                            • La condición debe estar entre paréntesis.
                                            • Se pueden usar operadores lógicos (&&, ||) y comparaciones (==, !=, >, <, >=, <=).
                                            • Todo se evalúa como una única expresión dentro de los paréntesis.

                                            2. FOREACH / ENDFOREACH

                                            ASSIGN listaAlumnos,[{nombre:"Ana"},{nombre:"Luis"}]
                                            FOREACH alumno IN V$listaAlumnos
                                                move {name:text1,text:"Alumno: P$alumno.nombre"}
                                            ENDFOREACH
                                            • Permite iterar sobre los elementos de una colección (array u objeto).
                                            • La variable que representa cada elemento debe ser un ASSIGN.
                                            • Se puede iterar sobre arrays ([1,2,3]) o arrays de objetos ([{nombre:"Ana"}, {nombre:"Luis"}]).
                                            • La variable usada en la iteración (por ejemplo color, entidad, clave) debe ser un ASSIGN, creado automáticamente si no existe.
                                            • El tercer parámetro de IN y IN_OBJECT (el origen de los datos) debe ser una referencia válida por su SNA V$(ruta)

                                            Modos disponibles

                                            • IN: Itera sobre un array.
                                            • IN_OBJECT: Itera sobre las claves de un objeto.
                                            • IN_RESOURCE: Itera sobre las entidades actualmente utilizando el recurso (FACILITY, STORAGE, etc.).
                                            • IN_QUEUE: Itera sobre las entidades en la cola de un recurso (FACILITY, STORAGE, etc.).
                                            FOREACH color, IN, V$(persona.colores)
                                                assign resultado, "P$color"
                                            ENDFOREACH
                                            
                                            FOREACH clave, IN_OBJECT, V$(persona)
                                                assign resultado, "P$clave"
                                            ENDFOREACH
                                            
                                            FOREACH entidad, IN_RESOURCE, Ventanilla
                                                assign ids, "P$entidad"
                                            ENDFOREACH
                                            
                                            FOREACH entidad, IN_QUEUE, Ventanilla
                                                assign ids, "P$entidad"
                                            ENDFOREACH


                                            3. REPEAT / UNTIL

                                            ASSIGN intento, 0
                                            
                                            REPEAT
                                                ASSIGN.INC intento
                                                move {name:text1,text:"Intento número: P$intento"}
                                            UNTIL (P$intento >= 3)
                                            
                                            
                                            • Repite un bloque hasta que una condición se cumpla.
                                            • Se ejecuta al menos una vez.
                                            • La condición entre paréntesis se evalúa tras cada repetición.
                                            • El bloque entre REPEAT y UNTIL puede contener cualquier otra instrucción GPSS-Plus, incluso estructuras anidadas.

                                            4. SWITCH / CASE / ENDCASE/ ENDSWITCH

                                            SWITCH P$color
                                            
                                            CASE ==,"rojo"
                                                move {name:text1,text:"Color ROJO"}
                                            
                                            CASE ==,"azul"
                                                move {name:text1,text:"Color AZUL"}
                                            
                                            CASE ==,"verde"
                                                move {name:text1,text:"Color VERDE"}
                                            
                                            ENDCASE
                                            
                                            ENDSWITCH
                                            • Permite ejecutar bloques específicos según el valor de una expresión, similar a switch-case en otros lenguajes.
                                            • La expresión del SWITCH puede ser cualquier valor evaluable (número, texto, etc.).
                                            • Cada CASE compara el valor con un operador (==, !=, >, <, etc.) y un segundo argumento.
                                            • Se ejecuta solo el primer CASE que cumpla la condición.
                                            • No hay necesidad de BREAK, ya que no continúa evaluando los siguientes casos.
                                            • El bloque se cierra con ENDCASE.

                                             5. WHILE / ENDWHILE

                                            assign intentos, ""
                                            assign intento, 0
                                            assign maximo, 5
                                            
                                            WHILE (P$intento < P$maximo)
                                                assign.inc intento
                                                assign intentos, "P$intentos P$intento"
                                            ENDWHILE
                                            
                                            move {name:text1, text:"Intentos: P$intentos"}

                                             

                                            • Ejecuta un bloque de código mientras se cumpla una condición. A diferencia de REPEAT, la condición se evalúa antes de cada iteración.
                                            • La condición debe ir entre paréntesis y puede usar expresiones con SNA (P$, VD$, D$) y operadores lógicos.
                                            • Si la condición es falsa al inicio, el bloque no se ejecuta nunca.

                                             


                                            Example:
                                            /* Estructuras de Control: FOREACH y REPEAT
                                            Iteración sobre arrays, objetos y recursos.
                                            */
                                            
                                            POSITION {NAME:POS1,X:651,Y:480}
                                            initial posY,400
                                            
                                            Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                            Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                            Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                            Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                            Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                            Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160,color:#000000}
                                            
                                            Facility {NAME:Facility1,X:382,Y:483,capacity:3}
                                            
                                            
                                            START 100
                                            
                                            GENERATE 10,0 {name:GEN1,X:110,Y:477}
                                            
                                            assign edad,2
                                            assign nombre,"Eva"
                                            
                                            ; IF (P$nombre=="Eva") -> Expresión incorrecta, faltan las comillas
                                            IF ("P$nombre"=="Eva")
                                            	move {name:Text1, text:"Expresiones correctas"}
                                            endif
                                            
                                            IF (P$edad > 18 || V$nombre=="Eva")
                                            	move {name:Text1, text:"Expresiones correctas"}
                                            endif
                                            
                                            
                                            ASSIGN intentos, 0
                                            move {name:Text1, text:"Color actual: P$valor"}
                                            
                                            ; FOREACH: recorrer todos los colores
                                            assign gustos_luis,""
                                            ASSIGN persona, {nombre:"Luis"
                                            		, gustos:["leer", "música", "viajar", "dormir"]
                                                    , ciudad:"Madrid"
                                                    , pais:"España"
                                                    }
                                            FOREACH gusto, IN, V$(persona.gustos)
                                                assign gustos_luis,"P$gustos_luis P$gusto"
                                            ENDFOREACH
                                            move {name:Text1, text:"Gustos de Luis: P$gustos_luis"}
                                            
                                            assign claves_valores,""
                                            FOREACH clave, IN_OBJECT, V$(persona)
                                                assign claves_valores,"P$(claves_valores) \n P$clave  P$(persona.P$clave)"
                                            ENDFOREACH
                                            
                                            move {name:Text6, text:"Claves y valores de persona:\n P$(claves_valores)"}
                                            
                                            assign entidades_cola,""
                                            FOREACH entidad, IN_QUEUE, Facility1
                                                assign entidades_cola,"P$entidades_cola P$entidad"
                                            ENDFOREACH
                                            move {name:Text3, text:"entidades en cola: P$entidades_cola"}
                                            
                                            
                                            assign entidades_dentro,""
                                            FOREACH entidad, IN_RESOURCE, Facility1
                                                assign entidades_dentro,"P$entidades_dentro P$entidad"
                                            ENDFOREACH
                                            move {name:Text4, text:"entidades dentro: P$entidades_dentro"}
                                            
                                            assign intento, 0
                                            assign maximo, 3
                                            
                                            assign intentos,""
                                            REPEAT
                                                assign.inc intento
                                                assign intentos,"P$intentos P$intento"
                                            UNTIL (P$intento >= P$maximo)
                                            move {name:Text5, text:"intentos: P$intentos"}
                                            
                                            advance 10,0 {to:Facility1}
                                            SEIZE Facility1
                                            advance 20,30
                                            RELEASE Facility1
                                            
                                            ADVANCE 100,0 {TO:POS1}
                                            ENDGENERATE 1
                                            
                                            • + Subprocesos y llamadas

                                              Subprocesos y llamadas en GPSS-Plus (introducción)

                                              GPSS-Plus incorpora un sistema completo de subprocesos que no existía en GPSS clásico, gracias a la introducción de pilas de ejecución (stacks).
                                              Esto permite que una entidad —o incluso el propio sistema— pueda invocar procedimientos independientes y retomar la ejecución de forma controlada.

                                              Existen varios tipos de llamada, según quién invoca y quién ejecuta el procedimiento:

                                              • CALL — Lo ejecuta la misma entidad que lo invoca.
                                              • SIGNAL — Lo ejecuta otra entidad, pero solo cuando la actual libera el turno.
                                              • SIGNALNOW — Lo ejecuta otra entidad inmediatamente, interrumpiendo a la actual.
                                              • TIMEOUT — Lo ejecuta una entidad virtual (VE) en un tiempo futuro.
                                              • ON_* — Lo ejecuta una VE al producirse un evento en un recurso (SEIZE, RELEASE, ENTER...).
                                              • TIMER — Lo ejecuta una VE periódicamente.
                                              • PRE_RUN — Lo ejecuta una VE al inicio de la simulación.

                                              Todos ellos saltan a un bloque PROCEDURE, que debe finalizar con ENDPROCEDURE.

                                              Nombres jerárquicos

                                              Los procedimientos pueden organizarse como rutas:

                                              agente.abrir
                                              cliente.saludar
                                              robot1.motor.arrancar

                                              El valor devuelto se almacenará automáticamente en una variable cuyo nombre coincide con el último fragmento ("abrir", "saludar", "arrancar").

                                              Parámetros y SNA disponibles

                                              Dentro de un PROCEDURE pueden consultarse los parámetros recibidos:

                                              • P$PARAM_A — valor evaluado

                                              • V$PARAM_A — valor bruto (array, objeto, número, string sin evaluar)

                                              Finalización del PROCEDURE

                                              El procedimiento puede terminar con:

                                              • RETURN valor — Vuelve al llamador.
                                              • RETURN_RESTORE valor — Para SIGNAL/SIGNALNOW: la entidad destino retoma exactamente donde estaba.
                                              • RETURN_RETRY valor — Para SIGNAL/SIGNALNOW: la entidad destino repite su paso actual (típico en reintentos de recursos).

                                               

                                               

                                              • + CALL

                                                El comando CALL permite que la propia entidad activa salte a un procedimiento y lo ejecute inmediatamente.
                                                Al finalizar, la entidad vuelve exactamente al paso siguiente del que llamó.

                                                Es el comportamiento más simple y directo del sistema de subprocesos.

                                                Sintaxis:

                                                CALL nombreProcedimiento, parámetroA, parámetroB, ...
                                                
                                                • Los parámetros se reciben dentro del procedimiento como P$PARAM_A, P$PARAM_B, etc.
                                                • El valor devuelto por el procedimiento se guarda automáticamente en una variable llamada como el último fragmento del nombre del PROCEDURE.

                                                Ejemplos de nombre → variable de resultado:

                                                CALL calcular, 10   →  P$calcular
                                                CALL cliente.sumar, 20,30  →  P$sumar
                                                CALL robot.motor.arrancar → P$arrancar

                                                Finalización del PROCEDURE:

                                                ENDPROCEDURE valor

                                                Finaliza y devuelve el valor indicado:

                                                ENDPROCEDURE 3 ; P$calcular = 3

                                                RETURN valor

                                                Hace lo mismo, pero resulta más explícito y legible:

                                                RETURN 3
                                                ENDPROCEDURE

                                                Ambas formas son equivalentes cuando se usa CALL.

                                                 

                                                Acceso a los parámetros:

                                                Dentro del procedimiento:

                                                • P$PARAM_A devuelve el valor evaluado.
                                                • V$PARAM_A devuelve el valor bruto (arrays, objetos, strings sin evaluar).

                                                Ejemplos:

                                                • P$(PARAM_A.nombre) si pasaste un objeto.
                                                • P$(PARAM_B.2) si pasaste un array.

                                                 


                                                Example:
                                                /* CALL */
                                                
                                                POSITION {NAME:POS1,   X:286, Y:244}
                                                
                                                
                                                initial posY,500
                                                Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                                Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}
                                                
                                                
                                                
                                                START 1
                                                
                                                
                                                GENERATE 10,0,0,1 {NAME:GA, X:100, Y:70}
                                                    ASSIGN datos, {nombre:"Ana", edad:30}
                                                    ASSIGN numero, 123
                                                    ASSIGN colores, ["rojo","verde"]
                                                
                                                    CALL procesar, V$datos, V$numero, V$colores,30
                                                
                                                    move {name:Text1, text:"Resultado del procedimiento: P$(procesar)"}
                                                
                                                    ADVANCE 10 {to:POS1}
                                                ENDGENERATE 1
                                                ;---------------------------------------
                                                PROCEDURE procesar
                                                    move {name:Text2, text:"Nombre: P$(PARAM_A.nombre)"}
                                                    move {name:Text3, text:"Edad: P$(PARAM_A.edad)"}
                                                    move {name:Text4, text:"Número: P$PARAM_B"}
                                                    move {name:Text5, text:"Primer color: P$(PARAM_C.0)"}
                                                    move {name:Text6, text:"Dato directo: P$(PARAM_D)"}
                                                
                                                    RETURN "OK"
                                                ENDPROCEDURE
                                                
                                                
                                                
                                                • + SIGNAL y SIGNALNOW

                                                  SIGNAL

                                                  SIGNAL invoca un PROCEDURE para que sea ejecutado por otra entidad, pero no interrumpe a la entidad que lo llama.

                                                  • La ejecución ocurrirá en el mismo instante de tiempo (AC1).

                                                  • Entrará al final del conjunto de eventos programados para ese mismo tiempo.

                                                  • La entidad actual continúa normalmente, termina su ciclo y luego la entidad señalada tomará el control en el momento programado.

                                                  • Es una llamada diferida.

                                                  En resumen: SIGNAL programa la ejecución del procedimiento, pero no interrumpe a la entidad que lo invoca.

                                                  SIGNALNOW

                                                  SIGNALNOW invoca un PROCEDURE para que sea ejecutado de inmediato por otra entidad, interrumpiendo a la entidad actual.

                                                  Puntos clave:

                                                  • La entidad invocada toma el control inmediatamente, dentro del mismo AC1.
                                                  • La entidad llamante queda suspendida, y retomará exactamente donde estaba (o repetirá la operación, según el retorno).
                                                  • La entidad señalada pasa a ser la primera en ejecutarse en ese instante.
                                                  • Es una llamada preemptiva.

                                                  Normalmente, el PROCEDURE debería finalizar con:

                                                  • RETURN_RESTORE
                                                    Si la entidad interrumpida debe reanudar exactamente donde estaba.

                                                  • RETURN_RETRY
                                                    Si debe repetir la operación en la que estaba (típico para SEIZE, ENTER o recursos).

                                                  En resumen: SIGNALNOW provoca una interrupción inmediata; la entidad señalada ejecuta el procedimiento sin esperar.

                                                  En el ejemplo:

                                                  Funcionamiento de SIGNAL / SIGNALNOW con agentes activos y dormidos

                                                  Cuando un procedimiento es ejecutado mediante SIGNAL o SIGNALNOW, el comportamiento final depende del estado en el que se encuentre la entidad receptora.

                                                  En este ejemplo se muestran dos casos muy distintos:

                                                  • agenteVivo → está en un bucle con ADVANCE, siempre activo.
                                                  • agenteDormido → está detenido en una instrucción REST, esperando indefinidamente.

                                                  El agente vivo tiene una tares de procesar un texto que tarda un tiempo considerable. Mientras no lo está procesando, muestra constantemente el tiempo AC1, pero cuando entra en el proceso, no es capaz de hacerlo notándose que el tiempo no se actualiza.

                                                  Si cambiamos la salida del procedure a:

                                                  RETURN P$PARAM_A + P$PARAM_B

                                                  Notaremos que las llamadas a sumar interrumpen la tarea.

                                                   

                                                  En resumen, esto permite ver las diferencias reales entre:

                                                  • RETURN
                                                  • RETURN_RESTORE
                                                  • RETURN_RETRY

                                                  PARA EL AGENTE VIVO (en ADVANCE)

                                                  • RETURN -> ERROR
                                                    El agente vuelve a su ejecución normal.
                                                    El problema es que el agente da por terminado su ADVANCE y finaliza el proceso AUNQUE aún le falte tiempo de ejecución.
                                                  • RETURN_RESTORE -> CORRECTO
                                                    El agente vuelve correctamente a su ejecución normal.
                                                    No pierde su turno ni su posición temporal.
                                                    El advance del proceso que estuviese ejecutando continúa exactamente donde se interrumpió.
                                                  • RETURN_RETRY -> ERROR FATAL Error fatal, la entidad vuelve a ejecutar el SIGNALNOW indefinidamente. Usar este retorno está reservado a circunstancias en las que se sabe que la entidad está en la cola de un recurso.

                                                   

                                                  PARA EL AGENTE DORMIDO (en RESTROOM)

                                                  • RETURN -> ERROR
                                                    RETURN indica que el procedimiento ha terminado, por lo que el agente continúa con el siguiente paso de su programa. Ese paso es TERMINATE_VE, por lo que el agente muere y la siguiente llamada de signalnow no tendrá agente receptor.
                                                  • RETURN_RESTORE -> CORRECTO
                                                    El agente vuelve correctamente a su ejecución normal dentro del RESTROOM.
                                                  • RETURN_RETRY -> CASI CORRECTO
                                                    El agente vuelve a ejecutar el REST quedando de nuevo dormido en el RESTROOM. No está en la cola del restroom, por lo que volverá a entrar indicando que el restroom tiene una entidad más cuando es incorrecto. Para evitarlo, habría que tratarlo como si interrumpiésemos una entidad en un SEIZE sacándola primero de la lista de ocupantes con WAKE.

                                                   

                                                   


                                                  Example:
                                                  /* SIGNAL y SIGNALNOW en agentes permanentes */
                                                  
                                                  SYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}
                                                  
                                                  POSITION {NAME:POS_1,   X:615, Y:398}
                                                  
                                                  Graphic {NAME:T_resultado1, type:TEXT, X:425, Y:281,text:"resultado1"}
                                                  Graphic {NAME:T_resultado2, type:TEXT, X:425, Y:256,text:"resultado2"}
                                                  Graphic {NAME:T2, type:TEXT, X:430, Y:501}
                                                  Graphic {NAME:T3, type:TEXT, X:430, Y:531}
                                                  Graphic {NAME:T4, type:TEXT, X:426, Y:310}
                                                  Graphic {NAME:T5, type:TEXT, X:425, Y:188}
                                                  
                                                  RESTROOM {name:RestRoomAgenteDormido,x:423,y:118}
                                                  
                                                  START 200
                                                  
                                                  /* ---- PRE RUN: Crear los agentes ---- */
                                                  PROCEDURE PRE_RUN
                                                      TIMEOUT agenteVivo.loop, 0
                                                      TIMEOUT agenteDormido.loop, 0
                                                      TERMINATE_VE
                                                  ENDPROCEDURE
                                                  
                                                  
                                                  /* ---- GENERADOR DE ENTIDADES NORMALES ---- */
                                                  GENERATE 15,0 {NAME:G1, X:153, Y:411}
                                                  
                                                      ASSIGN miValor, D$N * 10
                                                      MOVE {name:T4, text:"Entidad D$N comienza con miValor = P$miValor"}
                                                  
                                                      ADVANCE 10 {to:POS_1}
                                                  
                                                      ; Llamada SIGNAL diferida
                                                      SIGNAL agenteVivo.procesar, X$nAgenteVivo, "Hola"
                                                  
                                                      ; Llamada SIGNALNOW inmediata
                                                      SIGNALNOW agenteVivo.sumar, X$nAgenteVivo, P$miValor, 5
                                                  	MOVE {name:T_resultado1, text:"Resultado del agente Vivo = P$sumar"}
                                                  
                                                  	SIGNALNOW agenteDormido.sumar, X$nAgenteDormido, P$miValor, 5
                                                  	MOVE {name:T_resultado2, text:"Resultado del agente dormido = P$sumar"}
                                                  
                                                  
                                                  ENDGENERATE 4
                                                  
                                                  
                                                  /* ---- AGENTE VIVO ---- */
                                                  PROCEDURE agenteVivo.loop
                                                      SAVEVALUE nAgenteVivo, D$N
                                                      assign procesando,0
                                                      WHILE (1==1)
                                                          MOVE {name:T3, text:"Agente Vivo [P$(nAgente)] activo en t = AC1$"}
                                                          ADVANCE 2
                                                      ENDWHILE
                                                      terminate_ve
                                                  ENDPROCEDURE
                                                  
                                                  /* ---- AGENTE DORMIDO ---- */
                                                  PROCEDURE agenteDormido.loop
                                                      SAVEVALUE nAgenteDormido, D$N
                                                      assign procesando,0
                                                      MOVE {name:T5, text:"Agente Dormido [P$(nAgente)] activo"}
                                                      rest RestRoomAgenteDormido
                                                      terminate_ve
                                                  ENDPROCEDURE
                                                  
                                                  
                                                  /* ---- PROCEDIMIENTOS INVOCADOS POR ENTIDADES ---- */
                                                  
                                                  PROCEDURE agenteVivo.procesar
                                                  	if (P$procesando==1)
                                                      	return
                                                      endif
                                                      assign procesando,1
                                                      MOVE {name:T2, text:"SIGNAL → agente INICIA proceso mensaje: P$PARAM_A"}
                                                      ADVANCE 80
                                                      MOVE {name:T2, text:"SIGNAL → agente FINALIZA proceso mensaje: P$PARAM_A"}
                                                      assign procesando,0
                                                      RETURN ; viene de signal y no afecta el tipo a la entidad
                                                  ENDPROCEDURE
                                                  
                                                  PROCEDURE agenteVivo.sumar
                                                      ; PARAM_A = valor base
                                                      ; PARAM_B = incremento
                                                     ; RETURN P$PARAM_A + P$PARAM_B ; finaliza el proceso para admitir otro
                                                      RETURN_RESTORE P$PARAM_A + P$PARAM_B ; el proceso permanece en su tiempo
                                                     ; RETURN_RETRY P$PARAM_A + P$PARAM_B ; explota por no estar en un SEIZE o similar
                                                  ENDPROCEDURE
                                                  
                                                  ;-------------------------------------------------------------------
                                                  
                                                  PROCEDURE agenteDormido.sumar
                                                      ; PARAM_A = valor base
                                                      ; PARAM_B = incremento
                                                     
                                                     ; RETURN P$PARAM_A + P$PARAM_B ; explota por morir el agente al salir del restroom
                                                      
                                                      RETURN_RESTORE P$PARAM_A + P$PARAM_B ; procesa quedandose el agente en su restroom
                                                      
                                                     ; WAKE RestRoomAgenteDormido,0,X$nAgenteDormido ; saca al agente de estar en REST
                                                     ; RETURN_RETRY P$PARAM_A + P$PARAM_B ; procesa reiniciando el agente su restroom
                                                     
                                                  ENDPROCEDURE
                                                  
                                                  
                                                  • + Procedimientos de VE: TIMER, TIMEOUT, ON_*, PRE_RUN

                                                    TIMEOUT / ON_* / PRE_RUN / TIMER

                                                    Estos procedimientos son ejecutados por entidades virtuales (VE) recién creadas.
                                                    Las VE deben finalizar como cualquier entidad con un TERMINATE o TERMINATE_VE. La diferencia entre ambos BLOQUES es que la primera finaliza cualquier entidad y la segunda solo entidades virtuales. Usando el bloque TERMINATE_VE , el PROCEDURE podrá ser invocado para ser ejecutado por ambos tipos de entidades. 
                                                    Es importante decir que deben ser TERMINADAS obligatoriamente pues de llegar al ENDPROCEDURE no sabrían donde retornar.

                                                    TIMEOUT abrirRecurso, 50, P$valor, V$Objeto
                                                    
                                                    PROCEDURE abrirRecurso
                                                      move {name:text1, text:"Ejecutado en t=AC1$"}
                                                      TERMINATE_VE
                                                    ENDPROCEDURE

                                                     


                                                    Example:
                                                    POSITION {NAME:Salida, X:624, Y:199}
                                                    
                                                    Graphic {NAME:T1, type:TEXT, X:493, Y:505}
                                                    Graphic {NAME:T2, type:TEXT, X:493, Y:476}
                                                    Graphic {NAME:T3, type:TEXT, X:492, Y:444}
                                                    
                                                    Facility {NAME:Ventana, ON_RELEASE: cuandoSale, X:249, Y:335 }
                                                    
                                                    SYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}
                                                    
                                                    START 50
                                                    
                                                    ;----------------------------------------
                                                    PROCEDURE PRE_RUN
                                                        move {name:T1, text:"PRE_RUN ejecutado en t = AC1$"}
                                                        TIMEOUT avisoInicial, 10
                                                        TERMINATE_VE
                                                    ENDPROCEDURE
                                                    
                                                    ;----------------------------------------
                                                    GENERATE 15,0 {NAME:"GEN1", X:100, Y:100}
                                                    	ADVANCE 10 {to:Ventana}
                                                        SEIZE Ventana
                                                        ADVANCE 5
                                                        RELEASE Ventana        ; disparará ON_RELEASE
                                                        ADVANCE 10 {to:Salida}
                                                    ENDGENERATE 1
                                                    
                                                    ;----------------------------------------
                                                    PROCEDURE avisoInicial
                                                        move {name:T2, text:"TIMEOUT ejecutado en t = AC1$"}
                                                        TERMINATE_VE
                                                    ENDPROCEDURE
                                                    
                                                    PROCEDURE cuandoSale
                                                        move {name:T3, text:"ON_RELEASE ejecutado en t = AC1$"}
                                                        TERMINATE_VE
                                                    ENDPROCEDURE
                                                    
                                                    • + SCAPE / LOAD / UNLOAD

                                                      SCAPE

                                                      Es una llamada especial que tiene como destino un GENERATE.

                                                      SCAPE se usa cuando una entidad debe abandonar completamente su flujo actual y comenzar uno nuevo.
                                                      Esto es útil cuando una condición requiere reiniciar, cambiar de comportamiento o bifurcar el flujo de manera irreversible.

                                                      El salto SCAPE lleva a los únicos puntos seguros limpios de stack de direcciones: los GENERATES.
                                                      Conservará todos los valores de assign.

                                                      SCAPE nombreGenerate, 50, P$valor, V$Objeto
                                                      

                                                       

                                                      BACKPACK (LOAD / UNLOAD)

                                                      Una entidad solo puede ser cargada (LOAD) si está en REST porque en ese estado:

                                                      • Está dormida.

                                                      • No consume ciclo.

                                                      • No puede despertar sola, solo mediante WAKE por terceros.

                                                      • Es “segura” de manipular.

                                                      Por eso el BACKPACK es una herramienta de logística no de control de flujo. Se transporta cualquier entidad.

                                                       

                                                      Mecanismo para transportar entidades dormidas en un RESTROOM.

                                                      • LOAD: la entidad cargada es retirada del RESTROOM y pasa al backpack.

                                                      • UNLOAD: al llegar al destino, la entidad descargada empieza una nueva vida desde un GENERATE especificado.

                                                       

                                                      En el ejemplo:

                                                      En este ejemplo se simula una ciudad y un museo conectados por una carretera.
                                                      Los ciudadanos generan su propio flujo de vida: algunos viajan en vehículo particular y otros esperan en la parada de autobús.

                                                      Cuando llegan al museo o regresan a la ciudad, las entidades “cambian de mundo” utilizando SCAPE, iniciando un nuevo flujo en el generador correspondiente.

                                                      Los autobuses funcionan como transportistas: cargan ciudadanos dormidos (LOAD), los desplazan por la carretera y los descargan en el generador de destino (UNLOAD), donde cada uno continúa su propio flujo.

                                                      De este modo, cada entidad continúa su ciclo natural según su medio de transporte.


                                                      Example:
                                                      POSITION {NAME:Salida, X:178, Y:39}
                                                      POSITION {NAME:inicioCarretera, X:150, Y:313}
                                                      POSITION {NAME:finCarretera, X:514, Y:314}
                                                      
                                                      Facility {NAME:museo, capacity:50, X:534, Y:77 }
                                                      
                                                      Restroom {NAME:paradaAutobusCiudad, X:68, Y:321 }
                                                      Restroom {NAME:paradaAutobusMuseo, X:588, Y:312 }
                                                      
                                                      START 500
                                                      
                                                      ;----------------------------------------
                                                      GENERATE 15,0 {NAME:"GenUsuarios", X:100, Y:100 }
                                                      
                                                      	if (P$museoVisitado==1)
                                                         		advance 100,50 {from:inicioCarretera ,to:Salida}
                                                              terminate 1
                                                          endif
                                                          
                                                      	ASSIGN TIPO,0 ; EN VEHICULO PARTICULAR
                                                          if (RANDOM >0.2)
                                                          ASSIGN TIPO,1 ; EN AUTOBUS
                                                          endif
                                                          advance 10 {to:inicioCarretera}
                                                          if (P$TIPO==1)
                                                          REST paradaAutobusCiudad
                                                          endif
                                                          advance 50,10 {to:finCarretera}
                                                          SCAPE GenMuseo
                                                      ENDGENERATE 1
                                                      
                                                      ;----------------------------------------
                                                      GENERATE 0,0,0,0 {NAME:"GenMuseo", X:600, Y:160 }
                                                      	advance 20,30 {from:finCarretera,to:museo}
                                                          seize museo
                                                          advance 30,50
                                                          assign museoVisitado,1
                                                          mod {color:green}
                                                          release museo
                                                          
                                                          advance 10 {to:finCarretera}
                                                      
                                                          if (P$TIPO==1)
                                                          REST paradaAutobusMuseo
                                                          endif
                                                          advance 50 {to:inicioCarretera}
                                                          SCAPE GenUsuarios
                                                      ENDGENERATE 1
                                                      
                                                      ;----------------------------------------
                                                      
                                                      
                                                      ;----------------------------------------
                                                      GENERATE 50,0,0,2 {NAME:"GenAutobuses", X:300, Y:460,ecolor:red,eradio:10,visible:0 }
                                                      	advance 50 {to:inicioCarretera}
                                                          while (1==1)
                                                          	load miBackpackIda, paradaAutobusCiudad
                                                              mod {subtitle:"Pasajeros P$(miBackpackIda.LENGTH)"}
                                                        	  	advance 80,10 {to:finCarretera}
                                                         	 	unload miBackpackIda, GenMuseo
                                                              advance 10
                                                              load miBackpackVuelta, paradaAutobusMuseo
                                                              mod {subtitle:"Pasajeros P$(miBackpackVuelta.LENGTH)"}
                                                        	  	advance 80,10 {to:inicioCarretera}
                                                              unload miBackpackVuelta, GenUsuarios
                                                              advance 10
                                                          endwhile
                                                      ENDGENERATE 0
                                                      
                                                      
                                                      
                                                    • + Recursos retenedores
                                                      • + Generalidades

                                                        Tipos de recursos disponibles en GPSS-Plus:

                                                        • Facility → Recurso exclusivo con capacidad. Ej.: máquina, servidor.

                                                        • Storage → Recurso acumulativo. Ej.: almacén, depósito.

                                                        • Restroom → Recurso retenedor. Las entidades esperan a ser liberadas.

                                                        • Conditions → Recurso retenedor condicional. Paso según lógica.

                                                        • Stock → Almacén inteligente de productos etiquetados.

                                                        • Stater → Máquina de estados finitos. Control de procesos.

                                                        • Queuer → Recurso abierto. Para conteo, agrupación o análisis.


                                                        Permiten modelar capacidades compartidas, espacios físicos, almacenes, puntos de sincronización, etc. Cada tipo de recurso define una lógica propia de uso, pero todos pueden conectarse con procedimientos a través de eventos (hooks) como ON_SEIZE, ON_LEAVE, etc.

                                                        Histogramas automáticos:

                                                        Todos los recursos permiten generar histogramas automáticos sobre su uso definiendo su tabulación:

                                                        R_BIN_*: Tabula la ocupación del [R] Recurso.

                                                        R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:40

                                                        E_BIN_*: Tabula los tiempos de ocupación de la [E] Entidad.

                                                        E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40

                                                        SNA asociados:

                                                        Mediante R$(nombreRecurso,propiedad) puedes consultar en tiempo real:

                                                        • X, Y → Posición gráfica
                                                        • LEFT, IN, QUEUE, ENTRIES, CAPACITY → Estado operativo
                                                        • LOCK → 0 o 1 según si la entrada está bloqueada
                                                        • OCCUPIED → (solo en STORAGE) cantidad actual ocupada
                                                        R$(Fac1,LEFT)   ; Espacio disponible     
                                                        R$(Fac1,QUEUE)  ; Tamaño de de cola
                                                        R$(Fac1,LOCK)   ; 0|1 Bloqueada la entrada

                                                        HOOKs

                                                        Dependiendo del recurso en particular, pueden disponer de determinados Hooks atendidos por procedures ejecutados por entidades virtuales (VE).
                                                        Se crearán entidades virtuales (VE) que recorrerán el trigger y nacerán con el assign ENTITYNUMBER.

                                                        Facility {NAME:Fac2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt}
                                                        ;------------------------
                                                        PROCEDURE Fac1_attempt
                                                            MOVE {NAME:Text1,Text:"La entidad P$ENTITYNUMBER ha intentado entrar"}
                                                            TERMINATE_VE
                                                        ENDPROCEDURE

                                                         


                                                        Example:
                                                        POSITION {NAME:Salida,X:615,Y:388}
                                                        
                                                        initial posY,500
                                                        Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                        Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                        Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                        Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                        Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}
                                                        Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}
                                                        Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}
                                                        Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}
                                                        
                                                        Facility {NAME:Facility1,X:324,Y:560
                                                        	,capacity:3
                                                        	,ON_ATTEMPT:FACILITY1_attempt
                                                        	,ON_SEIZE:FACILITY1_seize
                                                        	,ON_RELEASE:FACILITY1_release
                                                        	,ON_QUEUE:FACILITY1_queue
                                                            }
                                                        
                                                        Storage {NAME:Storage1,X:324,Y:235
                                                        	,capacity:30
                                                        	,ON_ATTEMPT:STORAGE1_attempt
                                                        	,ON_ENTER:STORAGE1_enter
                                                        	,ON_LEAVE:STORAGE1_leave
                                                        	,ON_QUEUE:STORAGE1_queue
                                                            }
                                                        
                                                        
                                                        
                                                        START 1000
                                                        
                                                        ;-------------------------------
                                                        
                                                        GENERATE 10,0 {NAME:GEN1,X:62,Y:396}
                                                        
                                                        if (D$N%2==1)
                                                        
                                                            advance 10 {to:Facility1}
                                                            seize Facility1
                                                            advance 55,10
                                                            release Facility1
                                                        else
                                                            advance 10 {to:Storage1}
                                                            enter Storage1,random * 25 + 1
                                                            advance 35,10
                                                            leave Storage1
                                                        endif
                                                        
                                                        
                                                        advance 10 {to:Salida}
                                                        
                                                        ENDGENERATE 1
                                                        ;-------------------------------
                                                        
                                                        PROCEDURE FACILITY1_release
                                                        	move {name:Text1,text:"AC1$ Soy la VE [D$N] de ON_RELEASE la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE FACILITY1_queue
                                                        	move {name:Text2,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE FACILITY1_seize
                                                        	move {name:Text3,text:"AC1$ Soy la VE [D$N] de ON_SEIZE la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE FACILITY1_attempt
                                                        	move {name:Text4,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        ;-------------------------------
                                                        
                                                        PROCEDURE STORAGE1_leave
                                                        	move {name:Text5,text:"AC1$ Soy la VE [D$N] de ON_LEAVE la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE STORAGE1_queue
                                                        	move {name:Text6,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE STORAGE1_enter
                                                        	move {name:Text7,text:"AC1$ Soy la VE [D$N] de ON_ENTER la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        PROCEDURE STORAGE1_attempt
                                                        	move {name:Text8,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                            TERMINATE_VE
                                                        endprocedure 
                                                        
                                                        
                                                        
                                                        • + Facility

                                                          FACILITY

                                                          Modela un recurso exclusivo (como una máquina, un operador o una cabina).
                                                          Permite múltiples entidades según su capacity y cola si está ocupado.
                                                          Internamente gestiona dos listas: Entidades ocupantes y de entidades en espera.

                                                          Soporta varios métodos de selección de entidad entrante a través del parámetro METHOD:

                                                          • "FIFO"    Primero en entrar, primero en salir. Devuelve el primero de la lista (orden de llegada).
                                                          • "LIFO"    Último en entrar, primero en salir. Recorre hasta el último nodo en la lista.
                                                          • default    Equivale a "FIFO" si no se especifica método.

                                                           

                                                          Bloques asociados:

                                                          • SEIZE nombreRecurso ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.
                                                          • RELEASE nombreRecurso; Libera el recurso y permite el acceso de otra entidad.

                                                          Eventos disponibles:ON_ATTEMPT, ON_QUEUE, ON_SEIZE, ON_RELEASE

                                                          Facility {NAME:Fac1, CAPACITY:3, X:100, Y:100}
                                                          Facility {NAME:Fac2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }
                                                          ;------------------------
                                                          SEIZE Fac1
                                                          ADVANCE 10
                                                          RELEASE Fac1

                                                           


                                                          Example:
                                                          /*
                                                          
                                                          Recursos. Facility
                                                          
                                                          
                                                          */
                                                          
                                                          POSITION {NAME:Salida,X:615,Y:388}
                                                          
                                                          initial posY,500
                                                          Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                          Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                          Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                          Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                          Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}
                                                          Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}
                                                          Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}
                                                          Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}
                                                          
                                                          Facility {NAME:Facility1,X:324,Y:160
                                                          	,capacity:3
                                                          	,ON_ATTEMPT:FACILITY1_ATTEMPT
                                                          	,ON_SEIZE:FACILITY1_SEIZE
                                                          	,ON_RELEASE:FACILITY1_RELEASE
                                                          	,ON_QUEUE:FACILITY1_QUEUE
                                                              ,R_BIN_SIZE:1,R_BIN_COUNT:10
                                                              ,E_BIN_SIZE:1,E_BIN_COUNT:10
                                                              }
                                                          
                                                          
                                                          START 1000
                                                          
                                                          ;-------------------------------
                                                          
                                                          GENERATE 10,0 {NAME:GEN1,X:62,Y:396}
                                                          
                                                          advance 10 {to:Facility1}
                                                          seize Facility1
                                                          advance 55,10
                                                          release Facility1
                                                          advance 10 {to:Salida}
                                                          
                                                          ENDGENERATE 1
                                                          ;-------------------------------
                                                          
                                                          PROCEDURE FACILITY1_RELEASE
                                                          	move {name:Text1,text:"AC1$ Soy la VE [D$N] de ON_RELEASE la entidad P$ENTITYNUMBER"}
                                                              TERMINATE_VE
                                                          endprocedure 
                                                          PROCEDURE FACILITY1_QUEUE
                                                          	move {name:Text2,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                              TERMINATE_VE
                                                          endprocedure 
                                                          PROCEDURE FACILITY1_SEIZE
                                                          	move {name:Text3,text:"AC1$ Soy la VE [D$N] de ON_SEIZE la entidad P$ENTITYNUMBER"}
                                                              TERMINATE_VE
                                                          endprocedure 
                                                          PROCEDURE FACILITY1_ATTEMPT
                                                          	move {name:Text4,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                              TERMINATE_VE
                                                          endprocedure 
                                                          
                                                          
                                                          
                                                          
                                                          • + Storage

                                                            STORAGE

                                                            Modela un recurso exclusivo tipo almacén en el que la ocupación viene definica por cantidades a aportar por cada entidad.
                                                            Permite múltiples capacidades según su capacity y cola si no hay espacio disponible.
                                                            Internamente gestiona dos listas: Entidades ocupantes con su carga y de entidades en espera.

                                                            Soporta varias lógicas de selección de entrada a través del parámetro opcional METHOD:

                                                            Bloques asociados:

                                                            • ENTER nombreRecurso,cantidad ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.
                                                            • LEAVE nombreRecurso; Libera el recurso en la cantidad aportada y permite el acceso de otra entidad.

                                                            Eventos disponibles:ON_ATTEMPT, ON_QUEUE, ON_ENTER, ON_LEAVE

                                                            Soporta varios métodos de selección de entidad entrante a través del parámetro METHOD:

                                                            • "FIFO"    Primero en entrar, primero en salir. Devuelve el primero de la lista (orden de llegada).
                                                            • "LIFO"    Último en entrar, primero en salir. Recorre hasta el último nodo en la lista.
                                                            • "MIN_SPACE"    Elige a la entidad que necesita menos espacio (y cabe en lo disponible).
                                                            • "MAX_SPACE"    Elige a la entidad que necesita más espacio (dentro del espacio disponible).
                                                            • default    Equivale a "FIFO" si no se especifica método.


                                                            Estadísticas adicionales: EQ_BIN_* RQ_BIN_* Por capacidad utilizada.

                                                            Storage {NAME:Sto1, CAPACITY:3, X:100, Y:100,METHOD:MIN_SPACE}
                                                            Storage {NAME:Sto2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }
                                                            ;------------------------
                                                            Enter Sto2,6
                                                            ADVANCE 10
                                                            RELEASE Sto2

                                                             


                                                            Example:
                                                            /*
                                                            
                                                            Recursos. Storage
                                                            
                                                            
                                                            */
                                                            
                                                            POSITION {NAME:Salida,X:615,Y:388}
                                                            
                                                            initial posY,500
                                                            Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                            Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                            Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                            Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                            Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}
                                                            Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}
                                                            Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}
                                                            Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}
                                                            
                                                            Storage {NAME:Storage1,X:324,Y:235
                                                            	,capacity:30
                                                            	,ON_ATTEMPT:STORAGE1_attempt
                                                            	,ON_ENTER:STORAGE1_enter
                                                            	,ON_LEAVE:STORAGE1_leave
                                                            	,ON_QUEUE:STORAGE1_queue
                                                                ,E_BIN_START:34,E_BIN_SIZE:1,E_BIN_COUNT:12
                                                                ,R_BIN_SIZE:1,R_BIN_COUNT:10
                                                                ,EQ_BIN_START:34,EQ_BIN_SIZE:1,EQ_BIN_COUNT:12
                                                                ,RQ_BIN_START:0,RQ_BIN_SIZE:1,RQ_BIN_COUNT:32
                                                                }
                                                            
                                                            
                                                            START 1000
                                                            
                                                            ;-------------------------------
                                                            
                                                            GENERATE 10,0 {NAME:GEN1,X:62,Y:396}
                                                            
                                                            
                                                            advance 10 {to:Storage1}
                                                            enter Storage1,random * 25 + 1
                                                            advance 35,10
                                                            leave Storage1
                                                            
                                                            
                                                            
                                                            advance 10 {to:Salida}
                                                            
                                                            ENDGENERATE 1
                                                            ;-------------------------------
                                                            
                                                            PROCEDURE STORAGE1_leave
                                                            	move {name:Text5,text:"AC1$ Soy la VE [D$N] de ON_LEAVE la entidad P$ENTITYNUMBER"}
                                                                TERMINATE_VE
                                                            endprocedure 
                                                            PROCEDURE STORAGE1_queue
                                                            	move {name:Text6,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                                TERMINATE_VE
                                                            endprocedure 
                                                            PROCEDURE STORAGE1_enter
                                                            	move {name:Text7,text:"AC1$ Soy la VE [D$N] de ON_ENTER la entidad P$ENTITYNUMBER"}
                                                                TERMINATE_VE
                                                            endprocedure 
                                                            PROCEDURE STORAGE1_attempt
                                                            	move {name:Text8,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                                TERMINATE_VE
                                                            endprocedure 
                                                            
                                                            
                                                            
                                                            
                                                            
                                                            • + Restroom

                                                              RESTROOM

                                                              Modela un recurso retenedor.
                                                              Las entidades entran y quedan retenidas automáticamente en espera de que otra entidad la libre.
                                                              Internamente gestiona una lista: Entidades ocupantes.
                                                              Este recurso es especialmente útil para dormir agentes o mantener entidades en espera pasiva (no activa).

                                                              Bloques asociados:

                                                              • REST nombreRecurso ; La entidad queda retenida
                                                              • WAKE nombreRecurso[,cantidad de entidades a liberar][,número de entidad a liberar]; Libera las entidades determinadas si se especifica (0 ó -1 si es por numero de entidad) o libera solo el número de entidad si se especifica

                                                              Eventos disponibles: ON_REST, ON_WAKE

                                                              Restroom {NAME:Agentes, X:100, Y:100, ON_REST:Restroom1_onrest, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }
                                                              ;------------------------
                                                              REST Agentes ; el agente para a inactivo
                                                              ;------------------------
                                                              WAKE Agentes ; Despiesta todos los agentes
                                                              WAKE Agentes,0,X$nAgente ; Despierta un agente en concreto

                                                               


                                                              Example:
                                                              /*
                                                              
                                                              Recursos. Restroom
                                                              
                                                              
                                                              */
                                                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                              ;SYSTEM {TYPE:OPTIONS,Speed:8}
                                                              
                                                              Restroom {NAME:Restroom_agentes,X:100,Y:100}
                                                              
                                                              
                                                              POSITION {NAME:POS1,X:237,Y:449}
                                                              POSITION {NAME:POS2,X:269,Y:334}
                                                              POSITION {NAME:POS3,X:272,Y:202,type:terminate,title:end}
                                                              
                                                              Graphic {NAME:textAgente,Type:TEXT,X:430,Y:471,Text:"Agente"}
                                                              Graphic {NAME:Text2,Type:TEXT,X:431,Y:523,Text:"Entidad"}
                                                              
                                                              initial conta,0
                                                              initial nAgente,0
                                                              START 500
                                                              ;*****************************************************
                                                              PROCEDURE PRE_RUN
                                                              	timeout agente.main,1
                                                              	TERMINATE_VE 
                                                              ENDPROCEDURE
                                                              
                                                              ;*****************************************************
                                                              GENERATE 10,0,0,0 {NAME:GEN1,X:43,Y:300}
                                                              ADVANCE 20,0 {TO:POS1}
                                                              if (D$N%4==2)
                                                                  signalnow agente.Suma,X$nAgente
                                                                  signalnow agente.Resta,X$nAgente
                                                                  savevalue resultado,P$(agenteDato,X$nAgente)
                                                                  move {name:Text2,text:"Soy Entidad D$N : resultado X$resultado"}
                                                              endif
                                                              ADVANCE 20,0 {TO:POS2}
                                                              ADVANCE 20,0 {TO:POS3}
                                                              ENDGENERATE 1
                                                              
                                                              ;*******************************
                                                              procedure agente.main
                                                              	savevalue nAgente,D$N
                                                                  assign agenteDato,10
                                                                  while (1==1)
                                                                  move {name:textAgente,text:"Soy Agente X$nAgente [AC1: AC1$]"}
                                                                  REST Restroom_agentes
                                                                  endwhile
                                                              endprocedure
                                                              ;**********************************
                                                              procedure agente.Suma
                                                                  ASSIGN agenteDato,P$agenteDato + 2
                                                                  return_restore
                                                              endprocedure
                                                              ;**********************************
                                                              
                                                              procedure agente.Resta
                                                                  ASSIGN agenteDato,P$agenteDato - 1
                                                                  return_restore
                                                              endprocedure
                                                              
                                                              
                                                              
                                                              
                                                              
                                                              
                                                              
                                                              
                                                              
                                                              • + Conditions

                                                                Conditions

                                                                Modela un recurso retenedor condicional.
                                                                Una entidad puede acceder directamente o quedar retenida según se cumplan ciertas condiciones lógicas, tanto generales como particulares.

                                                                Lógica de retención:

                                                                El recurso evalúa tres tipos de condiciones:

                                                                • EXPRESIÓN GENERAL (EXPRESSION)
                                                                  Definida en el propio recurso (afecta a todas las entidades).

                                                                • EXPRESIÓN PARTICULAR
                                                                  Especificada en el bloque WAITUNTIL para una entidad concreta.

                                                                • EXPRESIÓN ABSOLUTA
                                                                  Indicada en WAITCHECK, y sobreescribe las demás. Si se cumple, libera las entidades.

                                                                Para que una entidad continúe, deben cumplirse ambas expresiones (GENERAL y PARTICULAR), salvo que se utilice una EXPRESIÓN ABSOLUTA, que actúa como única condición liberadora.

                                                                La liberación o paso de la entidad se produce a través del chequeo. Este chequeo se produce siempre que una entidad es liberada o cuando se solicita expresamente.

                                                                Las expresiones siempre se indican entre paréntesis.

                                                                Internamente gestiona una lista: Entidades ocupantes.
                                                                Este recurso es especialmente útil como multi-semáforo general o sala de espera para varios recursos en conjunto.
                                                                El chequeo puede ser cada tick o tiempo determinado a través de un TIMER.
                                                                Lo habitual es establecer los chequeos en los HOOKs de los recursos implicados.

                                                                Bloques asociados:

                                                                • WAITUNTIL nombreRecurso[,(expresionParticular)] ; La entidad queda retenida si no cumple la expresión ABSOLUTA o la PARTICULAR (si están definidas)
                                                                • WAITCHECK nombreRecurso[,(expresionAbsoluta)]; Chequea todas las entidades retenidas. Si se indica la expresíon ABSOLUTA, ésta será el único criterio liberador.

                                                                Eventos disponibles: ON_ATTEPMT, ON_CHECK, ON_QUEUE

                                                                Conditions {NAME:semaforo, EXPRESSION:(D$N %2 == 1) X:100, Y:100, ON_CHECK:semaforo_check}
                                                                ;------------------------
                                                                WAITUNTIL semaforo; La entidad continúa si es impar; (La entidad queda retenida si su número es par.)
                                                                WAITUNTIL semaforo,(D$N %3 == 0); La entidad continúa si es impar y múltiplo de 3. (3,9,15...)
                                                                ;------------------------
                                                                WAITCHECK semaforo; Chequea todas las entidades retenidas
                                                                WAITCHECK semaforo,(1==1) ; Libera todas las entidades retenidas
                                                                WAITCHECK semaforo,(D$N %2 == 0) ; Libera todas las entidades pares

                                                                Example:
                                                                /*
                                                                
                                                                Recursos. Conditions
                                                                
                                                                
                                                                */
                                                                SYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 350}
                                                                SYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER2, INTERVAL: 650}
                                                                
                                                                POSITION {NAME:Salida,X:615,Y:388}
                                                                
                                                                initial posY,500
                                                                Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                                Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                                Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                                Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                                Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                                                Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}
                                                                Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}
                                                                Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}
                                                                
                                                                Conditions {NAME:Conditions1,X:324,Y:160
                                                                	,expression:(D$N % 3 == 0)
                                                                	,ON_ATTEMPT:Conditions1_ATTEMPT
                                                                	,ON_CHECK:Conditions1_CHECK
                                                                	,ON_QUEUE:Conditions1_QUEUE
                                                                    }
                                                                
                                                                
                                                                START 100
                                                                
                                                                ;-------------------------------
                                                                
                                                                GENERATE 20,0 {NAME:GEN1,X:62,Y:396}
                                                                
                                                                advance 10 {to:Conditions1}
                                                                
                                                                waituntil Conditions1,(D$N!=3)
                                                                
                                                                move {name:Text1,text:"AC1$ Soy la Entidad [D$N] y avanzo"}
                                                                advance 100,50 {to:Salida}
                                                                
                                                                endgenerate 1
                                                                ;-------------------------------
                                                                PROCEDURE TIMER1
                                                                	waitcheck Conditions1,(D$N%2==1)
                                                                	move {name:Text6,text:"AC1$ Soy TIMER [D$N] WAITCHECK las impares"}
                                                                	TERMINATE_VE 
                                                                ENDPROCEDURE 
                                                                
                                                                PROCEDURE TIMER2
                                                                	waitcheck Conditions1,(1==1)
                                                                	move {name:Text7,text:"AC1$ Soy TIMER [D$N] WAITCHECK TODOS"}
                                                                	TERMINATE_VE 
                                                                ENDPROCEDURE 
                                                                
                                                                PROCEDURE Conditions1_QUEUE
                                                                	move {name:Text3,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                                    TERMINATE_VE
                                                                endprocedure 
                                                                PROCEDURE Conditions1_CHECK
                                                                	move {name:Text4,text:"AC1$ Soy la VE [D$N] de ON_CHECK la entidad P$ENTITYNUMBER"}
                                                                    TERMINATE_VE
                                                                endprocedure 
                                                                PROCEDURE Conditions1_ATTEMPT
                                                                	move {name:Text5,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                                    TERMINATE_VE
                                                                endprocedure 
                                                                ;-------------------------------
                                                                
                                                                • + Stock

                                                                  STOCK

                                                                  STOCK modela un almacén inteligente de productos etiquetados, que permite entradas y salidas de items con tipo y cantidad. Soporta múltiples entradas simultáneas, comprobaciones de disponibilidad y eventos (hooks) conectables a lógica externa.

                                                                  Es ideal para modelar inventarios, mercados, almacenes de componentes, etc.


                                                                  Estructura y comportamiento
                                                                  El recurso almacena productos en forma de objetos {clave: cantidad}, organizados internamente por tipo (clave).
                                                                  Permite múltiples operaciones simultáneas de carga y descarga, acumulando cantidades por producto.

                                                                  Bloques asociados:

                                                                  • STOCKIN nombreRecurso,nombreVariable ; Añade productos al Stock. La variable debe ser un objeto con forma {clave1: cantidad1 , clave2:cantidad2}.
                                                                  • STOCKOUT nombreRecurso,nombreVariable ; Extrae productos del STOCK, si hay disponibilidad suficiente.
                                                                    La variable debe ser un objeto con forma {clave1: cantidad1 , clave2:cantidad2}.

                                                                  SNA asociados:
                                                                  SC$(recurso, objeto) Retorna 0 ó 1 si el recurso tiene suficientes cantidades del objeto para poder realizar STOCKOUT.

                                                                  R$(nombre,STOCK,clave)  Devuelve el stock actual de un tipo concreto.

                                                                  VS$(nombre) → Devuelve el stock completo como objeto {clave1: qty1 , clave2: qty2, ...}.


                                                                  Eventos disponibles:ON_ATTEMPT, ON_STOCKIN, ON_STOCKOUT, ON_QUEUE

                                                                  STOCK {NAME:MARKET1,X:331,Y:210
                                                                       ,ON_QUEUE:MARKET1_QUEUE
                                                                       ,ON_STOCKIN:MARKET1_STOCKIN
                                                                       ,ON_STOCKOUT:MARKET1_STOCKOUT
                                                                       ,ON_ATTEMPT:MARKET1_ATTEMPT
                                                                       ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20
                                                                       ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100
                                                                       }
                                                                  ;------------------------
                                                                  assign mesa,round(random * 30)
                                                                  assign silla,round(random * 100)
                                                                  assign armario,round(random * 5)
                                                                  assign lampara,round(random * 5)
                                                                  assign camion,{mesa:P$mesa,silla:P$silla,armario:P$armario}
                                                                  assign.merge camion,{lampara:P$lampara}
                                                                  STOCKIN MARKET1, CAMION
                                                                  ;------------------------
                                                                  
                                                                  

                                                                   


                                                                  Example:
                                                                  /*
                                                                  
                                                                  Recursos. Stock
                                                                  
                                                                  
                                                                  */
                                                                  SYSTEM {TYPE:ON_TIMER, TRIGGER:MARKET1_TIMER, INTERVAL: 20}
                                                                  
                                                                  
                                                                  STOCK {NAME:MARKET1,X:331,Y:210
                                                                  	 ,ON_QUEUE:MARKET1_QUEUE
                                                                   	 ,ON_STOCKIN:MARKET1_STOCKIN
                                                                       ,ON_STOCKOUT:MARKET1_STOCKOUT
                                                                       ,ON_ATTEMPT:MARKET1_ATTEMPT
                                                                       ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20
                                                                       ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100
                                                                       }
                                                                  
                                                                  POSITION {NAME:POS1,X:70,Y:113}
                                                                  POSITION {NAME:POS2,X:595,Y:283}
                                                                  
                                                                  initial posY,500
                                                                  Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                                  Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                                  Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                                  Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                                  Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}
                                                                  Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}
                                                                  Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}
                                                                  Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}
                                                                  
                                                                  START 300 
                                                                  ;*****************************************************************
                                                                  ; PROVEEDORES: Generan productos para añadir al STOCK
                                                                  GENERATE 115,0 {NAME:PROVEEDORES,X:619,Y:194,ECOLOR:#006666,ERADIO:15}
                                                                  
                                                                  
                                                                  
                                                                  ADVANCE 20,0 {TO:MARKET1}
                                                                  
                                                                  assign mesa,ROUND(RANDOM * 30)
                                                                  assign silla,round(random * 100)
                                                                  assign armario,round(random * 5)
                                                                  assign lampara,round(random * 5)
                                                                  
                                                                  
                                                                  assign camion,{mesa:P$mesa,silla:P$silla,armario:P$armario}
                                                                  assign.merge camion,{lampara:P$lampara}
                                                                  STOCKIN MARKET1, V$camion
                                                                  
                                                                  MOD {RADIO:5}
                                                                  ADVANCE 20,0 {TO:POS2}
                                                                  TERMINATE 1
                                                                  
                                                                  ;*****************************************************************
                                                                  ; CLIENTES: Consumen productos del STOCK
                                                                  GENERATE 20,2 {NAME:CLIENTES,X:81,Y:207,ECOLOR:#666666}
                                                                  
                                                                  assign carrito,{}
                                                                  assign.merge carrito,{mesa:2}
                                                                  assign.merge carrito,{silla:4}
                                                                  
                                                                  if (SC$(MARKET1,carrito)==0)
                                                                  move {name:Text1,text:"SIN STOCK"}
                                                                  else
                                                                  move {name:Text1,text:"STOCK SUFICIENTE"}
                                                                  endif
                                                                  
                                                                  ADVANCE 10,0 {TO:MARKET1}
                                                                  
                                                                  STOCKOUT MARKET1, V$carrito
                                                                  MOD {RADIO:8}
                                                                  ADVANCE 50,50 {TO:POS1}
                                                                  TERMINATE 1
                                                                  
                                                                  ;-------------------------------
                                                                  
                                                                  PROCEDURE MARKET1_STOCKOUT
                                                                  	; MAPPER es objeto {mesa:N,silla:N}
                                                                      move {name:Text2,text:"ON_STOCKOUT Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)"}
                                                                      TERMINATE_VE
                                                                  endprocedure 
                                                                  
                                                                  PROCEDURE MARKET1_STOCKIN
                                                                  	; MAPPER es objeto {mesa:N,silla:N}
                                                                      move {name:Text3,text:"ON_STOCKIN Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)"}
                                                                      TERMINATE_VE
                                                                  endprocedure 
                                                                  
                                                                  PROCEDURE MARKET1_ATTEMPT
                                                                  	; MAPPER es objeto {mesa:N,silla:N}
                                                                      move {name:Text4,text:"ON_ATTEMPT Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)"}
                                                                      TERMINATE_VE
                                                                  endprocedure 
                                                                  
                                                                  PROCEDURE MARKET1_QUEUE
                                                                  	; MAPPER es objeto {mesa:N,silla:N}
                                                                      move {name:Text5,text:"ON_QUEUE Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)"}
                                                                      TERMINATE_VE
                                                                  endprocedure 
                                                                  ;-------------------------------
                                                                  PROCEDURE MARKET1_TIMER
                                                                  	assign tmp,VS$(MARKET1)
                                                                  	assign txt,""
                                                                  	foreach clave,IN_OBJECT,V$(tmp) 
                                                                         assign txt,"P$(txt) | P$(clave) : P$(tmp.P$(clave))"
                                                                      endforeach
                                                                      move {name:Text6,text:"AC1$ ON_TIMER [P$txt]"}
                                                                      TERMINATE_VE
                                                                  endprocedure 
                                                                  
                                                                  
                                                                  
                                                                  
                                                                  
                                                                  
                                                                  
                                                                  
                                                                  • + Backpack

                                                                    Backpack no es un recurso como tal ya que en realidad no realiza funciones internas, es más bien una herramienta de transporte de entidades, pero sí es cierto que las retiene.

                                                                    Para poder llevar entidades de un punto a otro de la simulación, por ejemplo, una furgoneta con paquetes se necesita algo que indique al sistema que esas entidades ya no están disponibles en la cola de eventos ni en ninguna otra cola.

                                                                    Este concepto podríamos llamarlo mochila (Backpack) donde una entidad podrá transportar entidades. Existen dos limitaciones:

                                                                    Solo se puede cargar desde un RESTROOM ya que es un recurso retenedor en la que las entidades no salen por sí mismas y sólo se pueden descargar en un GENERATE ya que es el único punto seguro con respecto a la pila de direcciones de la entidad (véase SCAPE).

                                                                    Tiene dos bloques asociados:

                                                                    • LOAD nombreMochila,nombreRestroom[,númeroEntidad]
                                                                    • UNLOAD nombreMochila,nombreGenerate[,númeroEntidad]

                                                                    El primero carga en la mochila todo el contenido de un Restroom o la entidad con ese número y el segundo es su paso inverso en el GENERATE.

                                                                    Las entidades en la mochila pueden obtenerse a través del propio ASSIGN generado con el mismo nombre de la mochila. Es un array con los números de las entidades.

                                                                    El ejemplo es el recorrido de una furgoneta que pasa por dos puntos de recogida y lleva los paquetes al punto de distribución.


                                                                    Example:
                                                                    RESTROOM {NAME:almacenReceptor1,X:220,Y:348,E_BIN_SIZE:1,E_BIN_COUNT:10}
                                                                    RESTROOM {NAME:almacenReceptor2,X:217,Y:182,E_BIN_SIZE:1,E_BIN_COUNT:10}
                                                                    RESTROOM {NAME:almacenDistribucion,X:662,Y:93}
                                                                    
                                                                    Graphic {NAME:Text1,Type:TEXT,X:100,Y:100,Text:"Terminado: NO",font:"20",color:red}
                                                                    
                                                                    
                                                                    START 1000
                                                                    ;************************************************************************
                                                                    
                                                                    GENERATE 8,3 {NAME:Recepcion1,X:66,Y:356}
                                                                    ADVANCE 20,0 {TO:almacenReceptor1}
                                                                    rest almacenReceptor1
                                                                    terminate 1
                                                                    
                                                                    ;************************************************************************
                                                                    
                                                                    GENERATE 5,3 {NAME:Recepcion2,X:65,Y:181}
                                                                    ADVANCE 20,0 {TO:almacenReceptor2}
                                                                    rest almacenReceptor2
                                                                    terminate 1
                                                                    
                                                                    ;************************************************************************
                                                                    
                                                                    GENERATE 40,0,0,1 {NAME:Furgonetas,X:40,Y:567
                                                                    		,eradio:16,ecolor:red,visible:0
                                                                    		,esubtitle:"P$(mochila1.LENGTH)"}
                                                                    ADVANCE0 {TO:PuntoDistribucion}
                                                                    while (1==1)
                                                                        ADVANCE 20,30 {TO:almacenReceptor1}
                                                                        load mochila1,almacenReceptor1
                                                                        ADVANCE 20,30 {TO:almacenReceptor2}
                                                                        load mochila1,almacenReceptor2
                                                                    
                                                                    	ADVANCE 20,30 {TO:PuntoDistribucion}
                                                                        unload mochila1,PuntoDistribucion
                                                                    endwhile
                                                                    terminate 1
                                                                    
                                                                    ;************************************************************************
                                                                    
                                                                    GENERATE 0,0,0,0 {NAME:PuntoDistribucion,X:669,Y:434}
                                                                    ADVANCE0 {TO:PuntoDistribucion}
                                                                    
                                                                    ADVANCE 20,50 {TO:almacenDistribucion}
                                                                    if (D$N>1000)
                                                                    move {name:Text1,text:"TERMINADO: SÍ",color:green}
                                                                    stop
                                                                    endif
                                                                    rest almacenDistribucion
                                                                    
                                                                    terminate 1
                                                                    
                                                                    
                                                                    
                                                                    
                                                                  • + Recursos lógicos
                                                                    • + Table

                                                                      Example:
                                                                      FACILITY {NAME:VENTANILLA1,X:380,Y:348,capacity:4}
                                                                      POSITION {NAME:POS1,X:218,Y:437}
                                                                      POSITION {NAME:POS2,X:591,Y:429}
                                                                      POSITION {NAME:POS3,X:713,Y:329}
                                                                      
                                                                      Graphic {NAME:Line1,Type:L,color:#FF0000, X1:218,Y1:500,X2:592,Y2:500}
                                                                      Graphic {NAME:Text1,Type:T,X:410,Y:527,Text:"Estadistic section"}
                                                                      
                                                                      TABLE {name: TABLA1,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100,EXPRESSION:(M1$ - P$TIEMPOINICIO)}
                                                                      
                                                                      START 100 
                                                                      
                                                                      ;***************************************************************
                                                                      
                                                                      
                                                                      GENERATE 8,3 {NAME:GEN1,X:66,Y:350}
                                                                      
                                                                      ADVANCE 30,0 {TO:POS1}
                                                                      ASSIGN TIEMPOINICIO,M1$
                                                                      ADVANCE 20,0 {TO:VENTANILLA1}
                                                                      
                                                                      SEIZE VENTANILLA1
                                                                      ADVANCE 20,20
                                                                      RELEASE VENTANILLA1
                                                                      
                                                                      
                                                                      ADVANCE 20,0 {TO:POS2}
                                                                      TABULATE TABLA1
                                                                      ADVANCE 20,0 {TO:POS3}
                                                                      
                                                                      TERMINATE 1
                                                                      
                                                                      ;***************************************************************
                                                                      • + FSM - Máquina de Estados Finitos

                                                                        FSM - Máquina de Estados Finitos

                                                                        El bloque FSM permite modelar una Máquina de Estados Finitos. Se trata de un recurso que gestiona estados internos definidos mediante una tabla de transiciones, activadas por entradas (INPUT).

                                                                        La lógica de funcionamiento se define mediante un objeto JSON con los siguientes campos:

                                                                        • STATES: Lista de estados posibles (opcional si se usa EVAL: 1)
                                                                        • TRANSITIONS: Lista de transiciones entre estados
                                                                        • INITIAL: Estado inicial (*Sólo para LOCAL:0)
                                                                        • EVAL: (opcional) Si es 1, permite usar expresiones matemáticas en TO.

                                                                         Cada transición puede contener:

                                                                        • FROM: define el estado previo. Si se define como "" actúa como comodín (acepta cualquier estado).
                                                                        • INPUT: nombre del input. "" actúa como comodín.
                                                                        • TO:  nuevo estado (valor directo o expresión si EVAL:1).
                                                                        • TRIGGER:  (opcional) procedimiento que se dispara al hacer la transición.
                                                                        ; EJEMPLO BÁSICO
                                                                        initial logic, { STATES: ["open", "close"],
                                                                            TRANSITIONS: [
                                                                                {FROM: "open", INPUT: "close", TO: "close", TRIGGER: "puertaAbierta"},
                                                                                {FROM: "close", INPUT: "open", TO: "open"},
                                                                                {FROM: "close", INPUT: "switch", TO: "open"},
                                                                                {FROM: "open", INPUT: "switch", TO: "close"}
                                                                            ],
                                                                            INITIAL: "open"
                                                                            }
                                                                        
                                                                        FSM {NAME:FSM1,X:434,Y:540, LOGIC:V$(logic)}
                                                                        
                                                                        
                                                                        ; EJEMPLO CICLICO
                                                                        initial logic2, { states: ["uno", "dos", "tres"],
                                                                            TRANSITIONS: [
                                                                                {FROM: "uno", INPUT: "siguiente", TO: "dos"},
                                                                                {FROM: "dos", INPUT: "siguiente", TO: "tres"},
                                                                                {FROM: "tres", INPUT: "siguiente", TO: "uno", TRIGGER: "reinicio"}
                                                                            ],
                                                                            INITIAL: "open"
                                                                            }
                                                                        FSM {NAME:FSM2,X:134,Y:140, LOGIC:V$(logic2)}
                                                                        
                                                                        
                                                                        ; EJEMPLO LÓGICA EVALUADA
                                                                        ; En este modo (eval: 1), los estados y transiciones pueden evaluarse como expresiones matemáticas dinámicas.
                                                                        
                                                                        initial logicTemp3, {
                                                                          TRANSITIONS: [
                                                                            {FROM: "", INPUT: "sube", TO: "(X + 1 <= 24 ? X + 1.5 : X)"},
                                                                            {FROM: "", INPUT: "baja", TO: "(X - 1 >= 18 ? X - 1.5 : X)"},
                                                                            {FROM: "", INPUT: "reset", TO: "21"}
                                                                          ],
                                                                          EVAL: 1,
                                                                          INITIAL: 21
                                                                        }
                                                                        
                                                                        FSM {NAME:Stater3, X:434, Y:540, LOGIC:V$(logicTemp3)}
                                                                        
                                                                        
                                                                        ; EJEMPLO LÓGICA EVALUADA CON PARÁMETRO ADICIONAL Y
                                                                        ; SE PUEDEN AÑADIR PARÁMETROS Y, Z A AS FUNCIONES 
                                                                        
                                                                        initial logicTemp4, {
                                                                          TRANSITIONS: [
                                                                            {FROM: "", INPUT: "sube", TO: "(X + 1 <= 24 ? X + Y : X)"},
                                                                            {FROM: "", INPUT: "baja", TO: "(X - 1 >= 18 ? X - Y : X)"},
                                                                            {FROM: "", INPUT: "reset", TO: "21"}
                                                                          ],
                                                                          EVAL: 1,
                                                                          INITIAL: 21
                                                                        }
                                                                        
                                                                        FSM {NAME:Stater4, X:434, Y:540, LOGIC:V$(logicTemp4)}

                                                                        Bloques asociados:

                                                                        • FSM nombreRecurso,input[, paramY [,paramZ]] ; Ejecuta una transición de estado según la lógica definida.
                                                                        FSM Fsm1,"OPEN" ; pasa a estado "CLOSE"
                                                                        FSM Fsm1,"SWITCH" ; si estaba en "OPEN" pasa a "CLOSE" y viceversa.
                                                                        
                                                                        FSM Fsm2,"siguiente" ; si estaba en "DOS" pasa a "TRES",... 
                                                                        
                                                                        
                                                                        FSM Fsm4,"subir",10 ; incrementa el state en 10

                                                                        SNA asociado:

                                                                        FSM es el único recurso que ofrece una operación atómica de consulta y modificación del estado.

                                                                        • R$(nombreStater,STATE) Devuelve el estado actual.
                                                                        • R$(nombreStater,IN_AFTER,input) Devuelve el estado actual y ejecuta el input en OPERACIÓN ATÓMICA.
                                                                        • R$(nombreStater,IN_BEFORE,input) Ejecuta el input y devuelve el estado actual en OPERACIÓN ATÓMICA.
                                                                        • R$(nombreStater,IN_BEFORE,input,Y,Z) Ejecuta el input con parámetros Y,Z para FSM tipo EVAL:1 y devuelve el estado actual en OPERACIÓN ATÓMICA.

                                                                        Esto permite modelar zonas críticas, semáforos y sincronización entre procesos.

                                                                        Usos típicos

                                                                        • Control de zonas críticas
                                                                        • Semáforos
                                                                        • Exclusión mutua
                                                                        • Sincronización de procesos
                                                                        • Funciones matemáticas con estado y parámetros
                                                                        waituntil Conditions1,("R$(Fsm1,IN_AFTER,close)"=="open") ; la entidad pasa y retiene las posteriores
                                                                        ...
                                                                        FSM Fsm1,"open" ; finaliza la zona de exclusión mutua
                                                                        waitcheck Conditions1 ; da paso al siguiente

                                                                         Y como almacén de funciones matemáticas con ejecución de dos parámetros adicionales:

                                                                        { FROM: "", INPUT: "incr", TO: "(X + Y * Z)" } ; X: State actual, Y,Z Parámetros extra de input
                                                                        
                                                                        

                                                                        Eventos disponibles: TRIGGER

                                                                        Cada transición puede incluir un trigger, que llamará a un PROCEDURE:

                                                                        PROCEDURE puertaAbierta
                                                                            move {name:text7,text:"La entidad P$ENTITYNUMBER ha abierto la puerta"}
                                                                            TERMINATE_VE
                                                                        endprocedure

                                                                        Modos de almacenamiento:

                                                                        Su modo por defecto es global, un estado por STATER, pero si se define:

                                                                        LOCAL:1

                                                                        su modo es de un estado para cada entidad. Perfecto como discriminador de caminos que evita el uso de SWITCH anidados.

                                                                        Dado que no se assigna valor inicialmente, éste se asignará mediente el acceso directo al assign asociado:

                                                                        assign Fsm1,10

                                                                         


                                                                        Example:
                                                                        POSITION {NAME:Salida,X:648,Y:383}
                                                                        
                                                                        
                                                                        initial posY,500
                                                                        initial logic, { STATES: ["open", "close"],
                                                                            TRANSITIONS: [
                                                                                {FROM: "open", INPUT: "close", TO: "close", TRIGGER: "puertaAbierta"},
                                                                                {FROM: "close", INPUT: "open", TO: "open"},
                                                                                {FROM: "close", INPUT: "switch", TO: "open"},
                                                                                {FROM: "open", INPUT: "switch", TO: "close"}
                                                                            ],
                                                                            INITIAL: "open"
                                                                            }
                                                                        
                                                                        FSM {NAME:FSM1,X:434,Y:540, LOGIC:V$(logic)}
                                                                        
                                                                        
                                                                        
                                                                        
                                                                        Graphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}
                                                                        Graphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}
                                                                        Graphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}
                                                                        Graphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}
                                                                        Graphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}
                                                                        Graphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}
                                                                        Graphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}
                                                                        Graphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}
                                                                        
                                                                        Conditions {NAME:Conditions1,X:317,Y:539
                                                                        	,expresion:("R$(FSM1,IN_AFTER,close)"=="open")
                                                                        	,ON_ATTEMPT:Conditions1_ATTEMPT
                                                                        	,ON_CHECK:Conditions1_CHECK
                                                                        	,ON_QUEUE:Conditions1_QUEUE
                                                                            }
                                                                        
                                                                        
                                                                        START 100
                                                                        
                                                                        ;-------------------------------
                                                                        
                                                                        GENERATE 20,0 {NAME:GEN1,X:62,Y:396}
                                                                            advance 10 {to:Conditions1}
                                                                            waituntil Conditions1
                                                                            advance 15,25 {to:Salida}
                                                                            STATE FSM1,"open"
                                                                            waitcheck Conditions1
                                                                        terminate 1
                                                                        ;-------------------------------
                                                                        PROCEDURE Conditions1_QUEUE
                                                                        	move {name:Text3,text:"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER"}
                                                                            TERMINATE_VE
                                                                        endprocedure 1
                                                                        PROCEDURE Conditions1_CHECK
                                                                        	move {name:Text4,text:"AC1$ Soy la VE [D$N] de ON_CHECK la entidad P$ENTITYNUMBER"}
                                                                            TERMINATE_VE
                                                                        endprocedure 1
                                                                        PROCEDURE Conditions1_ATTEMPT
                                                                        	move {name:Text5,text:"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER"}
                                                                            TERMINATE_VE
                                                                        endprocedure 1
                                                                        ;-------------------------------
                                                                        PROCEDURE puertaAbierta
                                                                        	move {name:Text7,text:"La entidad P$ENTITYNUMBER ha abierto la puerta"}
                                                                            TERMINATE_VE
                                                                        endprocedure 1
                                                                        ;-------------------------------
                                                                        
                                                                        
                                                                        
                                                                        • + Queuer

                                                                          QUEUER

                                                                          Modela un recurso abierto.
                                                                          Permite múltiples entidades sin criterio.
                                                                          Internamente gestiona una lista de entidades en el recurso.

                                                                          Su uso principal es estadístico o como agrupador de entidades.

                                                                          Bloques asociados:

                                                                          • QUEUE nombreRecurso ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.
                                                                          • DEPART nombreRecurso; Libera el recurso y permite el acceso de otra entidad.

                                                                          Eventos disponibles:ON_QUEUE, ON_DEPART

                                                                          QUEUER {NAME:Qventanilla1,X:374,Y:424
                                                                              ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 
                                                                              ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20}
                                                                          ;------------------------
                                                                          queue Qventanilla1
                                                                          seize ventanilla1
                                                                          depart Qventanilla1

                                                                           


                                                                          Example:
                                                                          /*
                                                                          
                                                                          Recursos. Queuer
                                                                          
                                                                          */
                                                                          ;SYSTEM {TYPE:OPTIONS,Speed:50,Pause:0}
                                                                          POSITION {NAME:POS1,X:620,Y:360}
                                                                          
                                                                          QUEUER {NAME:Qventanilla1,X:374,Y:424
                                                                          	,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 
                                                                              ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20}
                                                                          
                                                                          Facility {NAME:ventanilla1,X:373,Y:365,capacity:5}
                                                                          START 200
                                                                          
                                                                          ;*****************************************************
                                                                          GENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}
                                                                          ADVANCE 10 {TO:ventanilla1}
                                                                          
                                                                          queue Qventanilla1
                                                                          seize ventanilla1
                                                                          depart Qventanilla1
                                                                          ADVANCE 20,10
                                                                          release ventanilla1
                                                                          
                                                                          ADVANCE 10 {TO:POS1}
                                                                          ENDGENERATE 1
                                                                          
                                                                          • + Bridger, Dynamic...

                                                                            Existen varios recursos más con sus propias secciones como Bridger y Dynamic.

                                                                             

                                                                          • + Configuración del sistema

                                                                            El comando SYSTEM permite configurar distintos aspectos del motor de simulación. Su sintaxis básica es:

                                                                            SYSTEM {TYPE:..., ...}
                                                                            
                                                                            

                                                                            Parámetros de simulación (velocidad, precisión...)
                                                                            Puedes controlar la velocidad de simulación, la precisión temporal y la precisión decimal del tiempo:

                                                                            SYSTEM {TYPE:OPTIONS, SPEED:3}
                                                                            SYSTEM {TYPE:OPTIONS, TIME_DECIMALS:2}
                                                                            SYSTEM {TYPE:OPTIONS, width:1400, height:1400}
                                                                            SYSTEM {TYPE:OPTIONS, SEED:1400}
                                                                            
                                                                            • SPEED: Velocidad inicial de reproducción (por defecto: 5).
                                                                            • TIME_DECIMALS: Permite que el tiempo AC1 tenga valores como 3.14 en lugar de valores enteros (por defecto: 0).
                                                                            • WIDTH: Ancho del canvas de simulación. Por defecto 800.
                                                                            • HEIGHT : Alto del canvas de simulación. Por defecto 600.
                                                                            • SEED: Genera la secuencia de números aleatorios por Xorshift128+.
                                                                            • TRACE_ROUTES: Genera las trazas para V&V


                                                                            2.- Mostrar información básica del sistema

                                                                            Puedes activar o desactivar la visualización de elementos clave del motor:

                                                                            SYSTEM {TYPE:OPTIONS, SHOW_BASICS:1}

                                                                            Esto hará que se muestren en la parte inferior:

                                                                            • TG1: Tiempo global inicial
                                                                            • AC1: Tiempo actual
                                                                            • SPEED: Velocidad de simulación

                                                                            3.- Temporizadores (TIMER)

                                                                            Los temporizadores permiten ejecutar procedimientos periódicamente, sin intervención de entidades:

                                                                            SYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL:0.1}
                                                                            

                                                                             

                                                                            • TRIGGER: nombre del procedimiento a ejecutar.
                                                                            • INTERVAL: intervalo de tiempo (en unidades de simulación) entre ejecuciones.

                                                                            Esto es útil para tareas de monitorización, logging, cargas periódicas, etc.

                                                                             


                                                                            Example:
                                                                            SYSTEM {TYPE:OPTIONS, SPEED:5, TIME_DECIMALS:1, SHOW_BASICS:0}
                                                                            SYSTEM {TYPE:ON_TIMER, TRIGGER:actualizarGraficos, INTERVAL:0.5}
                                                                            
                                                                            PROCEDURE actualizarGraficos
                                                                               move {name:text1:text: "Texto actualizado a AC1$"}
                                                                               TERMINATE_VE
                                                                            ENDPROCEDURE
                                                                            • + Seguimiento, informe y debugging

                                                                              La plataforma cuenta con herramientas visuales y técnicas que permiten el seguimiento detallado de la simulación, tanto a nivel de ejecución como de resultados.

                                                                              Panel de depuración

                                                                              Desde el tercer panel (canvas de reproducción, seguimiento y debug, editor de código), puedes acceder a:

                                                                              • Cola de eventos
                                                                                Muestra en tiempo real qué entidades están programadas, su posición temporal (TG1) y en qué paso del programa se encuentran.

                                                                              • Recursos
                                                                                Detalla las entidades ocupantes, aquellas en espera (cola), y la capacidad de cada recurso.

                                                                              • Entidades
                                                                                Visualiza el estado de cada entidad: variables, posición, paso actual, retardo, etc.

                                                                              • Ficheros
                                                                                Se muestran todos los archivos creados mediante el comando FILE y el bloque WRITE.
                                                                                Incluye también el archivo especial DEBUG, que se genera automáticamente si se activan instrucciones con la etiqueta {debug:1} o {debug:2}.

                                                                                • {debug:1} registra información general de la ejecución de la línea.

                                                                                • {debug:2} incluye valores de variables evaluadas y resultados detallados.

                                                                              if (P$variable == 1) {debug:1}
                                                                              assign P$variable, X$origen {debug:2}

                                                                               

                                                                              Botón de informe

                                                                              Mediante el botón Informe, se genera una visión estadística completa de la simulación:

                                                                              • Información general de la simulación, tiempo final (AC1), número de entidades procesadas...
                                                                              • Estado e historial de cada recurso y sus gráficas.
                                                                              • Información de los PLOTTER y su gráficas.
                                                                            • + Season 4: Control de recursos y colas
                                                                              • + Accediendo y midiendo un recurso

                                                                                Este ejemplo presenta el uso básico de un recurso tipo STORAGE, útil para modelar inventarios, almacenes o zonas de capacidad limitada. Las entidades consumen unidades aleatorias del recurso, representando una carga variable.

                                                                                El interés del ejemplo está en cómo obtener el contenido del recurso en tiempo real por dos métodos diferentes:

                                                                                • Suma de datos individuales: usando FOREACH, se recorre cada entidad que ocupa el recurso y se suman sus valores asociados (en este caso, el número de unidades que ha tomado).

                                                                                • Consulta directa: mediante R$(ALMACEN1,LEFT) se obtiene el número de unidades libres, por lo que el total ocupado es CAPACITY - LEFT.

                                                                                Ambos enfoques permiten contrastar y validar la información, y se pueden aplicar para cálculos estadísticos, control de inventarios o gestión de alertas de saturación.

                                                                                El TIMER periódicamente actualiza la información mostrada en pantalla, ofreciendo una visión continua del estado del recurso.


                                                                                Example:
                                                                                /*
                                                                                
                                                                                 Accediendo y midiendo un recurso
                                                                                
                                                                                */
                                                                                STORAGE {NAME: ALMACEN1, CAPACITY:40, X:316,Y:401}
                                                                                
                                                                                SYSTEM {TYPE:ON_TIMER,INTERVAL:50,TRIGGER:PROCTIMER}
                                                                                
                                                                                GRAPHIC {NAME:BANNER1,TYPE:TEXT,X:184,Y:343,TEXT:ALEATORIO.}
                                                                                GRAPHIC {NAME:BANNER2,TYPE:TEXT,X:305,Y:283,TEXT:SUMA}
                                                                                GRAPHIC {NAME:BANNER3,TYPE:TEXT,X:306,Y:241,TEXT:DIRECT}
                                                                                
                                                                                POSITION {NAME:POS1,X:176,Y:296}
                                                                                POSITION {NAME:POS2,X:452,Y:300}
                                                                                POSITION {NAME:POS3,X:559,Y:299}
                                                                                
                                                                                START 100
                                                                                
                                                                                ;******************************************************
                                                                                
                                                                                GENERATE 30,10 {NAME:GEN1,X:61,Y:297}
                                                                                
                                                                                    ASSIGN ALEATORIO,FLOOR(RANDOM * 9 + 1) {debug:1}
                                                                                
                                                                                    MOVE {NAME:BANNER1,TEXT:"ALEATORIO: P$ALEATORIO"}
                                                                                    ADVANCE 20 {TO:POS1}
                                                                                    ADVANCE 20 {TO:ALMACEN1}
                                                                                    ENTER ALMACEN1  ,P$ALEATORIO
                                                                                    ADVANCE 110,90
                                                                                    LEAVE ALMACEN1 
                                                                                    ADVANCE 20 {TO:POS2}
                                                                                    ADVANCE 20,0 {TO:POS3}
                                                                                ENDGENERATE 1
                                                                                
                                                                                ;*****************************************************
                                                                                PROCEDURE PROCTIMER
                                                                                
                                                                                    ASSIGN SUMA,0
                                                                                
                                                                                    FOREACH NUMERO,IN_RESOURCE,ALMACEN1
                                                                                      ASSIGN SUMA,(P$(ALEATORIO,P$NUMERO) + P$SUMA)
                                                                                    ENDFOREACH
                                                                                
                                                                                    MOVE {NAME:BANNER2,TEXT:"SUMA ALEATORIOS: P$SUMA"}
                                                                                    ASSIGN DIRECT,40-R$(ALMACEN1,LEFT)
                                                                                    MOVE {NAME:BANNER3,TEXT:"DIRECT: P$DIRECT"}
                                                                                    TERMINATE_VE
                                                                                ENDPROCEDURE
                                                                                ;*****************************************************
                                                                                
                                                                                • + Gestionando el tiempo de uso

                                                                                  No todos los recursos deben atender con la misma duración a cada entidad. En ciertos escenarios —como servicios al cliente, centros de atención médica o estaciones de procesamiento— puede ser razonable adaptar el tiempo de atención en función de la carga del sistema.

                                                                                  En este ejemplo, tres ventanillas (Facility_1, Facility_2, Facility_3) reciben flujos de entidades con distinta intensidad. Cada una está acompañada por un Queuer, que permite medir y registrar el tamaño actual de la cola.

                                                                                  Antes de ser atendida, cada entidad llama a un procedimiento que calcula dinámicamente su tiempo de atención, basándose en el tamaño actual de la cola del recurso.

                                                                                  La lógica usada en este caso es una de las siguientes:

                                                                                  assign tamCola,R$(P$ventanilla,QUEUE) + 1
                                                                                      
                                                                                  ASSIGN tiempoAtencion, round(max(2, 120 / P$tamCola))
                                                                                  ASSIGN tiempoAtencion, round(max(2, 120 / sqrt(P$tamCola)))
                                                                                  ASSIGN tiempoAtencion, round(120 / log(P$tamCola + 1))

                                                                                  Esto significa que:

                                                                                  • A mayor cola, menor tiempo de atención, priorizando fluidez.

                                                                                  • A menor cola, más tiempo por entidad, mejorando la calidad del servicio.

                                                                                  Es una estrategia de balanceo automático entre calidad y rendimiento.

                                                                                  Esta técnica demuestra:

                                                                                  • Cómo vincular un recurso con su Queuer para medir carga.

                                                                                  • Cómo usar funciones matemáticas para adaptar la duración de los ADVANCE.

                                                                                  • Cómo mantener bajo control el tamaño de la cola sin sacrificar la atención.

                                                                                  El ejemplo puede ser fácilmente extendido a:

                                                                                  • Funciones más complejas de atención.

                                                                                  • Decisión sobre qué ventanilla elegir.

                                                                                  • Optimización del rendimiento global del sistema.

                                                                                   

                                                                                   


                                                                                  Example:
                                                                                  /*
                                                                                  
                                                                                   Gestionando el tiempo de uso
                                                                                  
                                                                                  */
                                                                                  Facility {NAME:Facility_1,X:400,Y:84,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}
                                                                                  Facility {NAME:Facility_2,X:400,Y:282,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}
                                                                                  Facility {NAME:Facility_3,X:400,Y:471,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}
                                                                                  
                                                                                  Queuer {NAME:Queuer_1,X:400,Y:174,R_BIN_SIZE:1,R_BIN_COUNT:30}
                                                                                  Queuer {NAME:Queuer_2,X:400,Y:363,R_BIN_SIZE:1,R_BIN_COUNT:30}
                                                                                  Queuer {NAME:Queuer_3,X:400,Y:557,R_BIN_SIZE:1,R_BIN_COUNT:30}
                                                                                  
                                                                                  
                                                                                  
                                                                                  Graphic {NAME:Text_1,Type:TEXT,X:400,Y:132}
                                                                                  Graphic {NAME:Text_2,Type:TEXT,X:400,Y:323}
                                                                                  Graphic {NAME:Text_3,Type:TEXT,X:400,Y:512}
                                                                                  
                                                                                  POSITION {NAME:POS_1,X:700,Y:137}
                                                                                  POSITION {NAME:POS_2,X:700,Y:328}
                                                                                  POSITION {NAME:POS_3,X:700,Y:506}
                                                                                  
                                                                                  START 1000
                                                                                  
                                                                                  ;*********************************************************
                                                                                  
                                                                                  GENERATE 10,0 {NAME:GEN1,X:100,Y:126}
                                                                                      CALL caminoVentanilla,1
                                                                                  ENDGENERATE 1
                                                                                  
                                                                                  GENERATE 15,0 {NAME:GEN2,X:100,Y:319}
                                                                                      CALL caminoVentanilla,2
                                                                                  ENDGENERATE 1
                                                                                  
                                                                                  GENERATE 20,0 {NAME:GEN3,X:100,Y:492}
                                                                                      CALL caminoVentanilla,3
                                                                                  ENDGENERATE 1
                                                                                  ;----------------------------------
                                                                                  PROCEDURE caminoVentanilla
                                                                                  	assign ventanilla,"Facility_P$PARAM_A"
                                                                                  	assign cola,"Queuer_P$PARAM_A" {debug:1}
                                                                                  	assign nVentanilla,"P$PARAM_A"
                                                                                      ADVANCE 20 {TO:"P$ventanilla"}
                                                                                      CALL calcularTiempo , "P$ventanilla",P$nVentanilla
                                                                                  	QUEUE P$cola
                                                                                  	SEIZE P$ventanilla
                                                                                  	DEPART P$cola
                                                                                      ADVANCE P$calcularTiempo
                                                                                      RELEASE P$ventanilla
                                                                                      ADVANCE 20 {TO:"POS_P$nVentanilla"}
                                                                                  ENDPROCEDURE
                                                                                  ;----------------------------------
                                                                                  PROCEDURE calcularTiempo
                                                                                  	assign ventanilla,"P$PARAM_A"
                                                                                  	assign nVentanilla,P$PARAM_B
                                                                                      assign tamCola,R$(P$ventanilla,QUEUE) + 1
                                                                                      
                                                                                      ;ASSIGN tiempoAtencion, round(max(2, 120 / P$tamCola))
                                                                                      ;ASSIGN tiempoAtencion, round(max(2, 120 / sqrt(P$tamCola)))
                                                                                      ASSIGN tiempoAtencion, round(120 / log(P$tamCola + 1))
                                                                                      move {name:"Text_P$nVentanilla",text:"tiempo Atención: P$tiempoAtencion"}
                                                                                  	RETURN P$tiempoAtencion  
                                                                                  ENDPROCEDURE
                                                                                  
                                                                                  
                                                                                  • + Redirigir las colas

                                                                                    En este ejemplo, las entidades deben decidir por cuál de tres caminos avanzar, cada uno asociado a una ventanilla con su propia cola (FACILITY con distinta capacidad). La decisión se toma en la función DECIDE.SELECCIONA, que recorre una lista con los nombres de los caminos y el tamaño actual de sus colas (QUEUE). El resultado es el nombre del camino con menos carga.

                                                                                    Ese valor es un STRING que se devuelve con ENDPROCEDURE y se almacena automáticamente en la variable P$SELECCIONA, que luego se usa directamente como llamada: CALL P$SELECCIONA. Esta mecánica permite una selección dinámica sin necesidad de condicionales o múltiples llamadas explícitas.

                                                                                    Las estructuras .decide se usan así para devolver directamente el nombre del procedimiento a ejecutar, ahorrando al programador tener que hacer un doble SWITCH o una cascada de IF.

                                                                                    Esto simplifica notablemente la lógica de decisión ya que con simplemente:

                                                                                    CALL DECIDE.SELECCIONA
                                                                                    CALL P$SELECCIONA

                                                                                    sustituye a:

                                                                                    CALL DECIDE.SELECCIONA
                                                                                    SWITCH P$SELECCIONA
                                                                                      CASE ==, "CAMINO1"
                                                                                        CALL CAMINO1
                                                                                      CASE ==, "CAMINO2"
                                                                                        CALL CAMINO2
                                                                                      ...
                                                                                    ENDSWITCH

                                                                                    Example:
                                                                                    /*
                                                                                    
                                                                                     Redirigir las colas
                                                                                    
                                                                                    */
                                                                                    FACILITY {NAME:VENTANILLA1,X:320,Y:450,capacity:3}
                                                                                    FACILITY {NAME:VENTANILLA2,X:320,Y:300,capacity:1}
                                                                                    FACILITY {NAME:VENTANILLA3,X:320,Y:150,capacity:2}
                                                                                    
                                                                                    POSITION {NAME:POS1,X:152,Y:300}
                                                                                    POSITION {NAME:POS2,X:497,Y:300}
                                                                                    
                                                                                    Graphic {NAME:Text1,Type:TEXT,X:100,Y:100}
                                                                                    
                                                                                    START 200 
                                                                                    ;*************************************************************************
                                                                                    GENERATE 6,0 {NAME:GEN1,X:54,Y:300}
                                                                                    ADVANCE 20,0 {TO:POS1,flow:1}
                                                                                    
                                                                                    CALL DECIDE.SELECCIONA ; return strings : "CAMINO1", "CAMINO1" ó "CAMINO1" creando un ASSIGN llamado P$SELECCIONA
                                                                                    CALL P$SELECCIONA
                                                                                    
                                                                                    ADVANCE 20 {TO:POS2,flow:1,merge:"camino"}
                                                                                    
                                                                                    ENDGENERATE 1
                                                                                    
                                                                                    ;*************************************************************************
                                                                                    PROCEDURE DECIDE.SELECCIONA
                                                                                    
                                                                                    assign mimap, [  
                                                                                    {cola:"CAMINO1",encola: R$(VENTANILLA1,QUEUE)}, 
                                                                                    {cola:"CAMINO2",encola: R$(VENTANILLA2,QUEUE)},  
                                                                                    {cola:"CAMINO3",encola: R$(VENTANILLA3,QUEUE)}
                                                                                    ]
                                                                                    
                                                                                    ASSIGN MINKEY,"CAMINO1"; inicializamos el KEY seleccionado
                                                                                    ASSIGN MINVAL,100000 ; inicializamos el VAL de menor valor para las comparaciones 
                                                                                    
                                                                                    FOREACH tmp,IN,V$mimap
                                                                                    
                                                                                      IF (P$(tmp.encola) < P$MINVAL)
                                                                                        ASSIGN MINKEY,"P$(tmp.cola)" 
                                                                                        ASSIGN MINVAL,P$(tmp.encola)
                                                                                        move {name:Text1,Text:"Decisión: P$(MINKEY) Cola: P$(MINVAL)"}
                                                                                      ENDIF
                                                                                    ENDFOREACH
                                                                                    
                                                                                    
                                                                                    ENDPROCEDURE "P$MINKEY" ; retornará, por ejemplo, "C1"
                                                                                    
                                                                                    ;**************************************************************************
                                                                                    
                                                                                    PROCEDURE CAMINO1
                                                                                    ADVANCE 20  {TO:VENTANILLA1,flow:1,decision:"camino"}
                                                                                    SEIZE VENTANILLA1
                                                                                    ADVANCE 25,10
                                                                                    RELEASE VENTANILLA1
                                                                                    ENDPROCEDURE
                                                                                    
                                                                                    PROCEDURE CAMINO2
                                                                                    ADVANCE 20 {TO:VENTANILLA2,flow:1,decision:"camino"}
                                                                                    SEIZE VENTANILLA2
                                                                                    ADVANCE 80,70
                                                                                    RELEASE VENTANILLA2
                                                                                    ENDPROCEDURE
                                                                                    
                                                                                    PROCEDURE CAMINO3
                                                                                    ADVANCE 20 {TO:VENTANILLA3,flow:1,decision:"camino"}
                                                                                    SEIZE VENTANILLA3
                                                                                    ADVANCE 30,20
                                                                                    RELEASE VENTANILLA3
                                                                                    ENDPROCEDURE
                                                                                    
                                                                                    ;***************************************************************
                                                                                    
                                                                                    
                                                                                    
                                                                                    
                                                                                    
                                                                                    
                                                                                    
                                                                                    • + Actualizando la cola de eventos: UPDATE

                                                                                      Hemos visto anteriormente el bloque ADVANCE como algo que parecía inmutable. Por ejemplo:

                                                                                      ADVANCE 20,10 
                                                                                      

                                                                                      El rol de UPDATE

                                                                                      Una entidad que está dentro de un ADVANCE no puede auto-modificar su tiempo de activación, ya que está inactiva. Por tanto, debe ser otra entidad (usualmente una entidad virtual, activada por un TIMER o un HOOK) la que ejecute UPDATE para ajustar su tiempo.

                                                                                      Técnicamente, UPDATE no modifica un ADVANCE: modifica la cola de eventos.
                                                                                      Extrae la entidad de su activación futura y la reprograma en un nuevo tiempo absoluto.
                                                                                      El motor no interpreta el cambio: solo mantiene la cola ordenada.

                                                                                       

                                                                                      Escenario: Condiciones cambiantes en una carretera

                                                                                      Simulamos un tramo de carretera bajo ciertas condiciones almacenadas en el SAVEVALUE conditions.

                                                                                      • En condiciones normales (conditions = 1.0), se tarda 100 unidades de tiempo en cruzar.

                                                                                      • Si conditions = 1.5, el trayecto dura 150 unidades de tiempo.

                                                                                      • Una entidad calcula su tiempo de avance cuando entra en el tramo.

                                                                                      • Si las condiciones cambian mientras una entidad está en camino, queremos ajustar su tiempo de llegada, para reflejar este cambio.

                                                                                      ¿Cómo se calcula?

                                                                                      Imagina que una entidad entró cuando conditions = 1.2 (tiempo de recorrido = 120), pero ahora las condiciones mejoran a 1.0. Si ya ha recorrido el 50%, le quedarían 50 unidades con las nuevas condiciones. En total, habría tardado 110 en lugar de 120.

                                                                                      El bloque UPDATE

                                                                                      UPDATE entidadID, nuevoTiempo
                                                                                      

                                                                                      Este comando reprograma a una entidad con un nuevo tiempo de activación, siempre que dicho tiempo sea ≥ AC1$ (el tiempo actual de simulación).

                                                                                      Qué demuestra este ejemplo:

                                                                                      • Que el comportamiento de las entidades puede ser modificado incluso mientras están en curso.

                                                                                      • Que TIMER + FOREACH + UPDATE forman una combinación para reaccionar dinámicamente.

                                                                                      • Que se permite representar escenarios del mundo real, donde las condiciones cambian y el sistema debe adaptarse.

                                                                                       


                                                                                      Example:
                                                                                      /*
                                                                                      
                                                                                       Actualizando la cola de eventos: UPDATE
                                                                                      
                                                                                      */
                                                                                      SYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 100}
                                                                                      
                                                                                      Queuer {NAME:Queuer1,X:350,Y:324}
                                                                                      Graphic {NAME:Text1,Type:TEXT,X:352,Y:381}
                                                                                      
                                                                                      POSITION {NAME:POS1,X:90,Y:513}
                                                                                      POSITION {NAME:POS2,X:638,Y:512}
                                                                                      POSITION {NAME:POS3,X:635,Y:322}
                                                                                      
                                                                                      START 100
                                                                                      
                                                                                      ;*********************************************************
                                                                                      INITIAL defaultTime,100
                                                                                      INITIAL conditions,1.0
                                                                                      ;*********************************************************
                                                                                      PROCEDURE TIMER1
                                                                                      	savevalue oldConditions,X$conditions
                                                                                      	savevalue conditions,round(random + 0.5,2)
                                                                                          MOVE {NAME:Text1,TEXT:"Old Conditions: X$oldConditions \nNew conditions: X$conditions"}
                                                                                          FOREACH number,IN_RESOURCE,Queuer1
                                                                                          
                                                                                          assign myConditions,X$conditions,P$number
                                                                                      
                                                                                          ASSIGN from, D$(ADVANCESTART, P$number)
                                                                                          ASSIGN lapse, D$(ADVANCELAPSE, P$number)
                                                                                          
                                                                                          ASSIGN completedRatio , (AC1$ - P$from) / P$lapse
                                                                                      	ASSIGN remainingRatio , 1 - P$completedRatio 
                                                                                      
                                                                                      	ASSIGN adjustedRemainingTime , P$remainingRatio  * X$defaultTime * X$conditions
                                                                                      	ASSIGN newTime, AC1$ + P$adjustedRemainingTime 
                                                                                      
                                                                                            if (X$conditions<1)
                                                                                           	 MOD {number:P$number,color:#00DD00}
                                                                                            else
                                                                                            	MOD {number:P$number,color:#000000}
                                                                                            endif
                                                                                            if (P$newTime < AC1$)
                                                                                           	 assign newTime,AC1$
                                                                                            endif
                                                                                            Update P$number,P$newTime
                                                                                          ENDFOREACH
                                                                                      
                                                                                          
                                                                                      
                                                                                          TERMINATE_VE
                                                                                      ENDPROCEDURE
                                                                                      ;*********************************************************
                                                                                      
                                                                                      
                                                                                      GENERATE 20,0 {NAME:GEN1,X:102,Y:203, ECOLOR:#FF3333, ERADIO:8,esubtitle:P$myConditions}
                                                                                          ADVANCE 16,5 {TO:POS1}
                                                                                          assign myTime,X$defaultTime * X$conditions
                                                                                          assign myConditions,X$conditions
                                                                                          Queue Queuer1
                                                                                          ADVANCE P$myTime,0 {TO:POS2}
                                                                                          Depart Queuer1
                                                                                          MOD {color:#FF3333}
                                                                                          ADVANCE 16,5 {TO:POS3}
                                                                                      ENDGENERATE 1
                                                                                      
                                                                                      
                                                                                      
                                                                                      
                                                                                      
                                                                                      
                                                                                    • + Season 5: Gráficos
                                                                                      • + Crear y modificar

                                                                                        Crear y modificar

                                                                                        GPSS-Plus permite definir elementos gráficos que se pueden mover, transformar y actualizar durante la simulación. Estos objetos pueden ser textos, curvas, imágenes, arcos o líneas, y pueden agruparse para formar unidades visuales completas.

                                                                                        Los gráficos pueden moverse de forma individual o en grupo, y se pueden modificar sus propiedades como:

                                                                                        • Posición (X, Y)
                                                                                        • Rotación (ROTATE)
                                                                                        • Tamaño (RESIZE)
                                                                                        • Opacidad (OPACITY)
                                                                                        • Contenido textual (TEXT)
                                                                                        • Color y ángulos, en figuras como ARC o LINE

                                                                                        Los comandos MOVE permiten usar:

                                                                                        • Valores absolutos, como X:100
                                                                                        • Valores relativos, accediendo al valor actual mediante GD$, por ejemplo: X:GD$(objeto,X) + 10

                                                                                        Este ejemplo muestra cómo una entidad, a lo largo de sus pasos, modifica variables internas (SAVEVALUE) y usa esas variables para actualizar objetos gráficos en cada avance.

                                                                                        Se ilustra el uso de gráficos de tipo TEXT, CURVE3, CURVE, ARC, IMAGE y GROUP, así como el uso combinado de animaciones relativas, escalado y rotación.

                                                                                        Sobre curvas y relleno

                                                                                        GPSS-Plus admite dos tipos de curvas:

                                                                                        • CURVE3: curva de tres puntos (Bezier simple), declarada con coordenadas directas X1, Y1, X2, Y2, X3, Y3
                                                                                        • CURVE: curva suavizada tipo Catmull-Rom, definida con una lista de puntos

                                                                                        Para declarar los puntos, existen dos formas equivalentes:

                                                                                        • Sintaxis compacta: POINTS:"[x1,y1],[x2,y2],[x3,y3],..."
                                                                                        • Sintaxis directa por pares: X1:1,Y1:2, X2:3,Y2:4, X3:5,Y3:6

                                                                                        Las curvas, líneas o arcos pueden ser abiertos o cerrados. Para representarlos como figuras cerradas con relleno, es necesario establecer:

                                                                                        • CLOSE:1
                                                                                        • FCOLOR: para el color de relleno

                                                                                        Example:
                                                                                        /*
                                                                                        
                                                                                         Crear y modificar gráficos
                                                                                        
                                                                                        */
                                                                                        SYSTEM {TYPE:OPTIONS, SPEED:8, TIME_DECIMALS:1}
                                                                                        
                                                                                        ;==============================
                                                                                        ; VARIABLES DINÁMICAS
                                                                                        ;==============================
                                                                                        INITIAL GRADO, 0
                                                                                        INITIAL ALTURA, 80
                                                                                        INITIAL ESCALA, 100
                                                                                        INITIAL CRECIENDO, 1
                                                                                        
                                                                                        ;==============================
                                                                                        ; ELEMENTOS GRÁFICOS
                                                                                        ;==============================
                                                                                        
                                                                                        GRAPHIC {NAME:txt, TYPE:TEXT, X:300, Y:50,text:"--test--"}
                                                                                        
                                                                                        
                                                                                        GRAPHIC {NAME:radarGrupo, TYPE:GROUP, X:300, Y:400}
                                                                                        
                                                                                        GRAPHIC {
                                                                                          GROUP:radarGrupo,
                                                                                          NAME:radarTexto,
                                                                                          TYPE:TEXT,
                                                                                          TEXT:"GRADO: X$GRADO",
                                                                                          COLOR:#00AAFF,
                                                                                          X:0, Y:-50
                                                                                        }
                                                                                        
                                                                                        GRAPHIC {
                                                                                          GROUP:radarGrupo,
                                                                                          NAME:radarImagen,
                                                                                          TYPE:IMAGE,
                                                                                          SRC:"PUERTA",
                                                                                          X:100, Y:100,
                                                                                          OPACITY:0.9,
                                                                                          ROTATE:0
                                                                                        }
                                                                                        
                                                                                        GRAPHIC {
                                                                                          GROUP:radarGrupo,
                                                                                          NAME:ondaVisual,
                                                                                          TYPE:CURVE,
                                                                                          POINTS:"[0,0],[40,X$ALTURA],[80,0]",
                                                                                          COLOR:red,
                                                                                          WIDTH:2,
                                                                                          OPACITY:0.6
                                                                                        }
                                                                                        
                                                                                        GRAPHIC {
                                                                                          NAME:puerta,
                                                                                          TYPE:IMAGE,
                                                                                          SRC:"PUERTA",
                                                                                          X:400, Y:100,
                                                                                          OPACITY:0.9,
                                                                                          ROTATE:0
                                                                                        }
                                                                                        
                                                                                        
                                                                                        GRAPHIC {
                                                                                          NAME:barreraCircular,
                                                                                          TYPE:ARC,
                                                                                          X:100, Y:400,
                                                                                          COLOR:#FFAA00,
                                                                                          RADIUS:60,
                                                                                          START_ANGLE:0,
                                                                                          END_ANGLE:X$GRADO,
                                                                                          OPACITY:1,
                                                                                          CLOSE:0
                                                                                        }
                                                                                        
                                                                                        
                                                                                        ;==============================
                                                                                        ; ENTIDAD PRINCIPAL
                                                                                        ;==============================
                                                                                        GENERATE 5, 0 {NAME:GEN1, X:50, Y:200}
                                                                                        
                                                                                        ;move {name:txt,text:"Pos X GD$(barreraCircular,X)"}
                                                                                        ; Aumentar grado (rotación)
                                                                                        SAVEVALUE GRADO, (X$GRADO + 15) % 360
                                                                                        
                                                                                        ; Subir/bajar la onda
                                                                                        IF ((X$GRADO % 180) < 90)
                                                                                          SAVEVALUE ALTURA, X$ALTURA + 4
                                                                                        ELSE
                                                                                          SAVEVALUE ALTURA, X$ALTURA - 4
                                                                                        ENDIF
                                                                                        
                                                                                        ; Animar el tamaño
                                                                                        IF (X$CRECIENDO == 1)
                                                                                          SAVEVALUE ESCALA, X$ESCALA + 5
                                                                                          IF (X$ESCALA >= 140)
                                                                                            SAVEVALUE CRECIENDO, 0
                                                                                          ENDIF
                                                                                        ELSE
                                                                                          SAVEVALUE ESCALA, X$ESCALA - 5
                                                                                          IF (X$ESCALA <= 100)
                                                                                            SAVEVALUE CRECIENDO, 1
                                                                                          ENDIF
                                                                                        ENDIF
                                                                                        
                                                                                        ADVANCE 5, 0
                                                                                        
                                                                                        ;==============================
                                                                                        ; ACTUALIZACIONES VISUALES
                                                                                        ;==============================
                                                                                        
                                                                                        MOVE {NAME:radarTexto, TEXT:"Grado: X$GRADO"}
                                                                                        MOVE {NAME:radarImagen, ROTATE:X$GRADO}
                                                                                        MOVE {NAME:radarImagen, RESIZE:X$ESCALA}
                                                                                        MOVE {NAME:ondaVisual, Y2:X$ALTURA}
                                                                                        
                                                                                        
                                                                                        MOVE {NAME:radarGrupo, Y:(GD$(radarGrupo,Y)+(RANDOM*10)-5)}
                                                                                        
                                                                                        MOVE {NAME:puerta, RESIZE_X:X$ESCALA * 2}
                                                                                        MOVE {NAME:barreraCircular, END_ANGLE:X$GRADO}
                                                                                        
                                                                                        
                                                                                        TERMINATE 1
                                                                                        
                                                                                        START 500
                                                                                        
                                                                                        • + Modificación por procedimiento

                                                                                          Antes de introducir el sistema de contextos CX$ para instanciar componentes gráficos reutilizables en LIBRERIAS INDEPENDIENTES, es importante comprender cómo se puede construir un componente visual manualmente, paso a paso, utilizando procedimientos (PROCEDURE) y estructuras de configuración (INITIAL).

                                                                                          Este ejemplo no representa la forma óptima ni la más recomendable para implementar componentes gráficos reutilizables, pero cumple un papel esencial en el aprendizaje:

                                                                                          • Permite entender cómo se declaran los gráficos agrupados (GROUP).

                                                                                          • Muestra cómo parametrizar su geometría y posición mediante diccionarios (INITIAL).

                                                                                          • Enseña cómo encapsular transformaciones visuales dentro de procedimientos.

                                                                                          • Introduce la llamada de “funciones visuales” desde entidades con CALL.

                                                                                          Este enfoque será más adelante reemplazado por el uso de contextos CX$, pero conviene dominar primero este nivel “manual” para comprender en profundidad cómo GPSS-Plus convierte procedimientos en componentes visuales autónomos y reutilizables.

                                                                                          En este ejemplo se representa un depósito con nivel visual, compuesto por un GROUP que contiene:

                                                                                          • un marco exterior (LINE cerrado)

                                                                                          • un nivel de relleno (LINE cerrado, con altura variable)

                                                                                          • un texto con el porcentaje de llenado visible

                                                                                          Toda la geometría y la posición del depósito están definidas mediante una estructura de configuración (config) y se aplican directamente a gráficos con nombres fijos. Aunque este enfoque simula una forma de parametrización, no es reutilizable tal como está: los nombres de los objetos gráficos están codificados de forma estática, lo que impide instanciar múltiples depósitos sin conflictos.

                                                                                          Esta limitación se resolverá más adelante con el uso de contextos CX$ y librerías, que permitirán crear múltiples instancias visuales de un mismo componente, cada una con su propio estado y configuración.

                                                                                           

                                                                                          El procedimiento nivel_init se encarga de generar los vértices del gráfico al inicio (PRE_RUN), y el procedimiento deposito_set actualiza visualmente el nivel de llenado según un porcentaje (P$PARAM_A).

                                                                                          También se incluye deposito_locate, que permite mover todo el grupo a una nueva posición.

                                                                                           

                                                                                           

                                                                                           


                                                                                          Example:
                                                                                          /*
                                                                                          
                                                                                           Gráficos. Modificación por procedimiento
                                                                                           
                                                                                          */
                                                                                          SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                          
                                                                                          
                                                                                          ;==============================
                                                                                          ; CONFIGURACIÓN DEL DEPÓSITO
                                                                                          ;==============================
                                                                                          INITIAL config, {deposito_coords:[0, 0, 20, 200],deposito_position:[180,180]} 
                                                                                          
                                                                                          ;==============================
                                                                                          ; GRÁFICOS
                                                                                          ;==============================
                                                                                          GRAPHIC {NAME:Deposito, TYPE:GROUP, X:0, Y:0}
                                                                                          
                                                                                          ; Marco exterior
                                                                                          GRAPHIC {
                                                                                            NAME:Marco,
                                                                                            GROUP:Deposito,
                                                                                            TYPE:LINE,
                                                                                            POINTS:"[0,0],[0,10],[10,10],[10,10]",
                                                                                            COLOR:#555555,
                                                                                            CLOSE:1
                                                                                          }
                                                                                          
                                                                                          ; Nivel interior (relleno)
                                                                                          GRAPHIC {
                                                                                            NAME:Nivel,
                                                                                            GROUP:Deposito,
                                                                                            TYPE:LINE,
                                                                                            POINTS:"[0,300],[0,300],[40,300],[40,300]",
                                                                                            COLOR:#3399FF,
                                                                                            FCOLOR:#99CCFF,
                                                                                            CLOSE:1
                                                                                          }
                                                                                          
                                                                                          ; Texto de porcentaje
                                                                                          GRAPHIC {
                                                                                            NAME:TextoNivel,
                                                                                            GROUP:Deposito,
                                                                                            TYPE:TEXT,
                                                                                            X:0, Y:0,
                                                                                            TEXT:"Nivel: 0%",
                                                                                            COLOR:#000000
                                                                                          }
                                                                                          START 100
                                                                                          
                                                                                          
                                                                                          PROCEDURE PRE_RUN
                                                                                          	timeout nivel_init,0
                                                                                          	TERMINATE 
                                                                                          ENDPROCEDURE 1
                                                                                          
                                                                                          ;==============================
                                                                                          ; PROCEDIMIENTO DE ACTUALIZACIÓN
                                                                                          ;==============================
                                                                                          PROCEDURE nivel_init
                                                                                          move {name:Marco
                                                                                          	,X1:X$(config.deposito_coords.0)-4,Y1:X$(config.deposito_coords.1)-4
                                                                                          	,X2:X$(config.deposito_coords.2)+4,Y2:X$(config.deposito_coords.1)-4
                                                                                          	,X3:X$(config.deposito_coords.2)+4,Y3:X$(config.deposito_coords.3)+4
                                                                                          	,X4:X$(config.deposito_coords.0)-4,Y4:X$(config.deposito_coords.3)+4
                                                                                          	}
                                                                                          
                                                                                          move {name:Nivel
                                                                                          	,X1:X$(config.deposito_coords.0),Y1:X$(config.deposito_coords.1)
                                                                                          	,X2:X$(config.deposito_coords.2),Y2:X$(config.deposito_coords.1)
                                                                                          	,X3:X$(config.deposito_coords.2),Y3:X$(config.deposito_coords.3)
                                                                                          	,X4:X$(config.deposito_coords.0),Y4:X$(config.deposito_coords.3)
                                                                                          	}
                                                                                          
                                                                                          MOVE {NAME:TextoNivel
                                                                                          		, x:(GD$(Marco,X1) + GD$(Marco,X2))/ 2
                                                                                          		, y:X$(config.deposito_coords.1)-14, TEXT:"Nivel: PP$A%"}
                                                                                          
                                                                                          MOVE {NAME:Deposito
                                                                                          		, x:X$(config.deposito_position.0)
                                                                                          		, y:X$(config.deposito_position.1)}   
                                                                                          terminate
                                                                                          endprocedure 1
                                                                                          
                                                                                          
                                                                                          PROCEDURE deposito_set
                                                                                          
                                                                                          ; PP$A = porcentaje deseado (0 a 100)
                                                                                          
                                                                                          ASSIGN yBase, X$(config.deposito_coords.1) ; y inferior 
                                                                                          ASSIGN yTop, X$(config.deposito_coords.3)  ; y superior
                                                                                          
                                                                                          ASSIGN alturaMaxima, P$yBase - P$yTop
                                                                                          ASSIGN alturaNivel, P$alturaMaxima * P$PARAM_A / 100
                                                                                          ASSIGN yActual, P$yBase - P$alturaNivel
                                                                                          
                                                                                          ; Mover vértices del nivel
                                                                                          MOVE {
                                                                                            NAME:Nivel,
                                                                                            Y3:P$yActual,
                                                                                            Y4:P$yActual
                                                                                          }
                                                                                          MOVE {NAME:TextoNivel,TEXT:"Nivel: P$PARAM_A%"}
                                                                                          
                                                                                          ENDPROCEDURE 1
                                                                                          
                                                                                          PROCEDURE deposito_locate
                                                                                          
                                                                                          MOVE {NAME:Deposito
                                                                                          		, x:P$PARAM_A
                                                                                          		, y:P$PARAM_B}   
                                                                                                  
                                                                                          ENDPROCEDURE 1
                                                                                          
                                                                                          ;==============================
                                                                                          ; ENTIDAD QUE LO USA
                                                                                          ;==============================
                                                                                          GENERATE 25,0 {NAME:Gen1}
                                                                                          
                                                                                          CALL deposito_set, 25
                                                                                          ADVANCE 5
                                                                                          
                                                                                          CALL deposito_set, 50
                                                                                          ADVANCE 5
                                                                                          
                                                                                          CALL deposito_set, 75
                                                                                          ADVANCE 5
                                                                                          
                                                                                          CALL deposito_set, 90
                                                                                          ADVANCE 5
                                                                                          
                                                                                          CALL deposito_set, 10
                                                                                          ADVANCE 5
                                                                                          CALL deposito_locate, GD$(Deposito,X)+4,GD$(Deposito,Y)+4
                                                                                          
                                                                                          TERMINATE 1
                                                                                          
                                                                                          
                                                                                          
                                                                                          
                                                                                          
                                                                                          • + Ejemplo: Reloj analógico animado

                                                                                            Este ejemplo representa un reloj de agujas tradicional (analógico) utilizando primitivas gráficas. A través de un PROCEDURE se inicializa el escenario con un arco de fondo, marcas horarias (TEXT) y las dos agujas (LINE). Una entidad virtual (TIMER) se genera periódicamente y actualiza las posiciones de las agujas usando trigonometría y variables temporales (SAVEVALUE).

                                                                                            Se combinan:

                                                                                            • ARC para la circunferencia principal.

                                                                                            • TEXT para los números y el reloj digital.

                                                                                            • LINE para representar las agujas.

                                                                                            Es un caso claro donde la animación no viene de entidades móviles, sino de transformaciones visuales programadas.

                                                                                            Se puede observar la velocidad de reproducción, con un valor de speed: 5 aproximadamente se acompasaría el AC1 con el segundero.


                                                                                            Example:
                                                                                            /*
                                                                                            
                                                                                             Gráficos. Reloj analógico animado
                                                                                             
                                                                                            */
                                                                                            SYSTEM {type:OPTIONS, TIME_DECIMALS:0, SPEED:7}   
                                                                                            SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                            SYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 10}
                                                                                            
                                                                                            
                                                                                            ; ANALOGIC CLOCK
                                                                                            GRAPHIC {NAME:OUT,TYPE:ARC,X:0,Y:0,COLOR:#FFFF99,RADIUS:200,START_ANGLE:0,END_ANGLE:360,CLOSE:0}
                                                                                            GRAPHIC {NAME:AT12,TYPE:TEXT,X:300,Y:500,TEXT:"12"}
                                                                                            GRAPHIC {NAME:AT3,TYPE:TEXT,X:500,Y:300,TEXT:"3"}
                                                                                            GRAPHIC {NAME:AT6,TYPE:TEXT,X:300,Y:100,TEXT:"6"}
                                                                                            GRAPHIC {NAME:AT9,TYPE:TEXT,X:100,Y:300,TEXT:"9"}
                                                                                            
                                                                                            GRAPHIC {NAME:MINLINE,TYPE:LINE,COLOR:#F00,X1:268,Y1:361,X2:265,Y2:332}
                                                                                            GRAPHIC {NAME:HOURLINE,TYPE:LINE,COLOR:#000,X1:240,Y1:361,X2:233,Y2:328}
                                                                                            ; DIGITAL CLOCK
                                                                                            GRAPHIC {NAME:TIME,TYPE:TEXT,X:300,Y:536,TEXT:"IT'S.. ",FONT:"26PX"}
                                                                                            GRAPHIC {NAME:REALTIME1,TYPE:TEXT,X:575,Y:536,TEXT:"REALTIME.. ",FONT:"14PX"}
                                                                                            GRAPHIC {NAME:REALTIME2,TYPE:TEXT,X:575,Y:516,TEXT:"REALTIME.. ",FONT:"14PX"}
                                                                                            
                                                                                            START 1
                                                                                            
                                                                                            ; THE CLOCK
                                                                                            
                                                                                            GENERATE 100,0 {NAME:GEN1,VISIBLE:0,EVISIBLE:0,X:647,Y:99}
                                                                                            
                                                                                            TERMINATE 0
                                                                                            
                                                                                            
                                                                                            PROCEDURE PRE_RUN
                                                                                            	SAVEVALUE MIN,0
                                                                                                SAVEVALUE A,0
                                                                                                SAVEVALUE B,0
                                                                                                SAVEVALUE CENTROX,300
                                                                                                SAVEVALUE CENTROY,300
                                                                                                SAVEVALUE RADIOM,180
                                                                                                SAVEVALUE RADIOH,120
                                                                                                MOVE {NAME:OUT,X:X$CENTROX,Y:X$CENTROY}
                                                                                                MOVE {NAME:MINLINE,x1:0,y1:0,x2:0,y2:0}
                                                                                            	MOVE {NAME:HOURLINE,x1:0,y1:0,x2:0,y2:0}
                                                                                            	TERMINATE_VE 
                                                                                            ENDPROCEDURE 
                                                                                            
                                                                                            PROCEDURE TIMER1
                                                                                            
                                                                                            	assign sys,SYS$
                                                                                                
                                                                                                ASSIGN sys, SYS$
                                                                                              	MOVE {name: REALTIME1, text:"P$(sys.date.year)/P$(sys.date.month)/P$(sys.date.day)"}
                                                                                              	MOVE {name: REALTIME2, text:"P$(sys.date.hour):P$(sys.date.min):P$(sys.date.sec)"}
                                                                                              
                                                                                            
                                                                                            	MOVE {NAME:TIME,TEXT:"[X$HOR:X$MIN2]"}
                                                                                                MOVE {NAME:MINLINE
                                                                                                		,X1: (X$CENTROX)
                                                                                                		,Y1:(X$CENTROY)
                                                                                                        ,X2:(X$CENTROX+SIN(X$A)*X$RADIOM)
                                                                                                        ,Y2:(X$CENTROY+COS(X$A)*X$RADIOM)}
                                                                                                MOVE {NAME:HOURLINE
                                                                                                		,X1:(X$CENTROX)
                                                                                                		,Y1:(X$CENTROY)
                                                                                                        ,X2:(X$CENTROX+SIN(X$B)*X$RADIOH)
                                                                                                        ,Y2:(X$CENTROY+COS(X$B)*X$RADIOH)}
                                                                                            
                                                                                                SAVEVALUE MIN, (X$MIN+1)
                                                                                                SAVEVALUE MIN2,(X$MIN %60)
                                                                                                SAVEVALUE HOR, (FLOOR(X$MIN/60))
                                                                                                SAVEVALUE A, (MODULO(X$MIN,60) *2*PI)/60
                                                                                                SAVEVALUE B, (MODULO(X$MIN,720) *2*PI)/720
                                                                                            	TERMINATE_VE 
                                                                                            ENDPROCEDURE
                                                                                            
                                                                                            
                                                                                            
                                                                                            
                                                                                            
                                                                                            • + En 3D

                                                                                              Aunque todo es representable en 2D, en ciertas simulaciones —especialmente las de naturaleza mecánica o con énfasis visual— puede ser deseable utilizar representaciones en 3D.

                                                                                              GPSS-Plus no pretende ser una herramienta de modelado 3D como tal; el enfoque está en facilitar simulaciones visualmente efectivas y fáciles de implementar. Por ello, el diseño 3D puede provenir de dos fuentes:

                                                                                              • Ficheros externos .GLB

                                                                                              • Primitivas geométricas integradas

                                                                                               

                                                                                              Primitivas 3D disponibles

                                                                                              Tipo Descripción
                                                                                              BOX Caja tridimensional con WIDTH, HEIGHT, DEPTH
                                                                                              SPHERE Esfera con RADIUS, SEGMENTS opcionales
                                                                                              TRIANGLE Triángulo
                                                                                              PLANE Plano formado por dos triángulos que comparten arista
                                                                                              PRISM Prisma regular entre dos puntos, con SIDES lados
                                                                                              OBJECT Objeto externo en formato .GLB

                                                                                               

                                                                                              Transformaciones permanentes (al crear)

                                                                                              Propiedad Aplicación Descripción
                                                                                              WIDTH, HEIGHT, DEPTH Permanente Redimensiona la geometría base
                                                                                              displace_x/y/z Permanente Desplaza el modelo con respecto a su pivote interno
                                                                                              rotate_x/y/z Permanente Gira el modelo respecto a su pivote interno

                                                                                              Transformaciones dinámicas (en MOVE)

                                                                                              Acción Descripción
                                                                                              MOVE_BETWEEN Posiciona el objeto entre dos puntos manteniendo su tamaño
                                                                                              STRETCH_BETWEEN Escala el objeto en Y para ajustarse entre dos puntos
                                                                                              rotate_y Añade rotación adicional sobre su eje Y, útil para tornillos o engranajes
                                                                                              y En MOVE_BETWEEN, permite desplazar a lo largo de la línea


                                                                                              Estas acciones son análogas a las uniones PRISMATIC y REVOLUTE en sistemas mecánicos, pero aplicadas directamente desde el conocimiento matemático de la posición del objeto.

                                                                                              Ejemplos

                                                                                              ; Coloca un muelle descrito en un fichero entre dos puntos definidos
                                                                                              MOVE {name:muelle, STRETCH_BETWEEN:"[0,0,0],[0,P$x_masa,0]"}
                                                                                              
                                                                                              ; coloca un pistón o segmento telescópico a lo largo de un segmento en la posición "X" con respecto al su punto central
                                                                                              MOVE {name:amortiguador, MOVE_BETWEEN:"[0,0,0],[0,P$x_masa,0]",y:P$x_masa -10}

                                                                                              Activar el modo 3D

                                                                                              SYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:30, CAMERA:1}

                                                                                              V_WIDTH, V_HEIGHT: área que ocuparán los objetos en unidades internas
                                                                                              CAMERA: preset de cámara (1 = isométrico, 2 = cenital, etc.)

                                                                                              El ejemplo:

                                                                                              Para el ejemplo, se usan dos puntos para rotar, en ese mismo eje, una tuerca.


                                                                                              Example:
                                                                                              /*
                                                                                              
                                                                                               Gráficos. 3D
                                                                                               
                                                                                              */
                                                                                              SYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}
                                                                                              SYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:40, V_HEIGHT:20, CAMERA:0}
                                                                                              SYSTEM {TYPE:OPTIONS, TIME_DECIMALS:1, SPEED:5}
                                                                                              
                                                                                              GRAPHIC {NAME:suelo, TYPE:PLANE, X:25, POINTS:"[0,0,0],[0,0,40],[40,0,0]", color:#fabada}
                                                                                              
                                                                                              GRAPHIC {NAME:nut, TYPE:OBJECT, src:NUT, X:0, Y:0, Z:0,WIDTH:20, HEIGHT:20, DEPTH:20,rotate_x:90,opacity:0.9}
                                                                                              
                                                                                              Graphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}
                                                                                              Graphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}
                                                                                              Graphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}
                                                                                              
                                                                                              
                                                                                              Graphic {NAME:linea4,Type:LINE,POINTS:"[10,0,10],[150,150,150]",color:red}
                                                                                              Graphic {NAME:Text1,Type:TEXT,X:20,Y:20,text:"sss",font:"2px"}
                                                                                              
                                                                                              
                                                                                              
                                                                                              START 1
                                                                                              ;===========================
                                                                                              ; PROCEDIMIENTOS
                                                                                              ;===========================
                                                                                              
                                                                                              PROCEDURE agente.init
                                                                                              
                                                                                              savevalue DT,0.1
                                                                                              savevalue velocidad,1
                                                                                              savevalue X,4
                                                                                              savevalue estado,0
                                                                                              
                                                                                                WHILE (1==1)
                                                                                              
                                                                                              	if (X$estado==0)
                                                                                                  savevalue X,X$X + X$DT * X$velocidad
                                                                                              	else 
                                                                                                  savevalue X,X$X - X$DT * X$velocidad
                                                                                              	endif
                                                                                                  
                                                                                                  if (X$X>10)
                                                                                                  savevalue estado,1
                                                                                                  endif
                                                                                                  if (X$X<4)
                                                                                                  savevalue estado,0
                                                                                                  endif
                                                                                                 
                                                                                                  assign altura, round(X$X,3)
                                                                                              	move {name:Text1,text:"Altura: P$altura"}
                                                                                                  ; La tuerca se coloca y rota sobre su eje
                                                                                                  MOVE {name:nut, MOVE_BETWEEN:"[10,0,10],[150,150,150]", rotate_y:P$altura * 60,y:P$altura}
                                                                                              
                                                                                                  ADVANCE 1, 0
                                                                                              
                                                                                                ENDWHILE
                                                                                              
                                                                                              STOP
                                                                                              ENDPROCEDURE
                                                                                              ;===========================
                                                                                              PROCEDURE PRE_RUN
                                                                                              
                                                                                                TIMEOUT agente.init, 0
                                                                                              
                                                                                                TERMINATE_VE
                                                                                              
                                                                                              ENDPROCEDURE
                                                                                              
                                                                                              
                                                                                              
                                                                                              
                                                                                              
                                                                                            • + Season 6: Agentes y componentes
                                                                                              • + Entidad virtual persistente. El agente

                                                                                                En GPSS-Plus, una entidad virtual (VE) suele ser efímera: nace, ejecuta una serie de bloques, y termina. Sin embargo, algunos modelos requieren procesos permanentes que puedan actuar como controladores, gestores, o procesos pasivos que esperan órdenes. A estas VEs especiales las llamamos agentes.

                                                                                                Qué es un agente

                                                                                                Un agente es simplemente una entidad virtual que nunca termina su ejecución. Para ello, se inicializa con el bloque TIMEOUT (fuera de los bloques GENERATE), lo que hace que comience sin depender de ningún flujo de llegada.

                                                                                                Una vez creada, el agente puede comportarse de dos formas principales:

                                                                                                1. Activa: ejecuta ciclos continuamente (por ejemplo, usando ADVANCE) para revisar y tomar decisiones. Como un TIMER que es siempre la misma entidad.

                                                                                                2. Pasiva: entra en espera con HOLD y ahí permanecerá mientras no se le ordene hacer otra cosa.

                                                                                                Cómo se crea:

                                                                                                En el PRE_RUN llamamos a su PROCEDURE inicializador:

                                                                                                timeout agente1.main,0 ; Se ejecutará en el instante 0.

                                                                                                Que por convenio se llamará ".main".

                                                                                                Este PROCEDURE se encargará de almacenar en un SAVEVALUE su identificador para que cualquier entidad pueda acceder a ella y tras esto entrará en el bucle infinito con espera activa o pasiva:

                                                                                                PROCEDURE agente1.main
                                                                                                    SAVEVALUE nAgente1,D$N
                                                                                                    WHILE (1==1)
                                                                                                    ADVANCE 100
                                                                                                    ... ; Acciones periódicas
                                                                                                    ENDWHILE
                                                                                                    TERMINATE_VE ; No se alcanzará nunca
                                                                                                ENDPROCEDURE
                                                                                                
                                                                                                ;----------------------------------------
                                                                                                
                                                                                                PROCEDURE agente2.main
                                                                                                    SAVEVALUE nAgente2,D$N
                                                                                                    HOLD HOLDER_AGENTES
                                                                                                    TERMINATE_VE ; No se alcanzará nunca
                                                                                                ENDPROCEDURE

                                                                                                Y sus métodos o PROCEDURES asociados siguen la convención agente.metodo, por ejemplo:

                                                                                                procedure agente2.liberacion_total
                                                                                                    MOVE {NAME:INFO2, TEXT:"T= AC1$ Agente 2 liberando todo"}
                                                                                                    UNHOLD HOLDER1
                                                                                                    UNHOLD HOLDER2
                                                                                                    RETURN_RESTORE ; Vuelve exactamente a su estado anterior
                                                                                                ENDPROCEDURE

                                                                                                Donde lo más importante a destacar es su finalización especial: RETURN_RESTORE

                                                                                                Este bloque devulverá al agente a la misma situación en la que se encontraba al ser interrumpida mediante el SIGNAL / SIGNALNOW.
                                                                                                Si estaba en un ADVANCE, se reanuda con el tiempo restante ajustado.
                                                                                                Si estaba en una cola de un recurso, no se alterará su situación.

                                                                                                Nótese que si se utilizase RETURN o ENDPROCEDURE, saltaría a la siguiente línea de código. Útil si se está en una espera activa y se quiere reiniciar el tiempo de actuación periódica.

                                                                                                En el ejemplo:

                                                                                                Vemos a dos agentes de ambos tipos liberando entidades de dos HOLDERS de formas arbitrarias.
                                                                                                El segundo, cada 20 entidades que completan el recorrido, libera todas las atrapadas.


                                                                                                Example:
                                                                                                /*
                                                                                                
                                                                                                 Entidad virtual persistente. El agente
                                                                                                 
                                                                                                */
                                                                                                SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                START 100
                                                                                                
                                                                                                
                                                                                                ;===> Configuración de recursos y posiciones
                                                                                                Restroom {NAME:Restroom1, X:337, Y:344}
                                                                                                Restroom {NAME:Restroom2, X:337, Y:258}
                                                                                                Restroom {NAME:Restroom_AGENTE2, X:567, Y:104}
                                                                                                
                                                                                                POSITION {NAME:ENTRADA, X:179, Y:301}
                                                                                                POSITION {NAME:SALIDA, X:559, Y:299}
                                                                                                
                                                                                                GRAPHIC {NAME:INFO1, TYPE:TEXT, X:335, Y:178, TEXT:"Agente1"}
                                                                                                GRAPHIC {NAME:INFO2, TYPE:TEXT, X:569, Y:62, TEXT:"Agente2"}
                                                                                                INITIAL contador,0
                                                                                                
                                                                                                
                                                                                                
                                                                                                ;*****************************************************
                                                                                                ;===> Inicialización del agente
                                                                                                PROCEDURE PRE_RUN
                                                                                                	timeout agente1.main,0
                                                                                                	timeout agente2.main,0
                                                                                                	TERMINATE_VE 
                                                                                                ENDPROCEDURE
                                                                                                
                                                                                                ;*****************************************************
                                                                                                ;===> Flujo principal de las entidades
                                                                                                GENERATE 15,5 {NAME:GEN1, X:50, Y:300}
                                                                                                ADVANCE 10 {TO:ENTRADA}
                                                                                                
                                                                                                ASSIGN aleatorio, FLOOR(RANDOM * 2) + 1
                                                                                                IF (P$aleatorio==1)
                                                                                                	MOD {color:#990000}
                                                                                                    ADVANCE 10 {TO:Restroom1}
                                                                                                    REST Restroom1
                                                                                                ELSE
                                                                                                    MOD {color:#999900}
                                                                                                    ADVANCE 10 {TO:Restroom2}
                                                                                                    REST Restroom2
                                                                                                ENDIF
                                                                                                
                                                                                                
                                                                                                
                                                                                                ADVANCE 30,30 {TO:SALIDA}
                                                                                                
                                                                                                if (D$N %20 == 0)
                                                                                                	SIGNAL agente2.liberacion_total,X$nAgente2
                                                                                                endif
                                                                                                
                                                                                                ENDGENERATE 1
                                                                                                
                                                                                                ;*******************************
                                                                                                procedure agente2.main
                                                                                                    ; Asigna su nombre único como variable si hace falta
                                                                                                    SAVEVALUE nAgente2,D$N
                                                                                                    MOVE {NAME:INFO2, TEXT:"Agente 1 activo"}
                                                                                                
                                                                                                    WHILE (1==1) ; inútil si siempre se usa RETURN_RESTORE
                                                                                                        REST Restroom_AGENTE2
                                                                                                    ENDWHILE
                                                                                                
                                                                                                    TERMINATE_VE ; No se alcanzará nunca
                                                                                                ENDPROCEDURE
                                                                                                
                                                                                                ;*******************************
                                                                                                procedure agente2.liberacion_total
                                                                                                    MOVE {NAME:INFO2, TEXT:"T= AC1$ Agente 2 liberando todo"}
                                                                                                	WAKE Restroom1
                                                                                                	WAKE Restroom2
                                                                                                	RETURN_RESTORE
                                                                                                ENDPROCEDURE
                                                                                                
                                                                                                ;**********************************
                                                                                                
                                                                                                
                                                                                                PROCEDURE agente1.main
                                                                                                    ; Asigna su nombre único como variable si hace falta
                                                                                                    SAVEVALUE nAgente1,D$N
                                                                                                    MOVE {NAME:INFO1, TEXT:"Agente 2 activo"}
                                                                                                
                                                                                                    WHILE (1==1)
                                                                                                        ADVANCE 50 ; El agente revisa cada 50 unidades
                                                                                                
                                                                                                        SAVEVALUE contador, X$contador + 1
                                                                                                        MOVE {NAME:INFO1, TEXT:"Agente: X$contador"}
                                                                                                
                                                                                                        IF (X$contador % 3==0)
                                                                                                        	assign liberar,R$(Restroom1,IN)
                                                                                                            MOVE {NAME:INFO1, TEXT:"Liberando Restroom 1 (P$liberar DE R$(Restroom1,IN))"}
                                                                                                            WAKE Restroom1
                                                                                                        ENDIF
                                                                                                
                                                                                                        IF (X$contador % 5 ==0)
                                                                                                        	assign liberar,round(R$(Restroom2,IN) * 2 / 3)
                                                                                                            MOVE {NAME:INFO1, TEXT:"Liberando Restroom 2 (P$liberar DE R$(Restroom2,IN))"}
                                                                                                            WAKE Restroom2,P$liberar
                                                                                                            
                                                                                                        ENDIF
                                                                                                
                                                                                                    ENDWHILE
                                                                                                
                                                                                                    TERMINATE_VE ; No se alcanzará nunca
                                                                                                ENDPROCEDURE
                                                                                                ;**********************************
                                                                                                
                                                                                                
                                                                                                
                                                                                                • + Agente como controlador

                                                                                                  En este capítulo exploramos cómo un agente puede actuar como un controlador visual autónomo, que reacciona a los cambios de su entorno y produce efectos animados. Veremos un ejemplo típico: una puerta automática que se abre cuando detecta entidades cerca y se cierra cuando no hay nadie.

                                                                                                  Idea base

                                                                                                  El agente es una VE que no termina nunca y que ejecuta repetidamente una tarea: comprobar si hay entidades en una zona determinada (simulada con un QUEUER) y mover la puerta en consecuencia.

                                                                                                  Este tipo de lógica es ideal para comportamientos animados, sensores, semáforos o cualquier objeto visual que actúe en base al entorno.


                                                                                                  Example:
                                                                                                  /*
                                                                                                  
                                                                                                    	Agente como controlador
                                                                                                   
                                                                                                  */
                                                                                                  SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                  
                                                                                                  Queuer {NAME:sensor,X:403,Y:575}
                                                                                                  
                                                                                                  POSITION {NAME:POS1,X:265,Y:454}
                                                                                                  POSITION {NAME:POS2,X:523,Y:449}
                                                                                                  POSITION {NAME:POS3,X:720,Y:450}
                                                                                                  
                                                                                                  Graphic {NAME:Line1,Type:LINE,color:#FF0000, X1:418,Y1:370,X2:413,Y2:383,X3:387,Y3:383,X4:381,Y4:370}
                                                                                                  Graphic {NAME:Line2,Type:LINE,color:#FF0000, X1:422,Y1:509,X2:414,Y2:486,X3:387,Y3:486,X4:382,Y4:508}
                                                                                                  
                                                                                                  Graphic {NAME:Text1,Type:TEXT,X:431,Y:262,Text:" "}
                                                                                                  
                                                                                                  initial nAgente,0
                                                                                                  START 200
                                                                                                  
                                                                                                  ;*****************************************************
                                                                                                  
                                                                                                  PROCEDURE PRE_RUN
                                                                                                  	timeout agente.main,0
                                                                                                  	TERMINATE_VE
                                                                                                  ENDPROCEDURE 1
                                                                                                  
                                                                                                  ;*****************************************************
                                                                                                  GENERATE 30,50 {NAME:GEN1,X:86,Y:450}
                                                                                                  
                                                                                                  	ADVANCE 20,0 {TO:POS1}
                                                                                                  	queue sensor
                                                                                                  	ADVANCE 20,0 {TO:POS2}
                                                                                                  	depart sensor
                                                                                                  	ADVANCE 20,0 {TO:POS3}
                                                                                                  
                                                                                                  ENDGENERATE 1
                                                                                                  
                                                                                                  
                                                                                                  ;*******************************
                                                                                                  procedure agente.main
                                                                                                  	savevalue nAgente,D$N
                                                                                                      assign usuarios,0
                                                                                                      assign estado,100
                                                                                                      assign posY2,GD$(Line1,Y2)
                                                                                                      while (1==1)
                                                                                                      	advance 1
                                                                                                          call agente.moverPuerta
                                                                                                      endwhile
                                                                                                      terminate_ve
                                                                                                  endprocedure
                                                                                                  
                                                                                                  
                                                                                                  procedure agente.moverPuerta
                                                                                                      IF (R$(sensor,IN)<=0)
                                                                                                      assign estado,P$estado + 5
                                                                                                      assign estado,MIN(100,P$estado)
                                                                                                      else 
                                                                                                      assign estado,P$estado -5
                                                                                                      assign estado,MAX(10,P$estado)
                                                                                                      ENDIF
                                                                                                  
                                                                                                  move {name:Line1,Y2:P$estado + P$posY2,Y3:P$estado + P$posY2}
                                                                                                  ;move {name:Text1,TEXT:"p$usuarios P$estado | P$posY1"}
                                                                                                  
                                                                                                  endprocedure
                                                                                                  
                                                                                                  
                                                                                                  
                                                                                                  
                                                                                                  
                                                                                                  • + SIGNAL Vs SIGNALNOW

                                                                                                    En este capítulo vamos a desgranar el funcionamiento conjunto de la cola de eventos y la pila de contexto (stack) dentro de GPSS-Plus, y cómo esto afecta a los comportamientos asíncronos con SIGNAL y síncronos con SIGNALNOW.

                                                                                                    La cola de eventos

                                                                                                    Cuando una entidad ejecuta un bloque ADVANCE, pasa a la cola de eventos, que se ordena cronológicamente. En caso de coincidencia de tiempo, la entidad que entra primero tendrá prioridad (FIFO).
                                                                                                    Esta cola contiene solo tres datos por elemento:

                                                                                                    • Tiempo de ejecución
                                                                                                    • Referencia a la entidad
                                                                                                    • Tipo de elemento

                                                                                                     

                                                                                                    El stack de contexto

                                                                                                    Cada entidad dispone de un stack privado, una pila de contexto que permite reanudar su ejecución tras un salto (CALL, FOREACH, etc.).
                                                                                                    Este stack es LIFO: el último contexto en entrar es el primero en salir.

                                                                                                    En llamadas SIGNAL o SIGNALNOW a otras entidades (agentes), se guarda en esta pila el estado de ejecución actual, para que pueda retomarse tras la llamada.

                                                                                                     

                                                                                                     

                                                                                                    ¿Qué sucede con SIGNAL?

                                                                                                    SIGNAL es asíncrono. La entidad que lo emite no se detiene: simplemente agenda la ejecución de un procedimiento en otro agente/entidad.

                                                                                                    Si enviamos tres SIGNAL seguidos al mismo agente:

                                                                                                    SIGNAL agente.suma2,X$nAgente
                                                                                                    SIGNAL agente.multiplica10,X$nAgente
                                                                                                    SIGNAL agente.suma4,X$nAgente

                                                                                                    la entidad emisora seguirá su camino, pero el agente receptor quedará planificado tres veces en la cola de eventos.

                                                                                                    Cuando el agente tome el control, usará su stack para ejecutar los procedimientos pendientes. Pero al ser una pila LIFO, ejecutará en orden inverso al deseado:

                                                                                                    (((0 + 4) * 10) + 2) = 42

                                                                                                    ¿Qué pasa con SIGNALNOW?

                                                                                                    SIGNALNOW es síncrono. Detiene temporalmente la entidad que lo invoca, ejecuta inmediatamente al agente y luego la entidad continúa.

                                                                                                    SIGNALNOW agente.suma2,X$nAgente
                                                                                                    SIGNALNOW agente.multiplica10,X$nAgente
                                                                                                    SIGNALNOW agente.suma4,X$nAgente

                                                                                                    Aquí, las llamadas se ejecutan en el orden escrito, porque se intercalan correctamente:

                                                                                                    (((0 + 2) * 10) + 4) = 24

                                                                                                    Ejemplo en acción

                                                                                                    Este modelo define un agente con tres métodos (suma2, multiplica10, suma4). Dos entidades lo invocan: una usando SIGNAL y otra con SIGNALNOW. En pantalla se puede ver la diferencia en el resultado.

                                                                                                    En el caso de SIGNAL, se observa que inicialmente el resultado sigue siendo 0 puesto que los SIGNAL no se han ejecutado. Basta un ADVANCE 0 (la entidad se vuelve a programar en la cola de eventos) para que los agentes se ejecuten y tras ello, el resultado está disponible.

                                                                                                     

                                                                                                     


                                                                                                    Example:
                                                                                                    /*
                                                                                                    
                                                                                                      	SIGNAL Vs SIGNALNOW
                                                                                                     
                                                                                                    */
                                                                                                    SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                    
                                                                                                    Restroom {NAME:RestroomAgentes,X:252,Y:60,visible:1}
                                                                                                    
                                                                                                    POSITION {NAME:POS1,X:552,Y:194}
                                                                                                    POSITION {NAME:POS2,X:552,Y:394}
                                                                                                    
                                                                                                    Graphic {NAME:Text1,Type:TEXT,X:313,Y:252,Text:"Entidad"}
                                                                                                    Graphic {NAME:Text2,Type:TEXT,X:313,Y:232,Text:"Entidad"}
                                                                                                    Graphic {NAME:Text3,Type:TEXT,X:326,Y:444,Text:"Entidad"}
                                                                                                    Graphic {NAME:TextAgente,Type:TEXT,X:311,Y:325,Text:"---",color:#ff3333}
                                                                                                    
                                                                                                    initial nAgente,0
                                                                                                    START 2
                                                                                                    
                                                                                                    ;*****************************************************
                                                                                                    
                                                                                                    PROCEDURE PRE_RUN
                                                                                                    	timeout agente.main,0
                                                                                                    	TERMINATE_VE
                                                                                                    ENDPROCEDURE 1
                                                                                                    
                                                                                                    ;*****************************************************
                                                                                                    GENERATE 10,0,0,1 {NAME:GEN1,X:100,Y:200}
                                                                                                    	
                                                                                                        assign resultado,0,X$nAgente
                                                                                                    	SIGNAL agente.suma2,X$nAgente
                                                                                                    	SIGNAL agente.multiplica10,X$nAgente
                                                                                                    	SIGNAL agente.suma4,X$nAgente
                                                                                                    	move {name:Text1,TEXT:"SIGNAL Previo: P$(resultado,X$nAgente)"}
                                                                                                        advance 0
                                                                                                    	move {name:Text2,TEXT:"SIGNAL Resultado: P$(resultado,X$nAgente)"}
                                                                                                    	
                                                                                                    	ADVANCE 20,0 {TO:POS1}
                                                                                                        
                                                                                                    ENDGENERATE 1
                                                                                                    
                                                                                                    GENERATE 60,0,0,1 {NAME:GEN2,X:100,Y:400}
                                                                                                    
                                                                                                    	assign resultado,0,X$nAgente
                                                                                                    	SIGNALNOW agente.suma2,X$nAgente
                                                                                                    	SIGNALNOW agente.multiplica10,X$nAgente
                                                                                                    	SIGNALNOW agente.suma4,X$nAgente
                                                                                                    	move {name:Text3,TEXT:"SIGNALNOW Resultado: P$(resultado,X$nAgente)"}
                                                                                                    	
                                                                                                    	ADVANCE 20,60 {TO:POS2}
                                                                                                        
                                                                                                    ENDGENERATE 1
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    ;*******************************
                                                                                                    procedure agente.main
                                                                                                    	savevalue nAgente,D$N
                                                                                                        assign resultado,0
                                                                                                        REST RestroomAgentes
                                                                                                        terminate_ve
                                                                                                    endprocedure
                                                                                                    
                                                                                                    procedure agente.suma2
                                                                                                        assign resultado,P$resultado + 2
                                                                                                        	move {name:TextAgente,TEXT:"ResultadoSUMA2: P$resultado T= AC1$"}
                                                                                                        RETURN_RESTORE
                                                                                                    endprocedure
                                                                                                    procedure agente.suma4
                                                                                                        assign resultado,P$resultado + 4
                                                                                                        	move {name:TextAgente,TEXT:"ResultadoSUMA4: P$resultado T= AC1$"}
                                                                                                        RETURN_RESTORE
                                                                                                    endprocedure
                                                                                                    procedure agente.multiplica10
                                                                                                        assign resultado,P$resultado * 10
                                                                                                        move {name:TextAgente,TEXT:"Resultadomultiplica10: P$resultado T= AC1$"}
                                                                                                        RETURN_RESTORE
                                                                                                    endprocedure
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    
                                                                                                    • + Colas de mensajes

                                                                                                      Vamos a ver un código con una narrativa compleja pero cuya realización resulta natural en GPSS-Plus gracias a sus primitivas para agentes, listas de tareas y sincronización por eventos.

                                                                                                      El ejemplo simula un restaurante simplificado: los clientes llegan, ocupan una mesa, piden una comanda y esperan a ser servidos. Los cocineros (agentes) despiertan si estaban dormidos, toman una comanda de una lista compartida, y generan entidades "plato" para cada tarea. Cuando todos los platos de un cliente están listos, este puede empezar a comer.

                                                                                                      Toda la coordinación se resuelve con una lista FIFO de tareas (SAVEVALUE.push/pop) y un RESTROOM común que regula el sueño y despertar de los agentes.
                                                                                                      No se necesita SIGNAL, ni ON_QUEUE, ni ON_RELEASE y, sin embargo, todo fluye perfectamente.

                                                                                                      Este modelo muestra cómo los agentes pueden autogestionar su agenda sin interrupciones externas, simplemente leyendo una cola de trabajo compartida.

                                                                                                      Aquí, el RESTROOM funciona como una cerradura de sueño: el primero que se despierte atiende, los demás esperan.


                                                                                                      Example:
                                                                                                      /*
                                                                                                      
                                                                                                        	 	Colas de mensajes
                                                                                                       
                                                                                                      */
                                                                                                      SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                      START 1000
                                                                                                      
                                                                                                      Facility {NAME:mesas,X:221,Y:60,capacity:7}
                                                                                                      Restroom {NAME:esperaServir,X:323,Y:60}
                                                                                                      Restroom {NAME:agentesDormidos,X:348,Y:357}
                                                                                                      
                                                                                                      POSITION {NAME:COMIENDO, X:579, Y:46}
                                                                                                      
                                                                                                      GRAPHIC {NAME:INFOA_1, TYPE:TEXT, X:149, Y:296, TEXT:"Agente1 listo"}
                                                                                                      GRAPHIC {NAME:INFOB_1, TYPE:TEXT, X:147, Y:237, TEXT:"infoB1"}
                                                                                                      GRAPHIC {NAME:INFOC_1, TYPE:TEXT, X:147, Y:268, TEXT:"infoC1"}
                                                                                                      
                                                                                                      GRAPHIC {NAME:INFOA_2, TYPE:TEXT, X:577, Y:292, TEXT:"Agente2 listo"}
                                                                                                      GRAPHIC {NAME:INFOB_2, TYPE:TEXT, X:576, Y:238, TEXT:"infoB2"}
                                                                                                      GRAPHIC {NAME:INFOC_2, TYPE:TEXT, X:576, Y:266, TEXT:"infoC2"}
                                                                                                      
                                                                                                      Graphic {NAME:TextComanda,Type:TEXT,X:270,Y:132,Text:"Comanda"}
                                                                                                      Graphic {NAME:TextA,Type:TEXT,X:271,Y:102,Text:"Sin servir"}
                                                                                                      Graphic {NAME:TextB,Type:TEXT,X:469,Y:84,Text:"Comiendo"}
                                                                                                      
                                                                                                      ;--------------------------------
                                                                                                      
                                                                                                      ;		CLIENTES
                                                                                                      
                                                                                                      ;--------------------------------
                                                                                                      GENERATE 8,5,0,8 {NAME:Clientes, X:53, Y:61}
                                                                                                          ADVANCE 3 {TO:mesas,FLOW:1}
                                                                                                          seize mesas
                                                                                                          call anadir_comanda
                                                                                                          rest esperaServir
                                                                                                          ADVANCE 120,30 {FROM:esperaServir,TO:COMIENDO,FLOW:1} ; comiendo
                                                                                                          release mesas
                                                                                                          if (R$(mesas,IN)<=0)
                                                                                                      	    stop
                                                                                                          endif
                                                                                                      ENDGENERATE 1
                                                                                                      
                                                                                                      ;**********************************
                                                                                                      procedure anadir_comanda
                                                                                                      
                                                                                                          ASSIGN cHorno, FLOOR(RANDOM * 4)
                                                                                                          ASSIGN cFogon, FLOOR(RANDOM * 6)
                                                                                                          ASSIGN cFreidora, FLOOR(RANDOM * 3)
                                                                                                          
                                                                                                          ASSIGN nPlatosPendientes, P$cHorno + P$cFogon + P$cFreidora
                                                                                                      
                                                                                                          savevalue.push comandas, [1,D$N,P$cHorno,P$cFogon,P$cFreidora] ; Añade comanda
                                                                                                          MOVE {name:TextComanda,text:"Horno: P$cHorno ; Fogon: P$cFogon ; Freidora: P$cFreidora"}
                                                                                                          
                                                                                                      	; avisamos a cocina despertando a todos, el primero que llegue atenderá la comanda
                                                                                                          ; el segundo volverá a dormirse
                                                                                                          wake agentesDormidos
                                                                                                      
                                                                                                      ENDPROCEDURE
                                                                                                      ;--------------------------------
                                                                                                      
                                                                                                      ;		COCINEROS / AGENTES
                                                                                                      
                                                                                                      ;--------------------------------
                                                                                                      
                                                                                                      PROCEDURE PRE_RUN
                                                                                                      	timeout agente.main,0,1 ; agente 1
                                                                                                      	timeout agente.main,0,2 ; agente 2
                                                                                                      	TERMINATE_VE 
                                                                                                      ENDPROCEDURE
                                                                                                      
                                                                                                      
                                                                                                      procedure agente.main
                                                                                                          ; Asigna su nombre único como variable
                                                                                                          if (P$PARAM_A==1)
                                                                                                          savevalue nAgente1,D$N
                                                                                                          assign nAgente,1
                                                                                                          else
                                                                                                          savevalue nAgente2,D$N
                                                                                                          assign nAgente,2
                                                                                                          endif
                                                                                                          
                                                                                                          MOVE {NAME:"INFOA_P$nAgente", TEXT:"Agente activo P$PARAM_A"}
                                                                                                      	savevalue comandas, []
                                                                                                          assign tareaEnCurso,0
                                                                                                      
                                                                                                      	WHILE (1==1)
                                                                                                          	
                                                                                                              if (VD$(comandas,LENGTH)<=0)
                                                                                                              ; Si no hay tareas, duerme (espera que un cliente lo despierte)
                                                                                                              	MOVE {NAME:"INFOA_P$nAgente", TEXT:"T: AC1$ Cocinero P$nAgente : DURMIENDO...", color:red}
                                                                                                                  rest agentesDormidos
                                                                                                              endif
                                                                                                      		assign nTareas,VD$(comandas,LENGTH)
                                                                                                      		MOVE {NAME:"INFOA_P$nAgente", TEXT:"Cocinero: tengo P$nTareas tareas, Despierto", color:green}
                                                                                                        
                                                                                                          	savevalue.pop comandas,comanda
                                                                                                      		assign tareaEnCurso,P$(comanda.0)
                                                                                                      		assign nEntidad,P$(comanda.1)
                                                                                                      		assign cHorno,P$(comanda.2)
                                                                                                      		assign cFogon,P$(comanda.3)
                                                                                                      		assign cFreidora,P$(comanda.4)
                                                                                                        
                                                                                                      		if (P$tareaEnCurso > 0) 
                                                                                                      
                                                                                                              MOVE {NAME:"INFOB_P$nAgente", TEXT:"Tengo P$nTareas tareas, ejecuto la Cliente P$nEntidad"}
                                                                                                              MOVE {NAME:"INFOC_P$nAgente", TEXT:"Horno: P$cHorno ; Fogon: P$cFogon ; Freidora: P$cFreidora"}
                                                                                                      
                                                                                                      		assign contador,0
                                                                                                              while (P$contador Configuración de recursos y posiciones
                                                                                                      Facility {NAME:Horno,X:348,Y:566,capacity:2}
                                                                                                      Facility {NAME:Fogon,X:347,Y:499,capacity:4}
                                                                                                      Facility {NAME:Freidora,X:347,Y:437,capacity:3}
                                                                                                      
                                                                                                      Graphic {NAME:TextPlatos1,Type:TEXT,X:160,Y:579,Text:"Platos..."}
                                                                                                      Graphic {NAME:TextPlatos2,Type:TEXT,X:533,Y:578,Text:"Platos2..."}
                                                                                                      
                                                                                                      POSITION {NAME:FINCOCINA, X:649, Y:509}
                                                                                                      
                                                                                                      GENERATE 0,0,0,0,0 {NAME:platos, X:69, Y:508}
                                                                                                      move {name:TextPlatos1,text:"Realizando plato para P$PARAM_A tipo P$PARAM_B"}
                                                                                                      assign entidadDestino,P$PARAM_A
                                                                                                      assign tipoComanda,"P$PARAM_B"
                                                                                                      
                                                                                                      switch "P$tipoComanda"
                                                                                                      case ==,"Horno"
                                                                                                      	ADVANCE 4 {TO:Horno,FLOW:1,DECISION:COCINA}
                                                                                                          seize Horno
                                                                                                          ADVANCE 40,5
                                                                                                          release Horno
                                                                                                      endcase
                                                                                                      
                                                                                                      case ==,"Fogon"
                                                                                                      	ADVANCE 4 {TO:Fogon,FLOW:1,DECISION:COCINA}
                                                                                                          seize Fogon
                                                                                                          ADVANCE 30,5
                                                                                                          release Fogon
                                                                                                      endcase
                                                                                                      
                                                                                                      case ==,"Freidora"
                                                                                                      	ADVANCE 4 {TO:Freidora,FLOW:1,DECISION:COCINA}
                                                                                                          seize Freidora
                                                                                                          ADVANCE 22,3
                                                                                                          release Freidora
                                                                                                      endcase
                                                                                                      
                                                                                                      endswitch
                                                                                                      
                                                                                                      ADVANCE 5 {TO:FINCOCINA,FLOW:1,MERGE:COCINA}
                                                                                                      move {name:TextPlatos2,text:"Terminado plato para P$entidadDestino tipo P$tipoComanda"}
                                                                                                      
                                                                                                      assign nPlatosPendientes, P$(nPlatosPendientes,P$entidadDestino) - 1,P$entidadDestino
                                                                                                      
                                                                                                      ; si ya no hay más plazos que cocinar, empiezan a comer
                                                                                                      if (P$(nPlatosPendientes,P$entidadDestino)<=0)
                                                                                                      	wake esperaServir,0,P$entidadDestino
                                                                                                      endif
                                                                                                      
                                                                                                      ENDGENERATE 0
                                                                                                      
                                                                                                      
                                                                                                      
                                                                                                      
                                                                                                      
                                                                                                      • + Gestión de entidades

                                                                                                        En este capítulo exploramos cómo un agente puede encargarse de monitorizar el paso de entidades por una zona del modelo y calcular métricas globales como el tiempo total de uso o la media de permanencia.

                                                                                                        El agente no actúa directamente sobre las entidades, sino que escucha eventos generados automáticamente por un QUEUER (a través de sus ON_QUEUE y ON_DEPART), y en base a esos eventos, registra cuándo entran y salen las entidades.

                                                                                                        Para ello, el agente mantiene una estructura de datos (array asociativo) donde guarda los tiempos de entrada, y luego calcula las diferencias al salir. De esta manera, puede mantener un seguimiento completo sin interferir en el flujo lógico del resto del modelo.

                                                                                                        Este tipo de lógica es útil para implementar estadísticas personalizadas, auditoría de uso, alertas o cualquier control que dependa de eventos externos y del historial de entidades.


                                                                                                        Example:
                                                                                                        /*
                                                                                                        
                                                                                                          	 	Gestión de entidades
                                                                                                         
                                                                                                        */
                                                                                                        SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                        
                                                                                                        Restroom {NAME:RestroomAgentes,X:100,Y:121,visible:1}
                                                                                                        queuer {NAME:Queuer1,X:321,Y:123,on_queue:Queuer1on_queue,on_depart:Queuer1on_depart}
                                                                                                        
                                                                                                        
                                                                                                        POSITION {NAME:POS1,X:251,Y:335}
                                                                                                        POSITION {NAME:POS2,X:469,Y:341}
                                                                                                        
                                                                                                        
                                                                                                        Graphic {NAME:Text1,Type:TEXT,X:425,Y:514,Text:"Entidad"}
                                                                                                        Graphic {NAME:TextAgente1,Type:TEXT,X:433,Y:462,Text:"---"}
                                                                                                        Graphic {NAME:TextAgente2,Type:TEXT,X:433,Y:416,Text:"---"}
                                                                                                        
                                                                                                        
                                                                                                        initial nAgente,0
                                                                                                        START 500
                                                                                                        
                                                                                                        ;*****************************************************
                                                                                                        
                                                                                                        PROCEDURE PRE_RUN
                                                                                                        	timeout agente.main,0
                                                                                                        	TERMINATE_VE
                                                                                                        ENDPROCEDURE 1
                                                                                                        
                                                                                                        ;*****************************************************
                                                                                                        GENERATE 10,0 {NAME:GEN1,X:43,Y:300}
                                                                                                        
                                                                                                        	move {name:Text1,TEXT:"Entidad Resultado P$(resultado,X$nAgente) AC1$"}
                                                                                                        	
                                                                                                        	ADVANCE 20,0 {TO:POS1}
                                                                                                        	queue Queuer1
                                                                                                        	ADVANCE 20,60 {TO:POS2}
                                                                                                            depart Queuer1
                                                                                                            
                                                                                                        ENDGENERATE 1
                                                                                                        
                                                                                                        ;*******************************
                                                                                                        procedure agente.main
                                                                                                        	savevalue nAgente,D$N
                                                                                                            assign datos,{}
                                                                                                            assign resultado,0
                                                                                                            assign media,0
                                                                                                            assign nEntidades,0
                                                                                                            assign tiempoTotal,0
                                                                                                            while (1==1)
                                                                                                            REST RestroomAgentes
                                                                                                            endwhile
                                                                                                            terminate_ve
                                                                                                        endprocedure
                                                                                                        ;**********************************
                                                                                                        procedure agente.IN
                                                                                                            assign datos.entidad_P$PARAM_A,P$PARAM_B
                                                                                                            move {name:TextAgente1,TEXT:"IN Entidad P$PARAM_A : Tiempo Inicio: P$(datos.entidad_P$PARAM_A)"}
                                                                                                            RETURN_RESTORE
                                                                                                        endprocedure
                                                                                                        ;**********************************
                                                                                                        
                                                                                                        procedure agente.OUT
                                                                                                        	assign nEntidades,P$nEntidades + 1
                                                                                                        	assign tiempoInicio,P$(datos.entidad_P$PARAM_A)
                                                                                                        	assign tiempoFin,P$PARAM_B
                                                                                                            assign tiempoTotal,P$tiempoTotal + P$tiempoFin - P$tiempoInicio
                                                                                                            assign media,P$tiempoTotal / P$nEntidades
                                                                                                            move {name:TextAgente2,TEXT:"OUT Entidad: P$PARAM_A Tiempo Total: P$tiempoTotal Media: P$media"}
                                                                                                            ASSIGN.DELETE datos,entidad_P$PARAM_A ; eliminamos la entrada de la lista
                                                                                                            RETURN_RESTORE
                                                                                                        endprocedure
                                                                                                        
                                                                                                        procedure Queuer1on_queue
                                                                                                        	SIGNAL agente.IN,X$nAgente,P$ENTITYNUMBER,AC1$
                                                                                                            TERMINATE_VE
                                                                                                        endprocedure
                                                                                                        
                                                                                                        procedure Queuer1on_depart
                                                                                                            SIGNAL agente.OUT,X$nAgente,P$ENTITYNUMBER,AC1$
                                                                                                        	TERMINATE_VE
                                                                                                        endprocedure
                                                                                                        
                                                                                                        
                                                                                                        • + Gestor de recursos

                                                                                                          En este capítulo presentamos un agente que actúa como gestor inteligente de un recurso compartido: un semáforo.

                                                                                                          El objetivo es permitir el paso de entidades desde distintos orígenes hacia distintos destinos, pero solo uno a la vez, de forma controlada. Se simula así un cruce en el que el semáforo cambia de dirección cada cierto tiempo, desbloqueando una de las cuatro posibles trayectorias.

                                                                                                          Estructura general:

                                                                                                          • Un INITIAL define la tabla tramos, que contiene la información de cada dirección (nombres de posiciones, holders, colores, etc.).

                                                                                                          • Las entidades que circulan pasan por un HOLDER si su dirección no está activa, y continúan si está permitida.

                                                                                                          • El agente agenteSemaforo.main se encarga de:

                                                                                                            • Apagar el semáforo actual (red)

                                                                                                            • Elegir aleatoriamente el siguiente (green)

                                                                                                            • Actualizar el texto y los colores

                                                                                                            • Hacer UNHOLD a las entidades en espera para ese tramo

                                                                                                          Aprendizaje clave:
                                                                                                          El agente actúa como planificador visual y operativo, y permite entender cómo un loop de trabajo constante puede gobernar múltiples entidades, simplemente ajustando cuándo se permite continuar y cuándo no.
                                                                                                          Este patrón es útil para gestionar accesos a zonas exclusivas, turnos de paso, recursos únicos o flujos alternantes.


                                                                                                          Example:
                                                                                                          /*
                                                                                                          
                                                                                                            	 	Gestor de recursos
                                                                                                           
                                                                                                          */
                                                                                                          SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                          
                                                                                                          INITIAL tramos,  [
                                                                                                              { tiempo: 10, color: "#009900", nInicio: "InicioN", nFin: "FinN", nCentro: "CentroN", nRestroom: "RestroomN", nSemaforo: "SemaforoN", nTexto: "TextN" },
                                                                                                              { tiempo: 20, color: "#FF0099", nInicio: "InicioS", nFin: "FinS", nCentro: "CentroS", nRestroom: "RestroomS", nSemaforo: "SemaforoS", nTexto: "TextS" },
                                                                                                              { tiempo: 30, color: "#000099", nInicio: "InicioE", nFin: "FinE", nCentro: "CentroE", nRestroom: "RestroomE", nSemaforo: "SemaforoE", nTexto: "TextE" },
                                                                                                              { tiempo: 40, color: "#0099FF", nInicio: "InicioO", nFin: "FinO", nCentro: "CentroO", nRestroom: "RestroomO", nSemaforo: "SemaforoO", nTexto: "TextO" }
                                                                                                            ]
                                                                                                            
                                                                                                          
                                                                                                          POSITION {NAME:InicioN,X:368,Y:541}
                                                                                                          POSITION {NAME:InicioS,X:393,Y:30}
                                                                                                          POSITION {NAME:InicioE,X:756,Y:270}
                                                                                                          POSITION {NAME:InicioO,X:41,Y:269}
                                                                                                          
                                                                                                          POSITION {NAME:FinN,X:398,Y:540}
                                                                                                          POSITION {NAME:FinS,X:416,Y:31}
                                                                                                          POSITION {NAME:FinE,X:754,Y:298}
                                                                                                          POSITION {NAME:FinO,X:40,Y:301}
                                                                                                          
                                                                                                          POSITION {NAME:CentroN,X:381,Y:347}
                                                                                                          POSITION {NAME:CentroS,X:393,Y:249}
                                                                                                          POSITION {NAME:CentroE,X:447,Y:294}
                                                                                                          POSITION {NAME:CentroO,X:325,Y:290}
                                                                                                          
                                                                                                          Graphic {NAME:SemaforoN,TYPE:ARC,X:413,Y:346,FCOLOR:"red",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}
                                                                                                          Graphic {NAME:SemaforoS,TYPE:ARC,X:363,Y:246,FCOLOR:"red",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}
                                                                                                          Graphic {NAME:SemaforoE,TYPE:ARC,X:453,Y:263,FCOLOR:"red",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}
                                                                                                          Graphic {NAME:SemaforoO,TYPE:ARC,X:326,Y:320,FCOLOR:red,RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}
                                                                                                          
                                                                                                          Restroom {NAME:RestroomN,X:713,Y:570,on_rest:actualiza}
                                                                                                          Restroom {NAME:RestroomS,X:713,Y:473,on_rest:actualiza}
                                                                                                          Restroom {NAME:RestroomE,X:713,Y:521,on_rest:actualiza}
                                                                                                          Restroom {NAME:RestroomO,X:713,Y:422,on_rest:actualiza}
                                                                                                          
                                                                                                          Graphic {NAME:TextN,Type:TEXT,X:412,Y:347,Text:"N",font:"18px",color:#ffffff}
                                                                                                          Graphic {NAME:TextS,Type:TEXT,X:363,Y:247,Text:"S",font:"18px",color:#ffffff}
                                                                                                          Graphic {NAME:TextE,Type:TEXT,X:453,Y:263,Text:"E",font:"18px",color:#ffffff}
                                                                                                          Graphic {NAME:TextO,Type:TEXT,X:326,Y:320,Text:"O",font:"18px",color:#ffffff}
                                                                                                          
                                                                                                          Graphic {NAME:textSem,Type:TEXT,X:165,Y:381,Text:"Semaforo",font:"18px",color:#000000}
                                                                                                          
                                                                                                          START 3000
                                                                                                          
                                                                                                          ;----------------------------- PROCEDURE PRINCIPAL DE MOVIMIENTO
                                                                                                          procedure toSemaforo ; P$(PARAM_A) = origen, P$PARAM_B = destino
                                                                                                          
                                                                                                          ADVANCE X$(tramos.P$(PARAM_A).tiempo),10 {from:X$(tramos.P$(PARAM_A).nInicio), to:X$(tramos.P$PARAM_A.nCentro)}
                                                                                                          
                                                                                                          if (X$semaforoAbierto!=P$PARAM_A)
                                                                                                              rest X$(tramos.P$(PARAM_A).nRestroom)
                                                                                                          endif
                                                                                                          
                                                                                                          ADVANCE 10,3 {to:X$(tramos.P$PARAM_B.nCentro)}
                                                                                                          ADVANCE X$(tramos.P$(PARAM_B).tiempo),10 {to:X$(tramos.P$(PARAM_B).nFin)}
                                                                                                          
                                                                                                          endprocedure
                                                                                                          
                                                                                                          ;----------------------------- AGENTE SEMÁFORO
                                                                                                          procedure agenteSemaforo.main
                                                                                                          
                                                                                                          savevalue semaforoAbierto,0
                                                                                                          
                                                                                                          while (1==1)
                                                                                                              savevalue semaforoAbiertoAnterior, X$semaforoAbierto
                                                                                                              move {name:X$(tramos.X$(semaforoAbierto).nSemaforo),FCOLOR:yellow}
                                                                                                              
                                                                                                              savevalue semaforoAbierto,-1
                                                                                                              advance 50
                                                                                                          
                                                                                                              call calcularDestino,X$semaforoAbiertoAnterior
                                                                                                          
                                                                                                              move {name:X$(tramos.X$(semaforoAbiertoAnterior).nSemaforo),FCOLOR:red}
                                                                                                              savevalue semaforoAbierto,P$calcularDestino
                                                                                                              move {name:textSem,text:"Semaforo Abierto: X$semaforoAbierto"}
                                                                                                               
                                                                                                              move {name:X$(tramos.X$(semaforoAbierto).nSemaforo),FCOLOR:green}
                                                                                                              wake X$(tramos.X$(semaforoAbierto).nRestroom)
                                                                                                          
                                                                                                              advance 200
                                                                                                          endwhile
                                                                                                          
                                                                                                          endprocedure
                                                                                                          
                                                                                                          ;----------------------------- PROCEDURE PARA CAMBIAR DESTINO
                                                                                                          procedure calcularDestino ; P$PARAM_A origen
                                                                                                          assign tmp, (P$PARAM_A + 1 + floor(random*3)) % 4 
                                                                                                          endprocedure P$tmp
                                                                                                          
                                                                                                          ;----------------------------- ACTUALIZACIÓN DE VALORES EN PANTALLA
                                                                                                          procedure actualiza
                                                                                                          
                                                                                                          move {name:X$(tramos.0.nTexto),text:"R$(X$(tramos.0.nRestroom),IN)"}
                                                                                                          move {name:X$(tramos.1.nTexto),text:"R$(X$(tramos.1.nRestroom),IN)"}
                                                                                                          move {name:X$(tramos.2.nTexto),text:"R$(X$(tramos.2.nRestroom),IN)"}
                                                                                                          move {name:X$(tramos.3.nTexto),text:"R$(X$(tramos.3.nRestroom),IN)"}
                                                                                                          
                                                                                                          terminate
                                                                                                          
                                                                                                          endprocedure
                                                                                                          
                                                                                                          ;----------------------------- GENERADOR ÚNICO
                                                                                                          
                                                                                                          GENERATE 10,5 {NAME:GEN,X:577,Y:575,ERADIO:10}
                                                                                                          
                                                                                                          assign origen,floor(random*4)
                                                                                                          mod {color:X$(tramos.P$origen.color)}
                                                                                                          
                                                                                                          call calcularDestino,P$origen
                                                                                                          call toSemaforo,P$origen,P$calcularDestino
                                                                                                          
                                                                                                          ENDGENERATE 1
                                                                                                          
                                                                                                          ;----------------------------- ARRANQUE DEL AGENTE
                                                                                                          
                                                                                                          PROCEDURE PRE_RUN
                                                                                                              TIMEOUT agenteSemaforo.main, 0
                                                                                                              TERMINATE_VE
                                                                                                          ENDPROCEDURE
                                                                                                          
                                                                                                          
                                                                                                          
                                                                                                          • + Entidad Componente

                                                                                                            En un motor de simulación por eventos discreto, una entidad suele ser un único hilo de ejecución: un flujo secuencial que avanza bloqueándose y reanudándose según el modelo.

                                                                                                            Este enfoque funciona, pero no coincide con el comportamiento real de muchas situaciones.

                                                                                                            Una persona que espera en la cola del autobús no solo espera:
                                                                                                            respira, consulta el móvil, piensa, se distrae y puede cambiar de opinión.

                                                                                                            Un vehículo que se desplaza también actúa en paralelo:
                                                                                                            consume energía, se desgasta, recibe señales, ajusta su ruta.

                                                                                                            Modelar todo esto con un único flujo lineal es una simplificación excesiva.

                                                                                                            Entidades componente: simultaneidad dentro de la entidad

                                                                                                            Por lo tanto, vamos a ampliar la forma en la que podemos ver una entidad para que pueda tener varias funciones en paralelo.

                                                                                                            Una entidad componente es una entidad virtual que ejecuta una función en nombre de otra entidad principal.
                                                                                                            Su existencia depende de la entidad principal, pero tiene su propio flujo de ejecución.

                                                                                                            Esto permite que una entidad principal tenga varios comportamientos activos simultáneamente, cada uno representado por una entidad componente independiente.

                                                                                                            Características clave:

                                                                                                            • La entidad componente se crea cuando nace la entidad principal.

                                                                                                            • Ejecuta su lógica en paralelo, sin bloquear el flujo principal.

                                                                                                            • Puede leer y modificar atributos de la entidad principal.

                                                                                                            • Puede despertarla, cambiar su estado o provocar decisiones.

                                                                                                            • Finaliza automáticamente cuando la entidad principal deja de existir.

                                                                                                            No es un “hijo” en sentido POO, ni un objeto, ni una propiedad.
                                                                                                            Es un comportamiento vivo, modelado como entidad.

                                                                                                            La cola de eventos sigue siendo estrictamente secuencial, pero ahora la entidad deja de ser monolítica: ya no representa un único flujo sino un pequeño sistema compuesto por varias entidades cooperando y modelando más profundamente a la entidad principal.

                                                                                                            Esto permite modelar fenómenos naturales de simultaneidad sin recurrir a hilos, semáforos ni programación externa.

                                                                                                            Ciclo de vida de una entidad componente

                                                                                                            El funcionamiento es sencillo y uniforme:

                                                                                                            1. La entidad principal nace.

                                                                                                            2. Inmediatamente se crea una entidad componente indicandole el número de la entidad principal.

                                                                                                            3. La entidad componente ejecuta su lógica en bucle WHILE:
                                                                                                              D$(EXIST,P$idEntidadPrincipal) == 1

                                                                                                            4. Puede influir en la entidad principal asignándole valores, modificando su estado o despertándola.

                                                                                                            5. Cuando la entidad principal desaparece, el componente termina automáticamente.

                                                                                                            Así se definen comportamientos paralelos naturales, como respiración, desgaste, decisiones internas, sensores activos o distracciones.

                                                                                                            En el ejemplo vemos a personas que llegan a una parada de autobús muy simplificado. Cuando llegan a 6, simplemente se van y mientras se encuentran en REST fuera de la cola de eventos.

                                                                                                            Pero las entidades también están leyendo el móvil y algunos pueden decidir dejar la cola e irse con urgencia.

                                                                                                            Cuando la entidad componente decide que hay que abandonar la cola, ejecuta lo necesario mientras la principal está aun dormida en el RESTROOM.

                                                                                                             


                                                                                                            Example:
                                                                                                            /*
                                                                                                            
                                                                                                            Componente
                                                                                                            
                                                                                                            */
                                                                                                            SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                            ;SYSTEM {TYPE:OPTIONS,Speed:8}
                                                                                                            
                                                                                                            Restroom {NAME:Restroom_autobus,X:333,Y:282}
                                                                                                            POSITION {NAME:SALIDA_URGENTE,X:335,Y:119}
                                                                                                            POSITION {NAME:SALIDA,X:615,Y:286,type:terminate,title:end}
                                                                                                            
                                                                                                            Graphic {NAME:textAgente,Type:TEXT,X:494,Y:336,Text:"Grupo"}
                                                                                                            Graphic {NAME:Text2,Type:TEXT,X:331,Y:76,Text:"Urgencia"}
                                                                                                            
                                                                                                            START 500
                                                                                                            ;*****************************************************
                                                                                                            PROCEDURE PRE_RUN
                                                                                                            	timeout verifica_cola,1
                                                                                                            	TERMINATE_VE 
                                                                                                            ENDPROCEDURE
                                                                                                            
                                                                                                            ;*****************************************************
                                                                                                            GENERATE 10,0,0,0 {NAME:GEN1,X:61,Y:288}
                                                                                                            
                                                                                                            ASSIGN salidaUrgente,0
                                                                                                            timeout componente_mirar_movil,0,D$N
                                                                                                            ADVANCE 20,0 {TO:Restroom_autobus}
                                                                                                            REST Restroom_autobus
                                                                                                            
                                                                                                            if (P$salidaUrgente == 1)
                                                                                                            ADVANCE 20,10 {TO:SALIDA_URGENTE}
                                                                                                            else
                                                                                                            ADVANCE 20,10 {TO:SALIDA}
                                                                                                            endif
                                                                                                            ENDGENERATE 1
                                                                                                            
                                                                                                            ;*******************************
                                                                                                            procedure verifica_cola
                                                                                                                while (1==1)
                                                                                                                    advance 5
                                                                                                                    if (R$(Restroom_autobus,OCCUPIED) > 5)
                                                                                                                    move {name:textAgente,text:"Liberando [AC1: AC1$]"}
                                                                                                                    WAKE Restroom_autobus ; todos salen
                                                                                                                    endif
                                                                                                                endwhile
                                                                                                            endprocedure
                                                                                                            ;**********************************
                                                                                                            
                                                                                                            procedure componente_mirar_movil
                                                                                                            	assign miEntidad,P$PARAM_A
                                                                                                                while (D$(EXIST,P$miEntidad)==1)
                                                                                                                    advance 5
                                                                                                                    if (R$(Restroom_autobus,IS_OCCUPIED_BY,P$miEntidad) == 1 && RANDOM < 0.02)
                                                                                                                    move {name:Text2,text:"Liberando la entidad P$miEntidad [AC1: AC1$]"}
                                                                                                                    assign salidaUrgente,1,P$miEntidad
                                                                                                                    mod {number:P$miEntidad, color:red}
                                                                                                                    WAKE Restroom_autobus,-1,P$miEntidad ; sale solo esta entidad
                                                                                                                    endif
                                                                                                                endwhile
                                                                                                                terminate_ve
                                                                                                            endprocedure
                                                                                                            ;**********************************
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                            
                                                                                                          • + Season 7: Contextos (CX$) y módulos (.mod)
                                                                                                            • + Creación de recursos dinámicos 1

                                                                                                              Antes de comenzar a ver los contextos debemos tener en cuenta cierta característica de GPSS-Plus.

                                                                                                              En general, los motores basados en Drag&drop los recursos se arrastran fácilmente sobre el canvas y ya están listos para usar. En GPSS-Plus hemos visto que es diferente, hay que escribir el código del COMANDO.

                                                                                                              Por contra, esa característica de los Drag&drop hace muy complicada la creación en tiempo de ejecución de un recurso. No estamos ahí para arrastrarlo al canvas. En GPSS-Plus basta con cambiar el COMANDO FACILITY por el BLOQUE NEWFACILITY.

                                                                                                              Solo se exige que esté creado antes de ser usado y para eso se suele hacer con el PRE_RUN.


                                                                                                              Example:
                                                                                                              /*
                                                                                                              
                                                                                                              Contextos y módulos. Creación dinámica 1
                                                                                                              
                                                                                                              
                                                                                                              */
                                                                                                              SYSTEM {TYPE:OPTIONS, SPEED:5}
                                                                                                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                              Position {NAME:pos_exit,X:581,Y:407}
                                                                                                              
                                                                                                              ; --- Facility definido de forma clásica ---
                                                                                                              FACILITY {NAME:resource_static, CAPACITY:1,  X:496, Y:164}
                                                                                                              
                                                                                                              START 200
                                                                                                              
                                                                                                              PROCEDURE PRE_RUN
                                                                                                                  ; Crear un segundo facility dinámico en el arranque
                                                                                                                  NEWFACILITY {NAME:resource_dynamic, CAPACITY:1,  X:300, Y:400}
                                                                                                                  TERMINATE_VE
                                                                                                              ENDPROCEDURE
                                                                                                              
                                                                                                              ;------------------------------------------------------
                                                                                                              ; --- Flujo de entidades ---
                                                                                                              GENERATE 10,0 {NAME:GEN1}     ; cada 10 ticks una entidad
                                                                                                              advance 15 {to:resource_static}
                                                                                                              
                                                                                                              SEIZE resource_static      
                                                                                                              ADVANCE 15        
                                                                                                              RELEASE resource_static
                                                                                                              
                                                                                                              advance 15 {to:resource_dynamic}
                                                                                                              
                                                                                                              SEIZE resource_dynamic    
                                                                                                              ADVANCE 17          
                                                                                                              RELEASE resource_dynamic
                                                                                                              
                                                                                                              advance 15 {to:pos_exit}
                                                                                                              
                                                                                                              TERMINATE 1
                                                                                                              
                                                                                                              
                                                                                                              
                                                                                                              
                                                                                                              
                                                                                                              • + Creación de recursos dinámicos 2

                                                                                                                Ahora que ya sabemos que se pueden crear en runtime, veamos su utilidad... la más evidente, la de crear multitud con un iterador.

                                                                                                                Solo debemos tener en cuenta qué nombre les hemos puesto para poder usarlos.

                                                                                                                En GPSS‑Plus, los nombres de recursos son strings reales, por lo que pueden construirse dinámicamente y usarse directamente en SEIZE, RELEASE, R$(), etc.


                                                                                                                Example:
                                                                                                                SYSTEM {TYPE:OPTIONS, SPEED:5}
                                                                                                                SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                Position {NAME:POS1,X:147,Y:311}
                                                                                                                Position {NAME:POS2,X:542,Y:327}
                                                                                                                Position {NAME:POS3,X:756,Y:322}
                                                                                                                
                                                                                                                
                                                                                                                Graphic {NAME:Text1,Type:TEXT,X:305,Y:571,Text:"Hello"}
                                                                                                                
                                                                                                                ; --- Facility definido de forma clásica ---
                                                                                                                FACILITY {NAME:f_static, CAPACITY:4, X:657, Y:325,color:blue}
                                                                                                                
                                                                                                                START 200
                                                                                                                
                                                                                                                PROCEDURE PRE_RUN
                                                                                                                    ; Crear un segundo facility dinámico en el arranque
                                                                                                                	assign t,0
                                                                                                                    
                                                                                                                    while (P$t < 5) 
                                                                                                                        assign t,P$t+1
                                                                                                                        assign pos_y,(P$t * 80 + 20)
                                                                                                                        assign nombre,"f_dynamic_P$t"
                                                                                                                        NEWFACILITY {NAME:P$nombre, CAPACITY:1, X:300, Y:(P$pos_y)}
                                                                                                                        NEWPOSITION {NAME:pos_P$nombre, X:200, Y:(P$pos_y)}
                                                                                                                    endwhile
                                                                                                                   
                                                                                                                    TERMINATE_VE
                                                                                                                ENDPROCEDURE
                                                                                                                
                                                                                                                ;------------------------------------------------------
                                                                                                                ; --- Flujo de entidades ---
                                                                                                                GENERATE 6,0 {NAME:GEN1,x:58,y:310} 
                                                                                                                
                                                                                                                advance 15,0 {to:POS1,flow:1}
                                                                                                                
                                                                                                                ASSIGN MINKEY,0 
                                                                                                                ASSIGN MINVAL,100000
                                                                                                                
                                                                                                                assign t,0
                                                                                                                while (P$t < 5) 
                                                                                                                    assign t,P$t+1
                                                                                                                    assign nombre,"f_dynamic_P$t"
                                                                                                                    if (R$(P$nombre,QUEUE) < P$MINVAL)
                                                                                                                        ASSIGN MINKEY,P$t 
                                                                                                                		ASSIGN MINVAL,R$(P$nombre,QUEUE)
                                                                                                                    endif
                                                                                                                endwhile
                                                                                                                
                                                                                                                assign siguiente_recurso,"f_dynamic_P$MINKEY"
                                                                                                                
                                                                                                                move {name:Text1,text:"Consulta entidad D$N: P$(siguiente_recurso) Cola: R$(P$siguiente_recurso,QUEUE)"}
                                                                                                                
                                                                                                                
                                                                                                                
                                                                                                                
                                                                                                                advance 15,0 {to:"pos_P$siguiente_recurso",flow:1,DECISION:"inicio"}
                                                                                                                advance 15,0 {to:"P$siguiente_recurso",flow:1}
                                                                                                                
                                                                                                                
                                                                                                                SEIZE P$siguiente_recurso   
                                                                                                                ADVANCE 25,15   
                                                                                                                RELEASE P$siguiente_recurso
                                                                                                                
                                                                                                                advance 15 {to:POS2,flow:1,MERGE:"salida"}
                                                                                                                
                                                                                                                advance 15 {to:f_static,flow:1}
                                                                                                                
                                                                                                                SEIZE f_static  
                                                                                                                ADVANCE 7,8        
                                                                                                                RELEASE f_static
                                                                                                                
                                                                                                                advance 15 {to:POS3,flow:1}
                                                                                                                
                                                                                                                TERMINATE 1
                                                                                                                
                                                                                                                
                                                                                                                
                                                                                                                
                                                                                                                • + Creación de recursos dinámicos 3

                                                                                                                  Y por último, también se pueden crear si surge la necesidad.

                                                                                                                  Solo hay que tener en cuenta que un recurso no se puede modificar, si se crea, solo puede bloquearse con el bloque LOCK.


                                                                                                                  Example:
                                                                                                                  /*
                                                                                                                  
                                                                                                                  Contextos y módulos. Creación dinámica 3
                                                                                                                  
                                                                                                                  
                                                                                                                  */
                                                                                                                  SYSTEM {TYPE:OPTIONS, SPEED:5}
                                                                                                                  Position {NAME:pos_exit,X:581,Y:407}
                                                                                                                  
                                                                                                                  ; --- Facility definido de forma clásica ---
                                                                                                                  FACILITY {NAME:resource_static, CAPACITY:1,  X:496, Y:164}
                                                                                                                  
                                                                                                                  START 200
                                                                                                                  
                                                                                                                  
                                                                                                                  ;------------------------------------------------------
                                                                                                                  ; --- Flujo de entidades ---
                                                                                                                  GENERATE 10,0 {NAME:GEN1}
                                                                                                                  advance 15 {to:resource_static}
                                                                                                                  
                                                                                                                  SEIZE resource_static      
                                                                                                                  ADVANCE 15        
                                                                                                                  RELEASE resource_static
                                                                                                                  
                                                                                                                  if (R$(resource_dynamic,EXIST)==0)
                                                                                                                  	NEWFACILITY {NAME:resource_dynamic, CAPACITY:1,  X:300, Y:400}
                                                                                                                  endif
                                                                                                                  
                                                                                                                  advance 15 {to:resource_dynamic}
                                                                                                                  
                                                                                                                  SEIZE resource_dynamic    
                                                                                                                  ADVANCE 17          
                                                                                                                  RELEASE resource_dynamic
                                                                                                                  
                                                                                                                  advance 15 {to:pos_exit}
                                                                                                                  
                                                                                                                  TERMINATE 1
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  
                                                                                                                  • + Reutilización del código. CX$

                                                                                                                    Seguro que a estas alturas te habrás dado cuenta de lo que puede suponer hacer una lógica que simule un taller completo.
                                                                                                                    Y nos podemos volver locos si nos dicen que son dos talleres. Y luego 10.

                                                                                                                    Parece que ahí deberíamos abandonar, pero no, para eso están los contextos de GPSS-Plus.
                                                                                                                    No son clases porque en este entorno de eventos discretos no tiene sentido como tal, pero veremos que se les parecen y mucho. Es más un espacio de nombres y aquí todo es mucho más simple.

                                                                                                                    Imaginemos un caso algo más trivial, un gráfico de un nivel de un depósito.
                                                                                                                    Ponemos las líneas, los colores y el texto y después queremos usarlo.
                                                                                                                    Ahí empiezan los problemas, que ese objeto, clase, dibujo, elemento o el nombre que quieras darle tiene tan incrustados los parámetros que resulta abrumador trabajar con él.
                                                                                                                    Y no solo eso, sino que si tenemos dos depósitos, el trabajo no es solo el doble sino que hay que ir con mucho cuidado de no pisar la variables y crear tantas LINE como sean necesarias.

                                                                                                                    Este es el problema que vamos a resolver.

                                                                                                                    Por analogía se parecerá a decir que es a través de clases con métodos, variables locales e instanciaciones, pero no es así. Se resolverá con una simple variable tipo string.

                                                                                                                    Hasta ahora, para mover un gráfico podríamos hacer:

                                                                                                                    CALL grafico_set,75

                                                                                                                    Y si nuestro procedure estaba correcto, redibujaría cierto gráfico para mostrar ese 75% de llenado.
                                                                                                                    Gráficos que previamente debíamos haber creado.

                                                                                                                    Ahora vamos a hacerlo diferente y de tal forma que todo lo que vamos a ver se podrá llevar a otro fichero que no nos moleste más y llamarlo con un INCLUDE.

                                                                                                                    Lo primero: crear un método descriptivo

                                                                                                                    Si nuestro objeto es un depósito, crearemos un procedure llamado:

                                                                                                                    PROCEDURE deposito.init

                                                                                                                    Este procedure va a ser invocado, normalmente, por una EV desde el PRE_RUN / CALL y se va a encargar de crear todo el gráfico en lugar de con COMANDOS GRAPHIC, con bloques NEWGRAPHIC.

                                                                                                                    Como todas las llamadas, podrán tener sus parámetros para ser ejecutado, por ejemplo:

                                                                                                                    call deposito.init, 20, 130,120, 100,"Carga","#ff0000"

                                                                                                                    ...donde deposito.init es el procedure y el resto son: ancho, alto, X, Y, nombre y color. También se puede hacer más cómodamente definiendo previamente un objeto con todas las características.

                                                                                                                    Y con esto podemos empezar a pintar nuestro depósito en base a esos parámetros:

                                                                                                                    NEWGRAPHIC { NAME:TextoNivel, GROUP:Deposito, TYPE:TEXT, X:0, Y:0, TEXT:"Nivel: 0% ", COLOR:#000000 }

                                                                                                                    Y así todo lo que queramos añadir a nuestro gráfico.

                                                                                                                    Después, solo tenemos que añadir algún método para gestionarlo:

                                                                                                                    PROCEDURE deposito.move
                                                                                                                        MOVE {NAME:Deposito, x:P$PARAM_A, y:P$PARAM_B}
                                                                                                                    ENDPROCEDURE

                                                                                                                     

                                                                                                                    Con esto tendremos un gráfico... ¡pero solo uno!

                                                                                                                     

                                                                                                                    Ese es el problema.

                                                                                                                    La solución viene de algo que hemos dicho referente al punto de separación en el nombre del procedure.

                                                                                                                    Para llamar a este procedure init o locate podemos llamarlo de tantas formas como instanciaciones queramos:

                                                                                                                    CALL aaa.deposito.init, 20, 130,120, 100,"Carga","#ff0000"
                                                                                                                    CALL bbb.deposito.init, 20, 80,420, 100,"Nivel","#ffff00"
                                                                                                                    CALL ccc.deposito.init, 20, 180,320, 100,"Nivel","#ff00ff"
                                                                                                                    ...
                                                                                                                    CALL aaa.deposito.locate,100,200
                                                                                                                    CALL bbb.deposito.locate,200,100
                                                                                                                    CALL ccc.deposito.locate,300,200
                                                                                                                    

                                                                                                                    Lo que hacemos es ponerle otro punto de separación al nombre de la instancia de ese objeto.

                                                                                                                    Y a partir de aquí debemos introducir el punto crítico: el valor del string de contexto CX$

                                                                                                                    Llamar a este procedure "aaa.deposito.locate" significa que:

                                                                                                                    1. El nombre real del procedure es "deposito.locate"

                                                                                                                    2. El contexto (CX$) es el string "aaa"

                                                                                                                    Y hay que tenerlo muy claro y muy presente.

                                                                                                                    Cuando entre una entidad en un PROCEDURE cuya llamada esté hecha con puntos de separación, se ejecutará la actividad bajo ese valor de CX$.

                                                                                                                    Y con esto es suficiente para no pisar variables ni recursos entre unas instancias y otras.

                                                                                                                    Por ejemplo:

                                                                                                                    SAVEVALUE CX$_altura , 100

                                                                                                                    ...realmente habrá creado:

                                                                                                                    SAVEVALUE aaa_altura , 100

                                                                                                                    O:

                                                                                                                    NEWGRAPHIC {NAME:CX$_Deposito, TYPE:GROUP, X:0, Y:0}

                                                                                                                    ...será realmente:

                                                                                                                    NEWGRAPHIC {NAME:aaa_Deposito, TYPE:GROUP, X:0, Y:0}

                                                                                                                    Por lo tanto, se habrán generado tantos SAVEVALUE, ASSIGN,NEWGRAPHIC, NEWRESTROOM como formas de llamar a los init de cada clase.

                                                                                                                    Así que solo tendremos que renombrar nuestros ASSIGNs internos y SAVEVALUEs y NAMEs.... añadiéndoles delante "CX$_"
                                                                                                                    Y cuando queramos acceder a esos mismos parámetros deberemos hacerlo usando paréntesis para seguir evitando un anidamiento indeseado.

                                                                                                                    Por ejemplo:

                                                                                                                    savevalue CX$_ALTO, 100
                                                                                                                    

                                                                                                                    ...se recupera con el SNA:

                                                                                                                    X$(CX$_ALTO)

                                                                                                                    Y con esto, podremos crear y mover tantas instancias de depósitos como queramos

                                                                                                                    Todo este material lo introducimos en un fichero separado y lo llamamos desde un include.

                                                                                                                     

                                                                                                                    Resumen de conceptos clave de esta técnica:

                                                                                                                    La separación con puntos en un CALL del tipo aaa.bbb.ccc se interpreta así:

                                                                                                                    - El contexto (CX$) es "aaa"

                                                                                                                    - El nombre real del procedure ejecutado es bbb.ccc

                                                                                                                    - CX$ es un SNA clave que representa el contexto de llamada actual y puede usarse para construir nombres dinámicos.

                                                                                                                    - Las variables (ASSIGN o SAVEVALUE) se pisan si no están dentro del contexto. Usa siempre:

                                                                                                                    ASSIGN CX$_nombre, valor
                                                                                                                    

                                                                                                                    ...para evitar colisiones entre instancias.

                                                                                                                    - Si el método necesita devolver un valor (como un "get"), puedes hacer:

                                                                                                                    CALL aaa.bbb.get
                                                                                                                    

                                                                                                                    ...y luego obtener el retorno con:

                                                                                                                    P$(get)
                                                                                                                    

                                                                                                                    - Todos los SAVEVALUE internos deberían usar CX$_ para ser privados a cada instancia.
                                                                                                                    Ejemplo:

                                                                                                                    SAVEVALUE CX$_estado, 1
                                                                                                                    


                                                                                                                    IMPORTANTE: Las EV creadas con SIGNAL o TIMEOUT mueren al finalizar el procedure, y sus ASSIGN desaparecen.
                                                                                                                    Solo CALL usa una entidad real que mantiene sus variables.

                                                                                                                    - Por coherencia y encapsulamiento, todo lo que se use desde fuera de una "clase" debe hacerse a través de sus métodos:

                                                                                                                    CALL instancia.objeto.metodo,...
                                                                                                                    

                                                                                                                    ...y no accediendo directamente a los nombres internos de gráficos, variables o recursos.

                                                                                                                     

                                                                                                                    En el ejemplo:

                                                                                                                    Existen dos ficheros, el principal y una librería llamada "./library_graphics/tank.lib".

                                                                                                                    Puedes ver el código abriéndolo en el menú "OPEN".

                                                                                                                     

                                                                                                                    Lo más interesante es que se configura la librería desde el programa principal desde PRE_RUN:

                                                                                                                        assign config,{title:"deposito"
                                                                                                                                ,x:100,y:100 
                                                                                                                                ,width:50 ,height:180
                                                                                                                                ,value:88
                                                                                                                                ,"color":"#ff0000"}
                                                                                                                    
                                                                                                                        call aaa.tank.init,V$config

                                                                                                                    Y se usa sin más desde el programa con CALL:

                                                                                                                    CALL aaa.tank.set, 10
                                                                                                                    
                                                                                                                    CALL aaa.tank.get
                                                                                                                    MOVE {NAME:Text1, TEXT:"Valor actual aaa: P$(get)"}

                                                                                                                     

                                                                                                                     


                                                                                                                    Example:
                                                                                                                    /*
                                                                                                                    
                                                                                                                    Contextos y módulos. Librerías gráficas
                                                                                                                    
                                                                                                                    
                                                                                                                    */
                                                                                                                    
                                                                                                                    SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                    Graphic {NAME:Text1,Type:TEXT,X:100,Y:319,Text:"Valor actual"}
                                                                                                                    Graphic {NAME:Text2,Type:TEXT,X:300,Y:312,Text:"Valor actual"}
                                                                                                                    
                                                                                                                    START 100
                                                                                                                    
                                                                                                                    include ./library_graphics/tank.lib
                                                                                                                    
                                                                                                                    ;-----------------------------------------------------------
                                                                                                                    ; Instanciamos dos depósitos con parámetros distintos
                                                                                                                    ; Cada uno se genera en su propio contexto: aaa y bbb
                                                                                                                    ; Los procedimientos serán los mismos, pero los datos y gráficos serán independientes
                                                                                                                    PROCEDURE PRE_RUN
                                                                                                                    	assign config,{title:"tank"
                                                                                                                        		,x:100,y:100 
                                                                                                                                ,width:50 ,height:180
                                                                                                                                ,value:88
                                                                                                                                ,max_value:100
                                                                                                                                ,"color":"#ff0000"}
                                                                                                                    	call aaa.tank.init,V$config
                                                                                                                    
                                                                                                                    	assign config2,{title:"tank"
                                                                                                                        		,x:300,y:100 
                                                                                                                                ,width:50 ,height:180
                                                                                                                                ,value:88
                                                                                                                                ,max_value:100
                                                                                                                                ,"color":"#ff00ff"}
                                                                                                                                
                                                                                                                    	call bbb.tank.init,V$config2
                                                                                                                    
                                                                                                                    	TERMINATE_VE 
                                                                                                                    ENDPROCEDURE
                                                                                                                    
                                                                                                                    
                                                                                                                    ;============================================================
                                                                                                                    GENERATE 25,0 {NAME:Gen1,x:100,y:400}
                                                                                                                    
                                                                                                                    
                                                                                                                    CALL aaa.tank.set, 25
                                                                                                                    CALL bbb.tank.set, 35
                                                                                                                    ADVANCE 5
                                                                                                                    
                                                                                                                    CALL aaa.tank.set, 52
                                                                                                                    CALL bbb.tank.setcolor, "blue"
                                                                                                                    ADVANCE 5
                                                                                                                    
                                                                                                                    CALL aaa.tank.get
                                                                                                                    MOVE {NAME:Text1, TEXT:"Valor actual aaa: P$(get)"}
                                                                                                                    
                                                                                                                    CALL bbb.tank.get
                                                                                                                    MOVE {NAME:Text2, TEXT:"Valor actual bbb: P$(get)"}
                                                                                                                    
                                                                                                                    CALL aaa.tank.set, 75
                                                                                                                    CALL bbb.tank.set, 10
                                                                                                                    ADVANCE 5
                                                                                                                    CALL aaa.tank.set, 90
                                                                                                                    ADVANCE 5
                                                                                                                    
                                                                                                                    CALL aaa.tank.set, 10
                                                                                                                    ADVANCE 5
                                                                                                                    
                                                                                                                    ENDGENERATE 1
                                                                                                                    
                                                                                                                    
                                                                                                                    • + Módulos

                                                                                                                      Ya hemos visto los contextos CX$ que resumiéndolos mucho son un assign especial de tipo string que sirve para que no coincidan los nombres de las variables y que se hereda de invocante a invocado. Es decir, si hacemos TIMEOUT, la EV que ejecute el PROCEDURE lo hará con el mismo valor de CX$ que el que lo llamó.

                                                                                                                      También se puede establecer con el BLOQUE CX:

                                                                                                                      CX "sevilla"

                                                                                                                      Y a partir de ese punto, el SNA CX$ tendrá ese valor.

                                                                                                                      Ahora que sabemos crear elementos gráficos con NEWGRAPHIC pasando el contexto, vamos a ver cómo crear un módulo completo con el mismo sistema.

                                                                                                                      Un módulo es conjunto de GENERATEs, NEWFACILITYs, PROCEDUREs... que funcionarían por sí mismos, normalmente bajo un contexto y que suelen contener al menos un GENERATE. Todo esto en un único fichero al que llamaremos con la extensión ".mod".

                                                                                                                      Lo más particular de estos módulos es que se usan con un INCLUDE y los parámetros afectarán a lo que esté en zona de bloques, pero no a lo que sea un GENERATE ya que es el BLOQUE/COMANDO que no es exactamente un BLOQUE sino que también es un punto de programa que crea entidades y donde es seguro donde enviar una entidad con SCAPE o UNLOAD.

                                                                                                                      Así que no tiene sentido hacer algo como:

                                                                                                                      GENERATE {name: CX$_gen,x:100,y:200}

                                                                                                                      porque una entidad virtual no va a pasar por ella para crear el GENERATE ni mucho menos podrá realizar un NEWGENERATE.

                                                                                                                      Este sí es el aspecto de un GENERATE en modulo:

                                                                                                                      GENERATE 0,0,0,0 {name:hub,visible:0,x:700,y:100}
                                                                                                                          advance 10,30 {from: hub , to:"CX$_posicion"}
                                                                                                                      ENDGENERATE
                                                                                                                      ;-------------------------------------------
                                                                                                                      PROCEDURE hub.init
                                                                                                                      ;... creación de recursos
                                                                                                                      ENDPROCEDURE

                                                                                                                      Al ejecutarse el INCLUDE, lo único que sucede es que este fichero pasa a formar parte del código general. Se concatenan los textos de código GPSS-Plus sin más.

                                                                                                                      Así que el código pasará a tener un nuevo GENERATE llamado "hub" que podrá ser el punto de aterrizaje de cualquier entidad que llegue con SCAPE o con UNLOAD. Lo importante es que aterricen esas entidades con el CX$ correcto.

                                                                                                                      Por esto, un módulo sin GENERATE no puede recibir entidades desde fuera, por lo que no puede funcionar como subsistema autónomo. En este ejemplo, "hub" es la referencia al punto de programa.

                                                                                                                      En suma, lo que tendremos en un módulo será, normalmente, uno o varios GENERATEs que procesarán entidades provinientes de otros contextos o módulos o del principal con acceso a recursos bajo ese mismo contexto.

                                                                                                                      En el ejemplo veremos un sistema básico.

                                                                                                                      Del GENERATE principal parten las entidades para ir a parar a un conjuntos de recursos que en bloque son un módulo llamado "plantaReciclaje".

                                                                                                                      Este módulo tiene, además de un ".init" similar al de creación de cualquier librería con NewGraphic, NewFacility... tiene el GENERATE para que lleguen a él las entidades a través de SCAPEen el principal:

                                                                                                                      scape plantaReciclaje {cx:"plantaA"}

                                                                                                                      Y hará que cada entidad llegue al generate "plantaReciclaje" con su stack de direcciones vacío pero todos los assign intactos y el valor de contexto establecido.

                                                                                                                      generate 0,0,0,0 {name:plantaReciclaje,visible:0}
                                                                                                                          call procesar
                                                                                                                          terminate 1
                                                                                                                      endgenerate
                                                                                                                      
                                                                                                                      procedure procesar
                                                                                                                          advance 10,10 {to:CX$_pos_in}
                                                                                                                          advance 10,10 {to:CX$_fac1}
                                                                                                                          seize CX$_fac1
                                                                                                                          advance 10,10
                                                                                                                          release CX$_fac1
                                                                                                                          advance 10,10 {to:CX$_pos_out}
                                                                                                                      endprocedure

                                                                                                                       En cierto modo es algo como la clásica instrucción "GOTO" pero en el que solo se permite el salto a un punto de programa seguro que es el GENERATE.

                                                                                                                       


                                                                                                                      Example:
                                                                                                                      /*
                                                                                                                      
                                                                                                                      Contextos y módulos. Módulo simple
                                                                                                                      
                                                                                                                      
                                                                                                                      */
                                                                                                                      SYSTEM {TYPE:OPTIONS, SPEED:5}
                                                                                                                      SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                      Position {NAME:POS1,X:147,Y:311}
                                                                                                                      
                                                                                                                      START 200
                                                                                                                      
                                                                                                                      include "./manual_es/Season_07_Contextos_y_modulos/modulo.mod"
                                                                                                                      
                                                                                                                      PROCEDURE PRE_RUN
                                                                                                                      
                                                                                                                      	assign config,{title:"Planta_A"
                                                                                                                          		,x:350,y:500
                                                                                                                                  }
                                                                                                                      	CALL plantaA.plantaReciclaje.init,V$config
                                                                                                                      	
                                                                                                                      	assign config,{title:"Planta B"
                                                                                                                          		,x:350,y:100
                                                                                                                                  }
                                                                                                                      	CALL plantaB.plantaReciclaje.init,V$config
                                                                                                                      
                                                                                                                          TERMINATE_VE
                                                                                                                      ENDPROCEDURE
                                                                                                                      
                                                                                                                      ;------------------------------------------------------
                                                                                                                      ; --- Flujo de entidades ---
                                                                                                                      GENERATE 6,0 {NAME:GEN1,x:58,y:310} 
                                                                                                                      
                                                                                                                      advance 15,0 {to:POS1}
                                                                                                                      
                                                                                                                      if (RANDOM > 0.5)
                                                                                                                      scape plantaReciclaje {cx:"plantaA"}
                                                                                                                      else
                                                                                                                      scape plantaReciclaje {cx:"plantaB"}
                                                                                                                      endif
                                                                                                                      
                                                                                                                      TERMINATE 1
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      • + Los módulos. Ejemplo completo

                                                                                                                        Vamos a ver un ejemplo de un sistema de control logístico.

                                                                                                                        Cada módulo se carga en un INCLUDE que se especializa en cada cosa.

                                                                                                                        Un JSON con los datos de varias ciudades será el punto de partida para crear los conjuntos de recursos de manera análoga a la que hemos visto.

                                                                                                                        Los paquetes se generan en los "punto_distribucion" almacenándose en un RESTROOM particular de ese punto.

                                                                                                                        Una furgoneta recorre los puntos de distribución cargando en su BACKPACK los paquetes provinientes del RESTROOM. Los BACKPACK y los RESTROOM funcionan entre ellos. No es posible poner en una mochila las entidades de ningún otro recurso.

                                                                                                                        La furgoneta descarga todas la entidades en el punto seguro GENERATE "hub_distribucion" que se encargará de clasificar los paquetes en diferentes RESTROOMs. Tras esto, la furgoneta cargará cada saca ya clasificada hará de nuevo el recorrido entregando los paquetes en su destino.

                                                                                                                        Al final, es un pequeño ejemplo de 150 líneas de código para hacer algo aparentemente complejo.

                                                                                                                         


                                                                                                                        Example:
                                                                                                                        SYSTEM {TYPE:OPTIONS, SPEED:5}
                                                                                                                        SYSTEM {TYPE:VISUAL,  WIDTH:900,HEIGHT:600}
                                                                                                                        SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                        
                                                                                                                        START 1
                                                                                                                        
                                                                                                                        include ./logistica/namespace.mod
                                                                                                                        include ./logistica/punto_distribucion.mod
                                                                                                                        include ./logistica/hub_distribucion.mod
                                                                                                                        include ./logistica/transportes.mod
                                                                                                                        
                                                                                                                        PROCEDURE PRE_RUN
                                                                                                                        	CALL crear_puntos_de_distribucion
                                                                                                                            CALL hub_distribucion.init
                                                                                                                            TERMINATE_VE
                                                                                                                        ENDPROCEDURE
                                                                                                                        
                                                                                                                        ;------------------------------------------------------
                                                                                                                        procedure crear_puntos_de_distribucion
                                                                                                                        
                                                                                                                        
                                                                                                                            FOREACH ciudad, IN_OBJECT, V$(aCiudades)
                                                                                                                                assign datosCiudad, V$(aCiudades.P$ciudad)
                                                                                                                                
                                                                                                                                assign config,{
                                                                                                                            		title:"P$(ciudad)"
                                                                                                                            		,x:P$(datosCiudad.x)
                                                                                                                                    ,y:P$(datosCiudad.y) 
                                                                                                                                    }
                                                                                                                        	call P$ciudad.punto_distribucion.init,V$config
                                                                                                                                
                                                                                                                            ENDFOREACH
                                                                                                                        endprocedure
                                                                                                                        
                                                                                                                        
                                                                                                                        • + La rehidratación

                                                                                                                          Un concepto avanzado y complejo cuando el sistema crece es la rehidratación.

                                                                                                                          Rehidratar significa cargar el estado del sistema no desde el inicio, sino desde un punto avanzado en el tiempo, reconstruyendo la simulación tal y como estaba en ese instante.
                                                                                                                          En el ejemplo logístico esto es fundamental: no es lo mismo “empezar a simular paquetes” que cargar el estado real de un ERP y continuar la simulación desde ahí.

                                                                                                                          De esta forma, el modelador se hace responsable de guardar todo aquello que quiera poder restaurar.

                                                                                                                           

                                                                                                                          Este proceso implica resolver tres problemas profundos:

                                                                                                                           

                                                                                                                          1. El tiempo simulado global (AC1) debe avanzar hasta el instante guardado.
                                                                                                                          2. Las entidades deben reprogramar sus tiempos internos, que siempre deben ser posteriores al nuevo AC1.
                                                                                                                          3. La forma de modelar cambia radicalmente: ya no basta con describir procesos; ahora hay que reconstruirlos.

                                                                                                                          En esta fase, más que modelar, estamos programando. La rehidratación es un problema técnico, no declarativo.

                                                                                                                          En el ejemplo:

                                                                                                                          Así que supongamos que lo que tenemos almacenado es que 4 camiones daban vueltas en un circuito de 4 posiciones. Los datos almacenados por última vez son:

                                                                                                                          • Tiempo del sistema: 105
                                                                                                                          • Camión 1: Pasó por la fase F3 y llegaría a destino en 110
                                                                                                                          • Camión 2: Pasó por la fase F1 y llegaría a destino en 108
                                                                                                                          • Camión 3: Pasó por la fase F3 y llegaría a destino en 109
                                                                                                                          • Camión 4: Pasó por la fase F2 y llegaría a destino en 109

                                                                                                                          Para reconstruir (rehidratar) este estado necesitamos resolver los tres problemas anteriores.

                                                                                                                          1. Avanzar el tiempo global

                                                                                                                          El bloque FORWARD_AC1 permite mover el reloj simulado hacia adelante.
                                                                                                                          Lo habitual es colocarlo como primera instrucción del PRE_RUN, de modo que el modelo arranque directamente en el instante deseado.

                                                                                                                          2. Reprogramar los tiempos de las entidades

                                                                                                                          Una vez que AC1 ha avanzado, todas las entidades deben tener tiempos posteriores al nuevo AC1.
                                                                                                                          Esto se consigue con UPDATE, que reprograma el final de un ADVANCE. Y en este caso, añadiremos también el tiempo de inicio para que la visualización comience allí donde se quedó.

                                                                                                                          La única condición es crítica:

                                                                                                                          UPDATE debe ejecutarse desde una VE cuando la entidad ya esté dentro de un ADVANCE.
                                                                                                                          Por eso la rehidratación requiere una pequeña coreografía temporal. Primero la introducimos en la cola correspondiente y después actualizamos los tiempos a través de un TIMEOUTinmediato.

                                                                                                                          3. Reconstruir el punto del programa

                                                                                                                          Este es el problema más complejo.
                                                                                                                          Si existiera un GOTO, sería trivial: saltaríamos al punto exacto donde estaba cada entidad.
                                                                                                                          Pero no existe, y no debe existir.

                                                                                                                          La solución correcta es:

                                                                                                                          Crear una máquina de estados finitos (FSM) local para cada entidad.

                                                                                                                          El estado de la FSM representa en qué punto del programa estaba la entidad cuando se guardó el sistema.
                                                                                                                          La lógica del modelo se reescribe para que cada entidad avance según su estado.

                                                                                                                          Esto convierte el flujo del programa en un autómata explícito, perfectamente restaurable.

                                                                                                                           

                                                                                                                          4. El detalle clave: toda entidad termina siempre en una cola

                                                                                                                          En un motor DES, el “estado último” de una entidad siempre es uno de estos:

                                                                                                                          • La cola de eventos, si está en un ADVANCE.
                                                                                                                          • La cola de un recurso, si está en un SEIZE.
                                                                                                                          • Una cola explícita, si está esperando.

                                                                                                                          Por tanto:

                                                                                                                           

                                                                                                                          Los estados de la FSM deben corresponderse con estas colas.

                                                                                                                           

                                                                                                                          No necesitamos reconstruir colas internas ni manipular estructuras ocultas:
                                                                                                                          basta con reinsertar las entidades en el punto correcto del programa, y el motor reconstruye el resto automáticamente.

                                                                                                                           


                                                                                                                          Example:
                                                                                                                          SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                          
                                                                                                                          POSITION {NAME:Pos1,X:368,Y:429}
                                                                                                                          POSITION {NAME:Pos2,X:634,Y:452}
                                                                                                                          POSITION {NAME:Pos3,X:592,Y:164}
                                                                                                                          POSITION {NAME:Pos4,X:343,Y:217}
                                                                                                                          
                                                                                                                          initial fsm_camion_logic, {
                                                                                                                              STATES: [
                                                                                                                                  "F1", "F2", "F3", "F4"
                                                                                                                              ],
                                                                                                                          
                                                                                                                              TRANSITIONS: [
                                                                                                                                  {FROM:"F1", INPUT:"tick", TO:"F2"},
                                                                                                                                  {FROM:"F2", INPUT:"tick", TO:"F3"},
                                                                                                                                  {FROM:"F3", INPUT:"tick", TO:"F4"},
                                                                                                                                  {FROM:"F4", INPUT:"tick", TO:"F1"}
                                                                                                                              ],
                                                                                                                          
                                                                                                                              INITIAL: "F1"
                                                                                                                          }
                                                                                                                          
                                                                                                                          FSM {NAME:FSM_CAMION, LOCAL:1, LOGIC:V$(fsm_camion_logic)}
                                                                                                                          FACILITY {NAME:Fac1,X:633,Y:385}
                                                                                                                          Graphic {NAME:Text1,Type:TEXT,X:324,Y:100}
                                                                                                                          
                                                                                                                          START 100
                                                                                                                          ;-------------------------------
                                                                                                                          PROCEDURE PRE_RUN 
                                                                                                                            FORWARD_AC1 105
                                                                                                                              assign params,{status:"F2",tiempoIni:90,tiempo:110,matricula:"1111"}
                                                                                                                          	new GEN1,0,V$params
                                                                                                                              assign params,{status:"F3",tiempoIni:88,tiempo:108,matricula:"2222"}
                                                                                                                          	new GEN1,0,V$params
                                                                                                                              assign params,{status:"F2",tiempoIni:89,tiempo:109,matricula:"3333"}
                                                                                                                          	new GEN1,0,V$params
                                                                                                                              assign params,{status:"F2",tiempoIni:104,tiempo:124,matricula:"4444"}
                                                                                                                          	new GEN1,0,V$params
                                                                                                                          	TERMINATE_VE 
                                                                                                                          ENDPROCEDURE
                                                                                                                          
                                                                                                                          ;-------------------------------
                                                                                                                          PROCEDURE camion_update
                                                                                                                          	; PARAM_A -> Numero entidad
                                                                                                                          	; PARAM_B -> tiempo final
                                                                                                                          	; PARAM_C -> tiempo inicial
                                                                                                                          	move {name:Text1,text:"camion_update P$(FSM_CAMION,P$PARAM_A) P$PARAM_C"}
                                                                                                                              if (D$(IN_ADVANCE,P$PARAM_A)==1)
                                                                                                                              	update P$PARAM_A, P$PARAM_B, P$PARAM_C
                                                                                                                              endif
                                                                                                                              TERMINATE_VE
                                                                                                                          endprocedure 1
                                                                                                                          ;-------------------------------
                                                                                                                          
                                                                                                                          GENERATE 0,0,0,0 {NAME:GEN1,X:62,Y:396}
                                                                                                                              assign params,V$PARAM_A
                                                                                                                              assign FSM_CAMION,"P$(params.status)"
                                                                                                                              mod {subtitle:"P$(params.matricula)"}
                                                                                                                              ; debe ser una VE y que se ejecute una vez hayan arrancado los advance.
                                                                                                                              timeout camion_update,0,D$N,P$(params.tiempo),P$(params.tiempoIni)
                                                                                                                          	
                                                                                                                             ; move {name:Text1,text:"Camión inicializado P$PARAM_C R$(FSM_CAMION,STATE) P$FSM_CAMION"}
                                                                                                                          	while (1==1)
                                                                                                                              	STATE FSM_CAMION,"tick"
                                                                                                                                  
                                                                                                                              	switch "R$(FSM_CAMION,STATE)"
                                                                                                                          	    case ==,"F1"
                                                                                                                          	        advance 20 {from:Pos4, to:Pos1}
                                                                                                                                  endcase
                                                                                                                          	    case ==,"F2"
                                                                                                                                  	 advance 20 {from:Pos1, to:Pos2}
                                                                                                                                  endcase
                                                                                                                          	    case ==,"F3"
                                                                                                                                      seize Fac1
                                                                                                                            	        advance 40 {from:Pos2, to:Pos3}
                                                                                                                                      release Fac1
                                                                                                                                  endcase
                                                                                                                          	    case ==,"F4"
                                                                                                                          	        advance 20 {from:Pos3, to:Pos4}
                                                                                                                                  endcase
                                                                                                                          
                                                                                                                                  endswitch
                                                                                                                              endwhile
                                                                                                                          
                                                                                                                          endgenerate 1
                                                                                                                          ;-------------------------------
                                                                                                                          
                                                                                                                          
                                                                                                                          
                                                                                                                        • + Season 8: Estadísticas
                                                                                                                          • + Acumulando datos en tablas

                                                                                                                            La simulación y la estadística van de la mano. Poco podremos hacer si no tenemos claros algunos conceptos básicos. En GPSS-Plus, los cálculos estadísticos se dividen en tres grandes grupos:

                                                                                                                            1. Tiempos que una entidad permanece en un recurso (orientado a la entidad).

                                                                                                                            2. Tiempos que un número de entidades está ocupando un recurso (orientado al recurso).

                                                                                                                            3. Cualquier otro dato personalizado que quieras medir (orientado al modelo).

                                                                                                                            En este primer ejemplo, trabajamos con el caso 3, donde queremos medir el tiempo que una entidad permanece en un tramo del circuito, incluyendo un recurso (una FACILITY) y su entorno.

                                                                                                                            Uso de TABLE para registrar tiempos de permanencia:

                                                                                                                            GPSS-Plus automatiza gran parte del trabajo. Solo tienes que crear una TABLE con los parámetros adecuados:

                                                                                                                            TABLE {name: TABLA1, E_BIN_START:0, E_BIN_SIZE:1, E_BIN_COUNT:100, EXPRESSION:(AC1$ - P$TIEMPOINICIO)}

                                                                                                                            Esto configura una tabla de 100 celdas de 1 unidad de ancho, que empieza en 0. Cada vez que ejecutemos TABULATE TABLA1, se ejecutará EXPRESSION :

                                                                                                                            (AC1$ - P$TIEMPOINICIO)

                                                                                                                            donde calculará la diferencia entre el tiempo actual (AC1$) y un instante anterior (P$TIEMPOINICIO) guardado previamente.

                                                                                                                            Este valor, que representa la permanencia de la entidad en el conjunto de recursos o zona del modelo, se acumulará en la celda correspondiente.

                                                                                                                            Un ejemplo básico

                                                                                                                            Creamos entidades que avanzan DESDE UN PUNTO pasando por una FACILITY Y HASTA OTRO PUNTO. después tabulamos la duración:

                                                                                                                            GENERATE 8,3
                                                                                                                                ADVANCE 30,0 {TO:POS1}
                                                                                                                                ASSIGN TIEMPOINICIO,AC1$      ; Inicio del cómputo
                                                                                                                                ADVANCE 20,0 {TO:VENTANILLA1} ; + 20
                                                                                                                            
                                                                                                                                SEIZE VENTANILLA1
                                                                                                                                ADVANCE 20,20                 ; +20 a +40 (aleatorio) + Tiempo en cola
                                                                                                                                RELEASE VENTANILLA1
                                                                                                                            
                                                                                                                            
                                                                                                                                ADVANCE 20,0 {TO:POS2}        ; + 20
                                                                                                                                TABULATE TABLA1               ; Fin del cómputo
                                                                                                                                ADVANCE 20,0 {TO:POS3}
                                                                                                                            
                                                                                                                            ENDGENERATE
                                                                                                                            
                                                                                                                            

                                                                                                                            ¿Qué mide la tabla?

                                                                                                                            Si observas los resultados en el informe (botón superior derecho), verás una distribución rectangular (uniforme) entre 60 y 80, con algunos tiempos superiores de las entidades que han permanecido en la cola. Es decir, que todas las entidades han permanecido un tiempo dentro de ese rango, como se esperaba.

                                                                                                                            Este es un buen punto de partida para empezar a analizar comportamientos internos del modelo con datos objetivos.


                                                                                                                            Example:
                                                                                                                            /*
                                                                                                                            
                                                                                                                              	 	Acumulando datos en tablas
                                                                                                                             
                                                                                                                            */
                                                                                                                            FACILITY {NAME:VENTANILLA1,X:380,Y:348,capacity:4}
                                                                                                                            POSITION {NAME:POS1,X:218,Y:437}
                                                                                                                            POSITION {NAME:POS2,X:591,Y:429}
                                                                                                                            POSITION {NAME:POS3,X:713,Y:329}
                                                                                                                            
                                                                                                                            Graphic {NAME:Line1,Type:LINE,color:#FF0000, X1:218,Y1:500,X2:592,Y2:500}
                                                                                                                            Graphic {NAME:Text1,Type:TEXT,X:410,Y:527,Text:"Estadistic section"}
                                                                                                                            
                                                                                                                            TABLE {name: TABLA1,E_BIN_START:50,E_BIN_SIZE:1,E_BIN_COUNT:60,EXPRESSION:(AC1$ - P$TIEMPOINICIO)}
                                                                                                                            ;TABLE {name: TABLA1,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:160,EXPRESSION:(AC1$ - D$M0)}
                                                                                                                            
                                                                                                                            START 500 
                                                                                                                            
                                                                                                                            ;***************************************************************
                                                                                                                            
                                                                                                                            
                                                                                                                            GENERATE 8,3 {NAME:GEN1,X:66,Y:350}
                                                                                                                            
                                                                                                                                ADVANCE 40,0 {TO:POS1}
                                                                                                                                ASSIGN TIEMPOINICIO,AC1$
                                                                                                                                ADVANCE 20,0 {TO:VENTANILLA1}
                                                                                                                            
                                                                                                                                SEIZE VENTANILLA1
                                                                                                                                ADVANCE 20,20
                                                                                                                                RELEASE VENTANILLA1
                                                                                                                            
                                                                                                                            
                                                                                                                                ADVANCE 20,0 {TO:POS2}
                                                                                                                                TABULATE TABLA1
                                                                                                                                ADVANCE 20,0 {TO:POS3}
                                                                                                                            
                                                                                                                            ENDGENERATE 1
                                                                                                                            
                                                                                                                            ;***************************************************************
                                                                                                                            
                                                                                                                            • + Tiempo de entidades

                                                                                                                              Ahora pasamos a las estadísticas centradas en el tiempo de las entidades en el recurso.

                                                                                                                              El las estadísticas calculadas sobre un único recurso se calculan automáticamente por GPSS-Plus:

                                                                                                                              Facility {NAME:ventanilla1,X:377,Y:362,capacity:10
                                                                                                                                   ,E_BIN_START:18,E_BIN_SIZE:1,E_BIN_COUNT:14
                                                                                                                                   }

                                                                                                                              Medimos el tiempo que una entidad (E) que permanece en el recurso con los parámetros E_BIN_* que tiene el mismo significado que en el bloque TABLE.

                                                                                                                              En el ejemplo siguiente, se generan entidades a un ritmo constante, y la FACILITY contiene un ADVANCE 20,10 lo que significa que el resultado estadístico serán datos entre 20 y 30.


                                                                                                                              Example:
                                                                                                                              /*
                                                                                                                              
                                                                                                                                	 	Tiempo de entidades
                                                                                                                               
                                                                                                                              */
                                                                                                                              SYSTEM {TYPE:OPTIONS,Speed:7}
                                                                                                                              POSITION {NAME:POS1,X:620,Y:360}
                                                                                                                              Facility {NAME:ventanilla1,X:377,Y:362,capacity:10,E_BIN_START:18,E_BIN_SIZE:1,E_BIN_COUNT:14}
                                                                                                                              START 200
                                                                                                                              
                                                                                                                              ;*****************************************************
                                                                                                                              GENERATE 8,0,0,0 {NAME:GEN1,X:111,Y:366}
                                                                                                                              ADVANCE 10 {TO:ventanilla1}
                                                                                                                              
                                                                                                                              seize ventanilla1
                                                                                                                              ADVANCE 20,10
                                                                                                                              release ventanilla1
                                                                                                                              
                                                                                                                              ADVANCE 10 {TO:POS1}
                                                                                                                              TERMINATE 1
                                                                                                                              
                                                                                                                              
                                                                                                                              
                                                                                                                              • + Uso del recurso

                                                                                                                                Ahora pasamos a las estadísticas centradas en el uso de un recurso, es decir, en cuánto tiempo ha estado ocupado por una o más entidades.

                                                                                                                                Si antes medíamos el tiempo que una entidad (E) permanece en el recurso con los parámetros E_BIN_*, ahora, para observar cómo de ocupado ha estado el recurso en cada instante, usamos R_BIN_*.

                                                                                                                                La tabla creada con R_BIN_* nos indica, para cada nivel de ocupación (0, 1, 2, ...), cuánto tiempo el recurso ha estado exactamente con esa cantidad de uso.

                                                                                                                                En el ejemplo siguiente, se generan entidades a un ritmo constante, y la FACILITY tiende a tener entre 4 y 6 unidades ocupadas, algo que se verá reflejado como un pico en ese rango en la gráfica generada automáticamente.

                                                                                                                                Por supuesto, nada impide que uses a la vez E_BIN_* y R_BIN_* sobre un mismo recurso, si deseas tener una visión desde ambos ángulos: el de la entidad y el del recurso.

                                                                                                                                 


                                                                                                                                Example:
                                                                                                                                /*
                                                                                                                                
                                                                                                                                  	 	Uso del recurso
                                                                                                                                 
                                                                                                                                */
                                                                                                                                SYSTEM {TYPE:OPTIONS,Speed:7}
                                                                                                                                POSITION {NAME:POS1,X:620,Y:360}
                                                                                                                                Facility {NAME:ventanilla1,X:377,Y:362,capacity:10,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:11}
                                                                                                                                START 200
                                                                                                                                
                                                                                                                                ;*****************************************************
                                                                                                                                GENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}
                                                                                                                                ADVANCE 10 {TO:ventanilla1}
                                                                                                                                
                                                                                                                                seize ventanilla1
                                                                                                                                ADVANCE 20,10
                                                                                                                                release ventanilla1
                                                                                                                                
                                                                                                                                ADVANCE 10 {TO:POS1}
                                                                                                                                ENDGENERATE 1
                                                                                                                                
                                                                                                                                
                                                                                                                                • + Las colas

                                                                                                                                  Las colas también pueden ser objeto de análisis estadístico. En GPSS-Plus, funcionan exactamente igual que los recursos, pero se gestionan de forma separada mediante estructuras específicas llamadas QUEUER.

                                                                                                                                  Para activar la recogida de estadísticas, basta con envolver el acceso al recurso con QUEUE y DEPART:

                                                                                                                                  queue Qventanilla1
                                                                                                                                  seize ventanilla1
                                                                                                                                  depart Qventanilla1

                                                                                                                                  Esto habilita el cálculo automático de:

                                                                                                                                  • El número de entidades en cola a lo largo del tiempo (R_BIN_*)

                                                                                                                                  • El tiempo de espera que cada entidad pasa en cola (E_BIN_*)

                                                                                                                                  En el informe, podrás visualizar gráficamente cómo se comportó la cola: cuándo se formó, cuánto tiempo esperaron las entidades, y si el dimensionamiento del recurso fue suficiente.

                                                                                                                                  El siguiente ejemplo muestra una FACILITY con colas frecuentes. Al aplicar QUEUE/DEPART, el sistema captura toda la estadística sin ningún código adicional.

                                                                                                                                   

                                                                                                                                   


                                                                                                                                  Example:
                                                                                                                                  /*
                                                                                                                                  
                                                                                                                                    	 	Las colas
                                                                                                                                   
                                                                                                                                  */
                                                                                                                                  SYSTEM {TYPE:OPTIONS,Speed:7}
                                                                                                                                  POSITION {NAME:POS1,X:620,Y:360}
                                                                                                                                  
                                                                                                                                  QUEUER {NAME:Qventanilla1,X:374,Y:424
                                                                                                                                  	,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 
                                                                                                                                      ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20
                                                                                                                                      }
                                                                                                                                  
                                                                                                                                  Facility {NAME:ventanilla1,X:373,Y:365,capacity:5}
                                                                                                                                  START 200
                                                                                                                                  
                                                                                                                                  ;*****************************************************
                                                                                                                                  GENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}
                                                                                                                                  ADVANCE 10 {TO:ventanilla1}
                                                                                                                                  
                                                                                                                                  queue Qventanilla1
                                                                                                                                  seize ventanilla1
                                                                                                                                  depart Qventanilla1
                                                                                                                                  ADVANCE 20,10
                                                                                                                                  release ventanilla1
                                                                                                                                  
                                                                                                                                  ADVANCE 10 {TO:POS1}
                                                                                                                                  ENDGENERATE 1
                                                                                                                                  
                                                                                                                                  • + Storages

                                                                                                                                    Los STORAGE son un tipo especial de recurso. A diferencia de las FACILITY, que gestionan entidades de forma individual, un STORAGE gestiona cantidades variables que una entidad puede introducir o extraer.

                                                                                                                                    Por ejemplo, una entidad puede representar un camión que deja 10 unidades en un depósito. Por tanto, el sistema no analiza cuántas entidades han pasado por el almacén, sino cuántas unidades se han almacenado y durante cuánto tiempo.

                                                                                                                                    ¿Qué se mide?

                                                                                                                                    Se recogen dos tipos de estadísticas:

                                                                                                                                    • EQ_BIN_*: Tiempo que cada unidad permanece en el almacenamiento (tiempo de ocupación por unidad).

                                                                                                                                    • RQ_BIN_*: Nivel de ocupación del almacenamiento en cada momento (por cantidad).

                                                                                                                                    Este comportamiento se activa igual que en otros recursos, pero usando los parámetros EQ_BIN_* y RQ_BIN_* para configurar los intervalos de las tablas.

                                                                                                                                    Un ejemplo práctico

                                                                                                                                    El siguiente código muestra un almacenamiento (STORAGE) con capacidad de 14 unidades. Las entidades entrantes ocupan una cantidad aleatoria (entre 1 y 5 unidades). La estadística resultante indicará cuánto tiempo estuvieron almacenadas esas unidades y cómo varió el nivel de ocupación del almacén:

                                                                                                                                    STORAGE {NAME:almacen1,X:377,Y:362,capacity:14,
                                                                                                                                             EQ_BIN_START:18,EQ_BIN_SIZE:1,EQ_BIN_COUNT:14,
                                                                                                                                             RQ_BIN_START:0, RQ_BIN_SIZE:1, RQ_BIN_COUNT:18}
                                                                                                                                    
                                                                                                                                    GENERATE 8,0,0,0
                                                                                                                                        ADVANCE 10 {TO:almacen1}
                                                                                                                                        ENTER almacen1,(RANDOM*5)+1
                                                                                                                                        ADVANCE 20,10
                                                                                                                                        LEAVE almacen1
                                                                                                                                        ADVANCE 10 {TO:pos1}
                                                                                                                                    ENDGENERATE 1

                                                                                                                                    ¿Qué observar?

                                                                                                                                    En el informe, verás dos tablas diferentes:

                                                                                                                                    • Una con los tramos de tiempo por unidad almacenada (EQ), que te dirá si las unidades permanecieron poco o mucho tiempo en el almacén.

                                                                                                                                    • Otra con los niveles de ocupación del almacén (RQ), donde podrás ver cuántas unidades había simultáneamente en cada instante.

                                                                                                                                    Este tipo de análisis es esencial en modelos de logística, inventario, o cualquier sistema con almacenamiento variable.

                                                                                                                                     

                                                                                                                                     


                                                                                                                                    Example:
                                                                                                                                    /*
                                                                                                                                    
                                                                                                                                      	 	Storages
                                                                                                                                     
                                                                                                                                    */
                                                                                                                                    SYSTEM {TYPE:OPTIONS,Speed:7}
                                                                                                                                    POSITION {NAME:POS1,X:620,Y:360}
                                                                                                                                    STORAGE {NAME:almacen1,X:377,Y:362,capacity:14
                                                                                                                                    	,EQ_BIN_START:18,EQ_BIN_SIZE:1,EQ_BIN_COUNT:14 
                                                                                                                                        ,RQ_BIN_START:0,RQ_BIN_SIZE:1,RQ_BIN_COUNT:18
                                                                                                                                        }
                                                                                                                                    START 200
                                                                                                                                    
                                                                                                                                    ;*****************************************************
                                                                                                                                    GENERATE 8,0,0,0 {NAME:GEN1,X:111,Y:366}
                                                                                                                                    ADVANCE 10 {TO:almacen1}
                                                                                                                                    
                                                                                                                                    ENTER almacen1,(RANDOM*5)+1
                                                                                                                                    ADVANCE 20,10
                                                                                                                                    LEAVE almacen1
                                                                                                                                    
                                                                                                                                    ADVANCE 10 {TO:POS1}
                                                                                                                                    ENDGENERATE 1
                                                                                                                                    
                                                                                                                                    • + Resumen

                                                                                                                                      En GPSS-Plus, las estadísticas se acumulan automáticamente si se definen las tablas con los parámetros adecuados. Existen varios tipos de estadísticas, clasificadas por lo que se mide:

                                                                                                                                      Estadísticas por entidad (E_BIN_*)

                                                                                                                                      Mide cuánto tiempo ha permanecido cada entidad en un recurso.

                                                                                                                                      • Aplicable a: Todos los recursos (FACILITY, STORAGE, QUEUE, etc.)

                                                                                                                                      • Ejemplo: Una entidad pasa 40 unidades de tiempo → se suma 1 al bin 40.

                                                                                                                                      Estadísticas por recurso (R_BIN_*)

                                                                                                                                      Mide cuánto tiempo un recurso ha tenido una cantidad concreta de entidades activas.

                                                                                                                                      • Aplicable a: Todos los recursos

                                                                                                                                      • Ejemplo: El recurso tiene 3 entidades durante 10 unidades → se suma 10 al bin 3.

                                                                                                                                      Estadísticas por unidad almacenada (EQ_BIN_*)

                                                                                                                                      Mide cuánto tiempo permanece cada unidad dentro de un STORAGE.

                                                                                                                                      • Aplicable a: Solo STORAGE

                                                                                                                                      • Ejemplo: Un camión con 5 unidades permanece 40 tiempos → se suma 5 al bin 40.

                                                                                                                                      Estadísticas por cantidad en almacenamiento (RQ_BIN_*)

                                                                                                                                      Mide cuánto tiempo un STORAGE contiene cierta cantidad total de unidades.

                                                                                                                                      • Aplicable a: Solo STORAGE

                                                                                                                                      • Ejemplo: 3 unidades durante 10 tiempos → se suma 30 al bin 3.

                                                                                                                                      Colas (QUEUER)

                                                                                                                                      Las colas tienen su propia tabla de estadísticas si se declara un QUEUER. Para recoger datos:

                                                                                                                                      queue Cola1
                                                                                                                                      seize Recurso1
                                                                                                                                      depart Cola1
                                                                                                                                      ADVANCE ...
                                                                                                                                      release Recurso1

                                                                                                                                      Se peude aplicar:

                                                                                                                                      • E_BIN_*: Tiempo que cada entidad pasó en cola.

                                                                                                                                      • R_BIN_*: Tiempo que la cola tuvo X entidades esperando.

                                                                                                                                      Personalización total: TABLE + TABULATE

                                                                                                                                      Para registrar datos arbitrarios:

                                                                                                                                      TABLE {name:TABLA1, E_BIN_START:0, E_BIN_SIZE:1, E_BIN_COUNT:100, EXPRESSION:(AC1$ - P$TIEMPOINICIO)}
                                                                                                                                      TABULATE TABLA1

                                                                                                                                      Esto permite medir cualquier cosa definida por una expresión matemática.

                                                                                                                                       

                                                                                                                                       

                                                                                                                                       

                                                                                                                                    • + Season 9: Funciones
                                                                                                                                      • + Definiendo las funciones

                                                                                                                                        ¿Qué es una función?

                                                                                                                                        Una función es un componente que produce un valor numérico a partir de una entrada. Ese valor puede representar tiempos, cantidades, costes, o cualquier otro parámetro variable que queramos incorporar al modelo.

                                                                                                                                        En términos matemáticos, podríamos escribir algo como:

                                                                                                                                        f(x) = 10 + (x * 5)

                                                                                                                                        Si x toma valores entre 0 y 1, entonces f(x) tomará valores entre 10 y 15.

                                                                                                                                        En GPSS-Plus, cuando usamos:

                                                                                                                                        ADVANCE 10,5

                                                                                                                                        estamos usando esa función implícita, se genera un valor aleatorio entre 0 y 1, lo multiplica y resuelve un valor entre 10 y 15. 

                                                                                                                                        En términos estadísticos, eso es una distribución uniforme entre 10 y 15, es decir: todos los valores dentro del rango tienen la misma probabilidad de ocurrir.

                                                                                                                                        Uso de las funciones:

                                                                                                                                        En muchos casos la realidad no es uniforme.

                                                                                                                                        Si medimos cuánto tardan 1000 vehículos en recorrer 100 km. La mayoría tardará cerca de 1 hora. Algunos tardarán algo menos, otros algo más. Pero casi ninguno lo hará en 30 minutos o en 2 horas.

                                                                                                                                        Eso no es una distribución uniforme. Es lo que se conoce como una distribución normal o gaussiana.

                                                                                                                                        Por eso, GPSS-Plus permite definir funciones que modelen con mayor precisión comportamientos como estos. Existen dos métodos para hacerlo:

                                                                                                                                        1. FUNCTION tipo VALUES

                                                                                                                                        Con este método, definimos manualmente una función basada en una tabla acumulada de probabilidades.

                                                                                                                                        Function {
                                                                                                                                          NAME: funValues,
                                                                                                                                          TYPE: "VALUES",
                                                                                                                                          EXPRESSION: "RANDOM",
                                                                                                                                          VALUES: "0.0001,0.0000/0.0002,0.0101/.../1.0000,1.0000/"
                                                                                                                                        }

                                                                                                                                        Esta tabla asocia claves (probabilidades acumuladas entre 0 y 1 EXPRESSION: "RANDOM") con valores resultantes. Si el valor aleatorio generado es 0.33, la función busca el intervalo que le corresponde (por ejemplo, entre 0.0293 y 0.2121) y devuelve el valor asociado que representa una distribución de tipo campana (gaussiana), donde los valores extremos tienen menos probabilidad que los valores centrales.

                                                                                                                                        Entonces, una instrucción como:

                                                                                                                                        ADVANCE 20 + (FN$funValues * 20)

                                                                                                                                        Generará valores entre 20 y 40, concentrados en torno al 30.

                                                                                                                                        2. FUNCTION tipo GAUSS

                                                                                                                                        Este método permite definir directamente una distribución gaussiana mediante sus parámetros característicos:

                                                                                                                                        Function {
                                                                                                                                          NAME: funGauss,
                                                                                                                                          TYPE: "GAUSS",
                                                                                                                                          A: 1,
                                                                                                                                          B: 30,
                                                                                                                                          SIGMA1: 3.3,
                                                                                                                                          SIGMA2: 3.3,
                                                                                                                                          INTERVALS: 100
                                                                                                                                        }
                                                                                                                                        
                                                                                                                                        • A: altura de la curva (no afecta al resultado, es visual)
                                                                                                                                        • B: valor central (media)
                                                                                                                                        • SIGMA1, SIGMA2: desviaciones estándar izquierda y derecha
                                                                                                                                        • INTERVALS: cantidad de divisiones para la función

                                                                                                                                        Con esta función, simplemente usamos:

                                                                                                                                        ADVANCE FN$funGauss
                                                                                                                                        

                                                                                                                                        Ambas técnicas producirán una distribución de tiempos en torno al valor 30, pero con métodos diferentes: una tabla predefinida frente a una curva construida automáticamente.

                                                                                                                                        Visualización y validación

                                                                                                                                        Las FACILITY con parámetros como:

                                                                                                                                        E_BIN_START:16, E_BIN_SIZE:1, E_BIN_COUNT:40

                                                                                                                                        registran estadísticas automáticas sobre cuánto tiempo ha permanecido cada entidad en el recurso. Estas estadísticas se visualizan en el informe y permiten comprobar cómo se comporta realmente la función.

                                                                                                                                        Veremos que, en ambos casos, la gráfica resultante es una distribución gaussiana. Esto permite validar que tanto la tabla manual como la función generada ofrecen comportamientos coherentes.

                                                                                                                                         

                                                                                                                                        Imagen informe

                                                                                                                                         


                                                                                                                                        Example:
                                                                                                                                        /*
                                                                                                                                        
                                                                                                                                          	 	Definiendo las funciones
                                                                                                                                         
                                                                                                                                        */
                                                                                                                                        SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                        
                                                                                                                                        Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:16,E_BIN_SIZE:1,E_BIN_COUNT:40,capacity:3}
                                                                                                                                        
                                                                                                                                        Facility {NAME:VENTANILLA2,X:384,Y:186,E_BIN_start:16,E_BIN_SIZE:1,E_BIN_COUNT:40,capacity:3}
                                                                                                                                        
                                                                                                                                        
                                                                                                                                        POSITION {NAME:POS1,X:278,Y:381}
                                                                                                                                        POSITION {NAME:POS2,X:482,Y:380}
                                                                                                                                        
                                                                                                                                        Function {name:funGauss, type:"GAUSS", a:1, b:30, sigma1:3.3, sigma2:3.3,intervals:100 }
                                                                                                                                        
                                                                                                                                        Function {NAME:funValues,type:"VALUES",expression:"RANDOM",VALUES:"
                                                                                                                                        0.0001,0.0000/0.0002,0.0101/0.0004,0.0202/0.0006,0.0303/0.0008,0.0404/
                                                                                                                                        0.0011,0.0505/0.0015,0.0606/0.0020,0.0707/0.0025,0.0808/0.0032,0.0909/
                                                                                                                                        0.0039,0.1010/0.0049,0.1111/0.0060,0.1212/0.0073,0.1313/0.0088,0.1414/
                                                                                                                                        0.0106,0.1515/0.0128,0.1616/0.0152,0.1717/0.0180,0.1818/0.0213,0.1919/
                                                                                                                                        0.0250,0.2020/0.0293,0.2121/0.0341,0.2222/0.0396,0.2323/0.0458,0.2424/
                                                                                                                                        0.0527,0.2525/0.0603,0.2626/0.0689,0.2727/0.0783,0.2828/0.0887,0.2929/
                                                                                                                                        0.1000,0.3030/0.1124,0.3131/0.1258,0.3232/0.1403,0.3333/0.1559,0.3434/
                                                                                                                                        0.1726,0.3535/0.1904,0.3636/0.2093,0.3737/0.2292,0.3838/0.2501,0.3939/
                                                                                                                                        0.2720,0.4040/0.2948,0.4141/0.3185,0.4242/0.3429,0.4343/0.3680,0.4444/
                                                                                                                                        0.3937,0.4545/0.4199,0.4646/0.4464,0.4747/0.4731,0.4848/0.5000,0.4949/
                                                                                                                                        0.5269,0.5051/0.5536,0.5152/0.5801,0.5253/0.6063,0.5354/0.6320,0.5455/
                                                                                                                                        0.6571,0.5556/0.6815,0.5657/0.7052,0.5758/0.7280,0.5859/0.7499,0.5960/
                                                                                                                                        0.7708,0.6061/0.7907,0.6162/0.8096,0.6263/0.8274,0.6364/0.8441,0.6465/
                                                                                                                                        0.8597,0.6566/0.8742,0.6667/0.8876,0.6768/0.9000,0.6869/0.9113,0.6970/
                                                                                                                                        0.9217,0.7071/0.9311,0.7172/0.9397,0.7273/0.9473,0.7374/0.9542,0.7475/
                                                                                                                                        0.9604,0.7576/0.9659,0.7677/0.9707,0.7778/0.9750,0.7879/0.9787,0.7980/
                                                                                                                                        0.9820,0.8081/0.9848,0.8182/0.9872,0.8283/0.9894,0.8384/0.9912,0.8485/
                                                                                                                                        0.9927,0.8586/0.9940,0.8687/0.9951,0.8788/0.9961,0.8889/0.9968,0.8990/
                                                                                                                                        0.9975,0.9091/0.9980,0.9192/0.9985,0.9293/0.9989,0.9394/0.9992,0.9495/
                                                                                                                                        0.9994,0.9596/0.9996,0.9697/0.9998,0.9798/0.9999,0.9899/1.0000,1.0000/
                                                                                                                                        "}
                                                                                                                                        
                                                                                                                                        START 500
                                                                                                                                        
                                                                                                                                        
                                                                                                                                        ;*****************************************************
                                                                                                                                        GENERATE 10,0,0,0 {NAME:GEN1,X:91,Y:383}
                                                                                                                                        
                                                                                                                                        ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                        SEIZE VENTANILLA1
                                                                                                                                        ADVANCE 20  + (FN$funValues * 20) ; definición clásica
                                                                                                                                        RELEASE VENTANILLA1
                                                                                                                                        ADVANCE 20,0 {TO:POS1}
                                                                                                                                        
                                                                                                                                        ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                        
                                                                                                                                        SEIZE VENTANILLA2
                                                                                                                                        ADVANCE FN$funGauss ; definición por parámetros
                                                                                                                                        ;ADVANCE 2 ; definición por parámetros
                                                                                                                                        RELEASE VENTANILLA2
                                                                                                                                        ADVANCE 20,0 {TO:POS2}
                                                                                                                                        
                                                                                                                                        ENDGENERATE 1
                                                                                                                                        
                                                                                                                                        • + Tipos de funciones de distribución

                                                                                                                                          El comando FUNCTION permite definir funciones que devuelven valores numéricos, ya sea mediante cálculos directos o a través de tablas de distribución precalculadas. Estas funciones se utilizan para simular comportamientos variables y realistas en operaciones como ADVANCE, ASSIGN, TABULATE, etc., y representan tiempos, cantidades, costes, probabilidades y más.

                                                                                                                                          En GPSS-Plus, las funciones pueden clasificarse en dos grandes grupos:

                                                                                                                                           

                                                                                                                                          1. Funciones basadas en distribuciones estadísticas (con tabla de valores)

                                                                                                                                          Estas funciones generan previamente una tabla interna con un número configurable de puntos (intervalos), sobre la que interpolan el resultado. Son ideales para representar comportamientos aleatorios con patrones conocidos. Los tipos disponibles son:

                                                                                                                                          • GAUSS: Distribución normal (campana de Gauss).

                                                                                                                                          • EXP: Distribución exponencial decreciente.

                                                                                                                                          • UNIFORM: Distribución uniforme (rectangular).

                                                                                                                                          • TRIANGULAR: Distribución triangular.

                                                                                                                                          • LOGNORMAL: Distribución log-normal, asimétrica hacia la derecha.

                                                                                                                                          • FDISTRIBUTION: Permite definir una función de distribución personalizada mediante una fórmula matemática.

                                                                                                                                           

                                                                                                                                          2. Funciones de cálculo directo (sin tabla)

                                                                                                                                          Estas funciones evalúan el resultado directamente, sin generar ni consultar una tabla. Son más flexibles cuando se desea trabajar con fórmulas o con modelos de conteo de eventos discretos. Incluyen:

                                                                                                                                          • POISSON: Genera un valor entero aleatorio según una distribución de Poisson (número de eventos por intervalo).

                                                                                                                                          • MATH: Evalúa directamente una expresión matemática. Admite múltiples parámetros que se sustituyen por letras (A, B, C, etc.) en orden.


                                                                                                                                          Example:
                                                                                                                                          Function {name:funGauss, type:"GAUSS", a:1, b:30, sigma1:3.3, sigma2:3.3,intervals:100 }
                                                                                                                                          
                                                                                                                                          Function {name:funExp, type:"EXP", b:0, lambda:0.15, intervals:100}
                                                                                                                                          
                                                                                                                                          Function {name:funUni, type:"UNIFORM", min:20, max:40, intervals:100}
                                                                                                                                          
                                                                                                                                          Function {name:funTri, type:"TRIANGULAR", min:20, max:40, mode:30, intervals:100}
                                                                                                                                          
                                                                                                                                          Function {name:funLog, type:"LOGNORMAL", mu:3.4, sigma:0.3, intervals:100}
                                                                                                                                          Function {name:funLog2, type:"LOGNORMAL", b:30, spread:5, intervals:100}
                                                                                                                                          
                                                                                                                                          Function {name:funCus, type:"FDISTRIBUTION", function:"A * Math.sin(B * x)", a:10, b:0.5, min:0, max:10, intervals:100}
                                                                                                                                          
                                                                                                                                          
                                                                                                                                          Function {name:funPoi, type:"POISSON", lambda:3.5}
                                                                                                                                          
                                                                                                                                          Function {Name:math1, type: math, EXPRESSION:"A * 2 + B"}
                                                                                                                                          
                                                                                                                                          • + Función Gauss

                                                                                                                                            La distribución gaussiana (o normal) representa una curva en forma de campana, donde los valores más probables están cerca de un valor central y los extremos tienen menor probabilidad. Es ideal para modelar fenómenos naturales como tiempos de atención, errores de medición o duración de procesos, donde la mayoría de los casos se agrupan alrededor de una media.

                                                                                                                                            Parámetros:

                                                                                                                                            • a: Altura de la curva (solo para visualización, no afecta al cálculo).
                                                                                                                                            • b: Valor central o media esperada del proceso.
                                                                                                                                            • sigma1: Dispersión (desviación estándar) hacia la izquierda del centro.
                                                                                                                                            • sigma2: Dispersión hacia la derecha del centro. Puede ser distinta a sigma1 para generar asimetría.
                                                                                                                                            • intervals: Número de tramos en que se divide la curva. A mayor número, más precisa.

                                                                                                                                            Por construcción, la distribución tomará valores aproximadamente entre b - sigma1 * 3 y b + sigma2 * 3.

                                                                                                                                            Este ejemplo muestra cómo tres ventanillas con funciones gaussianas diferentes producen distintas distribuciones de tiempos de servicio:

                                                                                                                                            ADVANCE FN$gfun1 ; simétrica
                                                                                                                                            ADVANCE FN$gfun2 ; dispersión derecha
                                                                                                                                            ADVANCE FN$gfun3 ; dispersión izquierda

                                                                                                                                             

                                                                                                                                             


                                                                                                                                            Example:
                                                                                                                                            /*
                                                                                                                                            
                                                                                                                                              	 	Gauss
                                                                                                                                             
                                                                                                                                            */
                                                                                                                                            SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                            
                                                                                                                                            Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                            
                                                                                                                                            Facility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                            
                                                                                                                                            Facility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                            
                                                                                                                                            
                                                                                                                                            POSITION {NAME:POS1,X:250,Y:381}
                                                                                                                                            POSITION {NAME:POS2,X:423,Y:383}
                                                                                                                                            POSITION {NAME:POS3,X:566,Y:384}
                                                                                                                                            
                                                                                                                                            Function {name:gfun1, TYPE:GAUSS, a:1, b:50, sigma1:3.3, sigma2:3.3,intervals:100 }
                                                                                                                                            Function {name:gfun2, TYPE:GAUSS, a:1, b:50, sigma1:1.0, sigma2:10.0,intervals:100 }
                                                                                                                                            Function {name:gfun3, TYPE:GAUSS, a:1, b:50, sigma1:10.0, sigma2:1.0,intervals:100 }
                                                                                                                                            
                                                                                                                                            
                                                                                                                                            START 500
                                                                                                                                            
                                                                                                                                            
                                                                                                                                            ;*****************************************************
                                                                                                                                            GENERATE 10,0,0,0 {NAME:GEN1,X:91,Y:383}
                                                                                                                                            
                                                                                                                                            ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                            SEIZE VENTANILLA1
                                                                                                                                            ADVANCE FN$gfun1
                                                                                                                                            RELEASE VENTANILLA1
                                                                                                                                            ADVANCE 20,0 {TO:POS1}
                                                                                                                                            
                                                                                                                                            ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                            
                                                                                                                                            SEIZE VENTANILLA2
                                                                                                                                            ADVANCE FN$gfun2
                                                                                                                                            RELEASE VENTANILLA2
                                                                                                                                            ADVANCE 20,0 {TO:POS2}
                                                                                                                                            
                                                                                                                                            ADVANCE 20,0 {TO:VENTANILLA3}
                                                                                                                                            
                                                                                                                                            SEIZE VENTANILLA3
                                                                                                                                            ADVANCE FN$gfun3
                                                                                                                                            RELEASE VENTANILLA3
                                                                                                                                            ADVANCE 20,0 {TO:POS3}
                                                                                                                                            
                                                                                                                                            
                                                                                                                                            ENDGENERATE 1
                                                                                                                                            • + Función Exponencial

                                                                                                                                              La distribución exponencial se utiliza para modelar eventos que ocurren de manera aleatoria pero con una probabilidad decreciente en el tiempo. Es ideal para situaciones como tiempos de espera, fallos de sistemas o llegadas al azar donde lo más probable es que algo ocurra al principio, y sea menos probable cuanto más se demore.

                                                                                                                                              Un ejemplo clásico: "Si estás esperando a ser atendido, es más probable que ocurra pronto; y cada minuto adicional reduce esa probabilidad."

                                                                                                                                              Esto no lo representa bien una distribución uniforme (como ADVANCE 10,5), pero sí una función exponencial, definida con el tipo:

                                                                                                                                              Function {name:efun1, type:"EXP", a:100, b:20, lambda:0.3, intervals:100}

                                                                                                                                              Parámetros

                                                                                                                                              • a: Altura de la curva (solo para visualización, no afecta al cálculo).

                                                                                                                                              • b: Valor base mínimo. Es el punto desde el cual comienza la distribución.

                                                                                                                                              • lambda: Tasa de decrecimiento. A mayor lambda, la caída es más rápida.

                                                                                                                                              • intervals: Número de tramos en los que se divide la curva (cuantos más, más precisa).

                                                                                                                                              Aproximadamente, el 99.9% de los valores estarán dentro del rango:

                                                                                                                                              • λ = 1 → de b a b + 6.91

                                                                                                                                              • λ = 0.5 → de b a b + 13.82

                                                                                                                                              • λ = 2 → de b a b + 3.45

                                                                                                                                              Ejemplo de uso comparativo:

                                                                                                                                              Function {Name:efun1, type:"EXP", a:100, b:20, lambda:0.3, intervals:70}
                                                                                                                                              Function {Name:efun2, type:"EXP", a:100, b:20, lambda:0.6, intervals:70}
                                                                                                                                              Function {Name:efun3, type:"EXP", a:100, b:20, lambda:1.0, intervals:70}
                                                                                                                                              
                                                                                                                                              ADVANCE FN$efun1 ; caída lenta
                                                                                                                                              ADVANCE FN$efun2 ; caída media
                                                                                                                                              ADVANCE FN$efun3 ; caída rápida

                                                                                                                                               

                                                                                                                                              Graph


                                                                                                                                              Example:
                                                                                                                                              /*
                                                                                                                                              
                                                                                                                                                	 	Exponential
                                                                                                                                               
                                                                                                                                              */
                                                                                                                                              SYSTEM {TYPE:OPTIONS, Speed:5}
                                                                                                                                              
                                                                                                                                              ;=== Facilities con seguimiento estadístico ===
                                                                                                                                              Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                              
                                                                                                                                              Facility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                              
                                                                                                                                              Facility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                              
                                                                                                                                              ;=== Posiciones finales para visualización ===
                                                                                                                                              POSITION {NAME:POS1, X:250, Y:381}
                                                                                                                                              POSITION {NAME:POS2, X:423, Y:383}
                                                                                                                                              POSITION {NAME:POS3, X:566, Y:384}
                                                                                                                                              
                                                                                                                                              ;=== EFunctions con distintas tasas de caída ===
                                                                                                                                              Function {Name:efun1, TYPE:EXP, a:100, b:20, Lambda:0.3, Intervals:70}
                                                                                                                                              Function {Name:efun2, TYPE:EXP, a:100, b:20, Lambda:0.6, Intervals:70}
                                                                                                                                              Function {Name:efun3, TYPE:EXP, a:100, b:20, Lambda:1.0, Intervals:70}
                                                                                                                                              
                                                                                                                                              ;=== Inicio de simulación ===
                                                                                                                                              START 500
                                                                                                                                              
                                                                                                                                              ;=== Flujo de entidades por las tres ventanillas ===
                                                                                                                                              GENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}
                                                                                                                                              
                                                                                                                                              ; VENTANILLA 1 – Caída lenta
                                                                                                                                              ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                              SEIZE VENTANILLA1
                                                                                                                                              ADVANCE FN$efun1
                                                                                                                                              RELEASE VENTANILLA1
                                                                                                                                              ADVANCE 20,0 {TO:POS1}
                                                                                                                                              
                                                                                                                                              ; VENTANILLA 2 – Caída media
                                                                                                                                              ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                              SEIZE VENTANILLA2
                                                                                                                                              ADVANCE FN$efun2
                                                                                                                                              RELEASE VENTANILLA2
                                                                                                                                              ADVANCE 20,0 {TO:POS2}
                                                                                                                                              
                                                                                                                                              ; VENTANILLA 3 – Caída rápida
                                                                                                                                              ADVANCE 20,0 {TO:VENTANILLA3}
                                                                                                                                              SEIZE VENTANILLA3
                                                                                                                                              ADVANCE FN$efun3
                                                                                                                                              RELEASE VENTANILLA3
                                                                                                                                              ADVANCE 20,0 {TO:POS3}
                                                                                                                                              
                                                                                                                                              ENDGENERATE 1
                                                                                                                                              
                                                                                                                                              • + Función Log-Normal

                                                                                                                                                ¿Qué es la distribución log-normal?

                                                                                                                                                Una variable sigue una distribución log-normal si el logaritmo de esa variable tiene una distribución normal. En otras palabras:

                                                                                                                                                “Muchos eventos pequeños ocurren frecuentemente, pero también es posible que aparezcan algunos eventos con valores muy grandes, aunque con baja probabilidad.”

                                                                                                                                                Casos típicos:

                                                                                                                                                • Tiempo de reparación de una máquina.

                                                                                                                                                • Duración de una llamada telefónica.

                                                                                                                                                • Ingresos económicos (la mayoría gana poco, unos pocos ganan mucho).

                                                                                                                                                Function {Name:lfunc1, Type:LOGNORMAL, b:30, sigma:0.1}

                                                                                                                                                Parámetros:

                                                                                                                                                • b: Valor más probable de la función (el pico de la curva).
                                                                                                                                                • sigma: Controla la dispersión de los valores hacia la derecha. A mayor sigma, más amplia es la “cola” de la distribución.

                                                                                                                                                GPSS-Plus convierte automáticamente estos valores a los parámetros matemáticos μ (media logarítmica) y σ (desviación estándar) para construir la curva log-normal.

                                                                                                                                                A diferencia de la gaussiana, la log-normal no es simétrica. Tiene una “cola” larga hacia valores más altos. Este comportamiento refleja bien los fenómenos donde los valores extremos son raros, pero posibles.

                                                                                                                                                Este ejemplo muestra tres funciones log-normales con el mismo tipo de curva, pero distinta dispersión.

                                                                                                                                                Function {Name:lfunc1, Type:LOGNORMAL, b:30, sigma:0.1}   ; curva estrecha
                                                                                                                                                Function {Name:lfunc2, Type:LOGNORMAL, b:20, sigma:0.15}  ; dispersión media
                                                                                                                                                Function {Name:lfunc3, Type:LOGNORMAL, b:40, sigma:0.2}   ; cola larga

                                                                                                                                                 

                                                                                                                                                Graph


                                                                                                                                                Example:
                                                                                                                                                /*
                                                                                                                                                
                                                                                                                                                  	 	Log-normal
                                                                                                                                                 
                                                                                                                                                */
                                                                                                                                                SYSTEM {TYPE:OPTIONS, Speed:5}
                                                                                                                                                
                                                                                                                                                Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}
                                                                                                                                                
                                                                                                                                                Facility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}
                                                                                                                                                
                                                                                                                                                Facility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}
                                                                                                                                                
                                                                                                                                                POSITION {NAME:POS1, X:250, Y:381}
                                                                                                                                                POSITION {NAME:POS2, X:423, Y:383}
                                                                                                                                                POSITION {NAME:POS3, X:566, Y:384}
                                                                                                                                                
                                                                                                                                                Function {Name:lfunc1, Type: LOGNORMAL, b:30, sigma:0.1}
                                                                                                                                                Function {Name:lfunc2, Type: LOGNORMAL, b:20, sigma:0.15}
                                                                                                                                                Function {Name:lfunc3, Type: LOGNORMAL, b:40, sigma:0.2}
                                                                                                                                                
                                                                                                                                                
                                                                                                                                                START 500
                                                                                                                                                
                                                                                                                                                GENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}
                                                                                                                                                
                                                                                                                                                ; VENTANILLA 1 – Dispersión pequeña
                                                                                                                                                ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                                SEIZE VENTANILLA1
                                                                                                                                                ADVANCE FN$lfunc1
                                                                                                                                                RELEASE VENTANILLA1
                                                                                                                                                ADVANCE 20,0 {TO:POS1}
                                                                                                                                                
                                                                                                                                                ; VENTANILLA 2 – Dispersión media
                                                                                                                                                ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                                SEIZE VENTANILLA2
                                                                                                                                                ADVANCE FN$lfunc2
                                                                                                                                                RELEASE VENTANILLA2
                                                                                                                                                ADVANCE 20,0 {TO:POS2}
                                                                                                                                                
                                                                                                                                                ; VENTANILLA 3 – Dispersión grande (cola larga)
                                                                                                                                                ADVANCE 20,0 {TO:VENTANILLA3}
                                                                                                                                                SEIZE VENTANILLA3
                                                                                                                                                ADVANCE FN$lfunc3
                                                                                                                                                RELEASE VENTANILLA3
                                                                                                                                                ADVANCE 20,0 {TO:POS3}
                                                                                                                                                
                                                                                                                                                ENDGENERATE 1
                                                                                                                                                
                                                                                                                                                
                                                                                                                                                • + Función Uniforme

                                                                                                                                                  La distribución uniforme representa la máxima incertidumbre: todos los valores dentro de un rango tienen exactamente la misma probabilidad.

                                                                                                                                                  "Es como lanzar un dado perfecto: ningún número tiene más posibilidades que otro."

                                                                                                                                                  Este tipo de distribución es útil cuando no hay razones para pensar que unos valores sean más probables que otros.

                                                                                                                                                  Parámetros

                                                                                                                                                  Function {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}
                                                                                                                                                  • Min: valor mínimo que puede tomar la función.

                                                                                                                                                  • Max: valor máximo que puede tomar la función.

                                                                                                                                                  Todos los valores entre Min y Max tienen la misma probabilidad.

                                                                                                                                                  En el gráfico del informe, la curva tiene forma de rectángulo plano. Cuanto mayor sea el rango, más dispersa será la distribución.

                                                                                                                                                  Este ejemplo compara tres distribuciones uniformes con distintos rangos. Todas tienen forma rectangular, pero difieren en amplitud y posición:

                                                                                                                                                  Function {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}   ; Rango pequeño
                                                                                                                                                  Function {Name:ufun2, TYPE:UNIFORM, Min:20, Max:60}   ; Rango amplio
                                                                                                                                                  Function {Name:ufun3, TYPE:UNIFORM, Min:30, Max:50}   ; Rango desplazado

                                                                                                                                                   

                                                                                                                                                  Graph


                                                                                                                                                  Example:
                                                                                                                                                  /*
                                                                                                                                                  
                                                                                                                                                    	 	Uniforme
                                                                                                                                                   
                                                                                                                                                  */
                                                                                                                                                  SYSTEM {TYPE:OPTIONS, Speed:5}
                                                                                                                                                  
                                                                                                                                                  Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                  
                                                                                                                                                  Facility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                  
                                                                                                                                                  Facility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                  
                                                                                                                                                  POSITION {NAME:POS1, X:250, Y:381}
                                                                                                                                                  POSITION {NAME:POS2, X:423, Y:383}
                                                                                                                                                  POSITION {NAME:POS3, X:566, Y:384}
                                                                                                                                                  
                                                                                                                                                  Function {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}
                                                                                                                                                  Function {Name:ufun2, TYPE:UNIFORM, Min:20, Max:60}
                                                                                                                                                  Function {Name:ufun3, TYPE:UNIFORM, Min:30, Max:50}
                                                                                                                                                  
                                                                                                                                                  START 500
                                                                                                                                                  
                                                                                                                                                  GENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}
                                                                                                                                                  
                                                                                                                                                  ; VENTANILLA 1 – Uniforme de 20 a 40
                                                                                                                                                  ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                                  SEIZE VENTANILLA1
                                                                                                                                                  ADVANCE FN$ufun1
                                                                                                                                                  RELEASE VENTANILLA1
                                                                                                                                                  ADVANCE 20,0 {TO:POS1}
                                                                                                                                                  
                                                                                                                                                  ; VENTANILLA 2 – Uniforme de 20 a 60
                                                                                                                                                  ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                                  SEIZE VENTANILLA2
                                                                                                                                                  ADVANCE FN$ufun2
                                                                                                                                                  RELEASE VENTANILLA2
                                                                                                                                                  ADVANCE 20,0 {TO:POS2}
                                                                                                                                                  
                                                                                                                                                  ; VENTANILLA 3 – Uniforme de 30 a 50
                                                                                                                                                  ADVANCE 20,0 {TO:VENTANILLA3}
                                                                                                                                                  SEIZE VENTANILLA3
                                                                                                                                                  ADVANCE FN$ufun3
                                                                                                                                                  RELEASE VENTANILLA3
                                                                                                                                                  ADVANCE 20,0 {TO:POS3}
                                                                                                                                                  
                                                                                                                                                  TERMINATE 1
                                                                                                                                                  
                                                                                                                                                  
                                                                                                                                                  
                                                                                                                                                  • + Función Triangular

                                                                                                                                                    La distribución triangular permite definir una estimación mínima, máxima y más probable (modo) de un valor. Tiene forma de triángulo, donde el valor del modo es el más frecuente y los extremos son menos probables.

                                                                                                                                                    Es útil cuando tienes una idea aproximada del comportamiento de un proceso, pero no datos suficientes para definir una distribución más compleja.

                                                                                                                                                    “Una entrega normalmente tarda 3 días, pero podría hacerlo en 2 o en 5.”

                                                                                                                                                    Ese tipo de incertidumbre es ideal para una distribución triangular.

                                                                                                                                                    Parámetros

                                                                                                                                                    Function {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30}
                                                                                                                                                    

                                                                                                                                                     

                                                                                                                                                    • Min: Valor mínimo posible.
                                                                                                                                                    • Max: Valor máximo posible.
                                                                                                                                                    • Mode: Valor más probable (pico de la distribución).

                                                                                                                                                    Este ejemplo compara tres funciones triangulares con diferentes posiciones del pico. Todas con el mismo rango, pero con distintas distribuciones:

                                                                                                                                                     

                                                                                                                                                    Function {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30} ; Pico centrado
                                                                                                                                                    Function {Name:tfun2, TYPE:TRIANGULAR, Min:20, Max:40, Mode:25} ; Pico sesgado a la izquierda
                                                                                                                                                    Function {Name:tfun3, TYPE:TRIANGULAR, Min:20, Max:40, Mode:35} ; Pico sesgado a la derecha

                                                                                                                                                     

                                                                                                                                                    Graph


                                                                                                                                                    Example:
                                                                                                                                                    /*
                                                                                                                                                    
                                                                                                                                                      	 	Triangular
                                                                                                                                                     
                                                                                                                                                    */
                                                                                                                                                    SYSTEM {TYPE:OPTIONS, Speed:5}
                                                                                                                                                    
                                                                                                                                                    Facility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                    
                                                                                                                                                    Facility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                    
                                                                                                                                                    Facility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}
                                                                                                                                                    
                                                                                                                                                    POSITION {NAME:POS1, X:250, Y:381}
                                                                                                                                                    POSITION {NAME:POS2, X:423, Y:383}
                                                                                                                                                    POSITION {NAME:POS3, X:566, Y:384}
                                                                                                                                                    
                                                                                                                                                    Function {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30}
                                                                                                                                                    Function {Name:tfun2, TYPE:TRIANGULAR, Min:20, Max:40, Mode:25}
                                                                                                                                                    Function {Name:tfun3, TYPE:TRIANGULAR, Min:20, Max:40, Mode:35}
                                                                                                                                                    
                                                                                                                                                    START 500
                                                                                                                                                    
                                                                                                                                                    GENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}
                                                                                                                                                    
                                                                                                                                                    ; VENTANILLA 1 – Triángulo centrado
                                                                                                                                                    ADVANCE 20,4 {TO:VENTANILLA1}
                                                                                                                                                    SEIZE VENTANILLA1
                                                                                                                                                    ADVANCE FN$tfun1
                                                                                                                                                    RELEASE VENTANILLA1
                                                                                                                                                    ADVANCE 20,0 {TO:POS1}
                                                                                                                                                    
                                                                                                                                                    ; VENTANILLA 2 – Triángulo con pico a la izquierda
                                                                                                                                                    ADVANCE 20,0 {TO:VENTANILLA2}
                                                                                                                                                    SEIZE VENTANILLA2
                                                                                                                                                    ADVANCE FN$tfun2
                                                                                                                                                    RELEASE VENTANILLA2
                                                                                                                                                    ADVANCE 20,0 {TO:POS2}
                                                                                                                                                    
                                                                                                                                                    ; VENTANILLA 3 – Triángulo con pico a la derecha
                                                                                                                                                    ADVANCE 20,0 {TO:VENTANILLA3}
                                                                                                                                                    SEIZE VENTANILLA3
                                                                                                                                                    ADVANCE FN$tfun3
                                                                                                                                                    RELEASE VENTANILLA3
                                                                                                                                                    ADVANCE 20,0 {TO:POS3}
                                                                                                                                                    
                                                                                                                                                    ENDGENERATE 1
                                                                                                                                                    
                                                                                                                                                    
                                                                                                                                                    
                                                                                                                                                    
                                                                                                                                                    • + Función Poisson

                                                                                                                                                      La distribución Poisson modela la cantidad de eventos que ocurren en un intervalo de tiempo fijo, siempre que:

                                                                                                                                                      • Los eventos ocurren independientemente.

                                                                                                                                                      • La tasa media de ocurrencia (λ) es constante.

                                                                                                                                                       

                                                                                                                                                      ¿Cuándo usar Poisson?

                                                                                                                                                       

                                                                                                                                                      • Número de llamadas por minuto en un call center.

                                                                                                                                                      • Llegadas de clientes a una tienda.

                                                                                                                                                      • Errores de fabricación por día.

                                                                                                                                                      Parámetro:

                                                                                                                                                      λ (lambda): número medio esperado de eventos por intervalo.

                                                                                                                                                      Por ejemplo, si λ = 3, en promedio habrá 3 eventos por unidad de tiempo, aunque puede haber 2, 4, 0, etc. La variabilidad está incluida en la distribución.

                                                                                                                                                       

                                                                                                                                                      Relación con la distribución exponencial

                                                                                                                                                      • Poisson cuenta cuántos eventos ocurren.

                                                                                                                                                      • Exponencial mide el tiempo entre eventos.

                                                                                                                                                       

                                                                                                                                                      Ambas están controladas por el mismo parámetro λ, y son complementarias:

                                                                                                                                                      Poisson(λ):      Número de eventos por intervalo    λ
                                                                                                                                                      Exponencial(λ):  Tiempo entre eventos consecutivos    1 / λ

                                                                                                                                                      ¿Qué muestra este ejemplo?

                                                                                                                                                      Comparación visual de ambas distribuciones para 3 valores distintos de λ:

                                                                                                                                                      Function {Name:fPoisson1, TYPE:POISSON, LAMBDA:X$LAMBDA1}
                                                                                                                                                      Function {Name:fPoisson2, TYPE:POISSON, LAMBDA:X$LAMBDA2}
                                                                                                                                                      Function {Name:fPoisson3, TYPE:POISSON, LAMBDA:X$LAMBDA3}
                                                                                                                                                      
                                                                                                                                                      Function {Name:fexp1, TYPE:EXP, LAMBDA:X$LAMBDA1, b:0}
                                                                                                                                                      Function {Name:fexp2, TYPE:EXP, LAMBDA:X$LAMBDA2, b:0}
                                                                                                                                                      Function {Name:fexp3, TYPE:EXP, LAMBDA:X$LAMBDA3, b:0}

                                                                                                                                                      Los resultados se visualizan en texto dinámico con la media acumulada:

                                                                                                                                                      Tendencia a λ = 3.0 → Media ≈ 3.0
                                                                                                                                                      Tendencia a λ = 0.25 → Media ≈ 0.25
                                                                                                                                                      
                                                                                                                                                      Exp = 1 / λ = 0.333 → Media ≈ 0.333

                                                                                                                                                      Esta simulación sirve para entender visualmente cómo:

                                                                                                                                                      • Poisson estabiliza el número de eventos hacia λ.

                                                                                                                                                      • Exponencial estabiliza el tiempo entre eventos hacia 1 / λ.

                                                                                                                                                       

                                                                                                                                                      Graph

                                                                                                                                                       

                                                                                                                                                       

                                                                                                                                                       

                                                                                                                                                       


                                                                                                                                                      Example:
                                                                                                                                                      /*
                                                                                                                                                      
                                                                                                                                                        	 	Poisson
                                                                                                                                                       
                                                                                                                                                      */
                                                                                                                                                      SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                                      
                                                                                                                                                      Graphic {NAME:pText1,Type:TEXT,X:309,Y:281,Text:"Media1"}
                                                                                                                                                      Graphic {NAME:pText2,Type:TEXT,X:309,Y:238,Text:"Media2"}
                                                                                                                                                      Graphic {NAME:pText3,Type:TEXT,X:308,Y:198,Text:"Media3"}
                                                                                                                                                      
                                                                                                                                                      Graphic {NAME:eText1,Type:TEXT,X:506,Y:280,Text:"Exp1"}
                                                                                                                                                      Graphic {NAME:eText2,Type:TEXT,X:506,Y:238,Text:"Exp2"}
                                                                                                                                                      Graphic {NAME:eText3,Type:TEXT,X:504,Y:196,Text:"Exp3"}
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      POSITION {NAME:POS1,X:598,Y:395}
                                                                                                                                                      INITIAL contador,0
                                                                                                                                                      
                                                                                                                                                      INITIAL LAMBDA1,1/4
                                                                                                                                                      INITIAL iLAMBDA1,round(1/X$LAMBDA1,3)
                                                                                                                                                      INITIAL pTotal1,0
                                                                                                                                                      INITIAL eTotal1,0
                                                                                                                                                      
                                                                                                                                                      INITIAL LAMBDA2,3/4
                                                                                                                                                      INITIAL iLAMBDA2,round(1/X$LAMBDA2,3)
                                                                                                                                                      INITIAL pTotal2,0
                                                                                                                                                      INITIAL eTotal2,0
                                                                                                                                                      
                                                                                                                                                      INITIAL LAMBDA3,3
                                                                                                                                                      INITIAL iLAMBDA3,round(1/X$LAMBDA3,3)
                                                                                                                                                      INITIAL pTotal3,0
                                                                                                                                                      INITIAL eTotal3,0
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      Function {Name:fPoisson1, TYPE:POISSON, LAMBDA:X$LAMBDA1}
                                                                                                                                                      Function {Name:fPoisson2, TYPE:POISSON, LAMBDA:X$LAMBDA2}
                                                                                                                                                      Function {Name:fPoisson3, TYPE:POISSON, LAMBDA:X$LAMBDA3}
                                                                                                                                                      
                                                                                                                                                      Function {Name:fexp1, TYPE:EXP, LAMBDA:X$LAMBDA1, b:0}
                                                                                                                                                      Function {Name:fexp2, TYPE:EXP, LAMBDA:X$LAMBDA2, b:0}
                                                                                                                                                      Function {Name:fexp3, TYPE:EXP, LAMBDA:X$LAMBDA3, b:0}
                                                                                                                                                      
                                                                                                                                                      START 2000
                                                                                                                                                      
                                                                                                                                                      ;*****************************************************
                                                                                                                                                      GENERATE 2,0,0,0 {NAME:GEN1,X:218,Y:395}
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE contador,X$contador + 1
                                                                                                                                                      
                                                                                                                                                      ; --- Acumulation Poisson ---
                                                                                                                                                      SAVEVALUE pTotal1,X$pTotal1 + FN$fPoisson1
                                                                                                                                                      SAVEVALUE pMedia1, X$pTotal1 / X$contador
                                                                                                                                                      SAVEVALUE pMediaRound1, round(X$pMedia1,3)
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE pTotal2,X$pTotal2 + FN$fPoisson2
                                                                                                                                                      SAVEVALUE pMedia2, X$pTotal2 / X$contador
                                                                                                                                                      SAVEVALUE pMediaRound2, round(X$pMedia2,3)
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE pTotal3,X$pTotal3 + FN$fPoisson3
                                                                                                                                                      SAVEVALUE pMedia3, X$pTotal3 / X$contador
                                                                                                                                                      SAVEVALUE pMediaRound3, round(X$pMedia3,3)
                                                                                                                                                      
                                                                                                                                                      ; --- Acumulation Exponentian ---
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE eTotal1,X$eTotal1 + FN$fexp1
                                                                                                                                                      SAVEVALUE eMedia1, X$eTotal1 / X$contador
                                                                                                                                                      SAVEVALUE eMediaRound1, round(X$eMedia1,3)
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE eTotal2,X$eTotal2 + FN$fexp2
                                                                                                                                                      SAVEVALUE eMedia2, X$eTotal2 / X$contador
                                                                                                                                                      SAVEVALUE eMediaRound2, round(X$eMedia2,3)
                                                                                                                                                      
                                                                                                                                                      SAVEVALUE eTotal3,X$eTotal3 + FN$fexp3
                                                                                                                                                      SAVEVALUE eMedia3, X$eTotal3 / X$contador
                                                                                                                                                      SAVEVALUE eMediaRound3, round(X$eMedia3,3)
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      move {name:pText1,text:"Tendencia a Lambda = X$LAMBDA1  -->  X$pMediaRound1"}
                                                                                                                                                      move {name:pText2,text:"Tendencia a Lambda = X$LAMBDA2  -->  X$pMediaRound2"}
                                                                                                                                                      move {name:pText3,text:"Tendencia a Lambda = X$LAMBDA3  -->  X$pMediaRound3"}
                                                                                                                                                      
                                                                                                                                                      move {name:eText1,text:"Exp = 1/X$LAMBDA1 = X$iLAMBDA1 -->  X$eMediaRound1"}
                                                                                                                                                      move {name:eText2,text:"Exp = 1/X$LAMBDA2 = X$iLAMBDA2 -->  X$eMediaRound2"}
                                                                                                                                                      move {name:eText3,text:"Exp = 1/X$LAMBDA3 = X$iLAMBDA3 -->  X$eMediaRound3"}
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      ADVANCE 30,10 {TO:POS1}
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      ENDGENERATE 1
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      
                                                                                                                                                      • + Funciones tipo FDISTRIBUTION

                                                                                                                                                        Distribuciones personalizadas

                                                                                                                                                        El tipo FDISTRIBUTION permite definir funciones de distribución completamente personalizadas mediante una expresión matemática, sin limitarse a fórmulas predefinidas como GAUSS, EXP o TRIANGULAR.

                                                                                                                                                        Estas funciones generan una tabla de distribución internamente, evaluando una expresión matemática en un rango determinado. Es ideal para representar distribuciones no estándar o variantes de las clásicas.

                                                                                                                                                         

                                                                                                                                                        ¿Cómo funciona?

                                                                                                                                                        GPSS-Plus construye una tabla de distribución basada en:

                                                                                                                                                        • Una expresión matemática que define la forma de la función (como una campana, un triángulo, etc.)

                                                                                                                                                        • Un rango de valores (min, max) donde se evalúa dicha función

                                                                                                                                                        • Un número de intervals que determina la resolución de la tabla

                                                                                                                                                        • Parámetros opcionales (A, B, C...) que puedes usar dentro de la fórmula

                                                                                                                                                        Una vez construida la tabla, la función actúa como cualquier otra distribución: se toma un valor aleatorio, se consulta la tabla y se devuelve el resultado correspondiente.

                                                                                                                                                        Sintaxis:

                                                                                                                                                        Function {
                                                                                                                                                          Name: nombre,
                                                                                                                                                          Type: FDISTRIBUTION,
                                                                                                                                                          Expression: "fórmula",
                                                                                                                                                          min: valorMinimo,
                                                                                                                                                          max: valorMaximo,
                                                                                                                                                          intervals: cantidad,
                                                                                                                                                          A: valor, B: valor, ...
                                                                                                                                                        }

                                                                                                                                                        Ejemplo 1: Distribución gaussiana personalizada

                                                                                                                                                        Function {
                                                                                                                                                          Name:gauss1,
                                                                                                                                                          Type: FDISTRIBUTION,
                                                                                                                                                          EXPRESSION: "(1 / (A * SQRT(2 * PI))) * EXP(-0.5 * ((X - B) / A)^2)",
                                                                                                                                                          A: 2.3,
                                                                                                                                                          B: 30,
                                                                                                                                                          min: B - (3 * A),
                                                                                                                                                          max: B + (3 * A),
                                                                                                                                                          intervals: 100
                                                                                                                                                        }

                                                                                                                                                        Este ejemplo crea una función tipo campana centrada en 30, con dispersión 2.3. Es equivalente a una gaussiana clásica, pero totalmente configurable.

                                                                                                                                                        Ejemplo 2: Distribución triangular definida por fórmula

                                                                                                                                                        Function {
                                                                                                                                                          Name:tri1,
                                                                                                                                                          Type: FDISTRIBUTION,
                                                                                                                                                          EXPRESSION: "(1 / C) * (1 - ABS((X - B) / (C / 2)))",
                                                                                                                                                          B: 25, C: 30,
                                                                                                                                                          min: B - C / 2,
                                                                                                                                                          max: B + C / 2,
                                                                                                                                                          intervals: 100
                                                                                                                                                        }

                                                                                                                                                        Esta función genera una distribución triangular con pico en 25, anchura total de 30, y base de [10, 40].

                                                                                                                                                        Detalles técnicos:

                                                                                                                                                        • La variable X es el eje horizontal (dominio).

                                                                                                                                                        • La función se evalúa intervals veces entre min y max.

                                                                                                                                                        • La integral se normaliza automáticamente para comportarse como una distribución.

                                                                                                                                                        • Todos los parámetros (A, B, C, etc.) pueden ser expresiones.

                                                                                                                                                         

                                                                                                                                                         


                                                                                                                                                        Example:
                                                                                                                                                        /*
                                                                                                                                                        
                                                                                                                                                          	 	FDistribution
                                                                                                                                                         
                                                                                                                                                        */
                                                                                                                                                        SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                                        
                                                                                                                                                        Facility {NAME:VENTANILLA1,X:244,Y:190,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:20}
                                                                                                                                                        
                                                                                                                                                        Facility {NAME:VENTANILLA2,X:412,Y:188,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:20}
                                                                                                                                                        
                                                                                                                                                        POSITION {NAME:POS1,X:200,Y:387}
                                                                                                                                                        POSITION {NAME:POS2,X:341,Y:381}
                                                                                                                                                        POSITION {NAME:POS3,X:507,Y:380}
                                                                                                                                                        
                                                                                                                                                        ;GAUSSIANA
                                                                                                                                                        Function {Name:tName1, type: FDISTRIBUTION, EXPRESSION:"(1 / (A * SQRT(2 * PI))) * EXP(-0.5 * ((X - B) / A)^2)", b:30,  a: 2.3, min:B - (3 * A), max:B + (3 * A),intervals:100}
                                                                                                                                                        
                                                                                                                                                        ; TRIANGULAR B: centro del rango C: ancho total del rango
                                                                                                                                                        Function {Name:tName2, A:0, B:25, C:30, D:0, type: FDISTRIBUTION, EXPRESSION:"(1 / C) * (1 - ABS((X - B) / (C / 2)))", min:B - C/2, max:B + C/2, intervals:100} 
                                                                                                                                                        
                                                                                                                                                        START 1000
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        ;*****************************************************
                                                                                                                                                        GENERATE 2,0,0,0 {NAME:GEN1,X:56,Y:319}
                                                                                                                                                        
                                                                                                                                                        ADVANCE 20,0 {TO:POS1}
                                                                                                                                                        
                                                                                                                                                        ADVANCE 30,10 {TO:VENTANILLA1}
                                                                                                                                                        
                                                                                                                                                        SEIZE VENTANILLA1
                                                                                                                                                        ADVANCE FN$tName1
                                                                                                                                                        RELEASE VENTANILLA1
                                                                                                                                                        
                                                                                                                                                        ADVANCE 20,0 {TO:POS2}
                                                                                                                                                        
                                                                                                                                                        ADVANCE 20,10 {TO:VENTANILLA2}
                                                                                                                                                        
                                                                                                                                                        SEIZE VENTANILLA2
                                                                                                                                                        ADVANCE FN$tName2
                                                                                                                                                        RELEASE VENTANILLA2
                                                                                                                                                        
                                                                                                                                                        ADVANCE 20,0 {TO:POS3}
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        ENDGENERATE 1
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        
                                                                                                                                                        • + Funciones tipo MATH

                                                                                                                                                          Fórmulas matemáticas personalizadas

                                                                                                                                                          El tipo MATH permite definir funciones que se evalúan como fórmulas matemáticas directas, sin utilizar distribuciones ni tablas de valores precalculadas. Son útiles cuando el valor que necesitamos depende de variables de forma determinista o con una expresión algebraica.

                                                                                                                                                          Este tipo de función no genera aleatoriedad. Simplemente toma los parámetros indicados en orden, los sustituye en la expresión y devuelve el resultado.

                                                                                                                                                          Sintaxis:

                                                                                                                                                          Function {Name:nombre, Type:MATH, Expression:"A + B * C"}

                                                                                                                                                           

                                                                                                                                                          • Name: nombre identificador de la función.
                                                                                                                                                          • Type: debe ser MATH.
                                                                                                                                                          • Expression: fórmula a evaluar, usando letras mayúsculas como variables (A, B, C...). Estas letras representan los parámetros pasados en orden.

                                                                                                                                                          ¿Cómo se llama una función MATH?

                                                                                                                                                          Se utiliza como cualquier función FN$, pero con parámetros entre paréntesis:

                                                                                                                                                          FN$(nombre, valor_A, valor_B, ...)

                                                                                                                                                           

                                                                                                                                                           

                                                                                                                                                           

                                                                                                                                                           


                                                                                                                                                          Example:
                                                                                                                                                          /*
                                                                                                                                                          
                                                                                                                                                            	 	Math
                                                                                                                                                           
                                                                                                                                                          */
                                                                                                                                                          Graphic {NAME:Text1,Type:TEXT,X:370,Y:312,Text:"Hello"}
                                                                                                                                                          
                                                                                                                                                          Function {Name:math1, type: MATH, EXPRESSION:"A * 2 + B"}
                                                                                                                                                          
                                                                                                                                                          START 1
                                                                                                                                                          ;*****************************************************
                                                                                                                                                          GENERATE 1,0,0,1 {NAME:GEN1,X:56,Y:319}
                                                                                                                                                          
                                                                                                                                                          assign AAA,4
                                                                                                                                                          assign BBB,6
                                                                                                                                                          
                                                                                                                                                          move {name:Text1,text:"Result: FN$(math1,P$AAA,P$BBB)"}
                                                                                                                                                          
                                                                                                                                                          ENDGENERATE 1
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                          
                                                                                                                                                        • + Season 10: Sistemas continuos
                                                                                                                                                          • + Sistema continuo dentro del discreto

                                                                                                                                                            Hasta ahora hemos visto cómo GPSS-Plus es, en su esencia, un motor de simulación de eventos discretos. Pero también es capaz de simular sistemas continuos, es decir, aquellos en los que los cambios ocurren de forma gradual a lo largo del tiempo.

                                                                                                                                                            ¿Qué es un sistema continuo?

                                                                                                                                                            Un sistema continuo es aquel en el que los valores cambian sin interrupciones. Por ejemplo:

                                                                                                                                                            • El nivel de un depósito que se va llenando.

                                                                                                                                                            • La temperatura de un horno que se calienta.

                                                                                                                                                            • La velocidad de un coche acelerando suavemente.

                                                                                                                                                            En estos casos no hay “saltos” puntuales como los de una cola o un cliente que entra o sale. El cambio ocurre de forma progresiva.

                                                                                                                                                            ¿Y cómo simular eso en un entorno discreto?

                                                                                                                                                            La respuesta es: haciendo trampa, pero de forma inteligente. En lugar de simular un cambio constante y fluido, lo dividimos en pequeños pasos temporales, lo bastante rápidos como para que parezca continuo.
                                                                                                                                                            Es como el cine: cada fotograma es una imagen estática, pero al reproducirse 24 por segundo, vemos movimiento.

                                                                                                                                                            ¿Cómo se hace esto en GPSS-Plus?

                                                                                                                                                            Con un ajustes clave:

                                                                                                                                                            SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}
                                                                                                                                                            

                                                                                                                                                             

                                                                                                                                                            • TIME_DECIMALS:1 indica que el tiempo se mide con un decimal (0.1).
                                                                                                                                                            • SPEED:5 hace que el sistema se actualice con una interpretación media. Del 0 que indica pausa a 10 que es el máximo de velocidad. La interpretación media (5) es que 10 instantes AC1 tandan aproximadamente 1 segundo. Con SPEED:2, 1 segundo equivale aproximadamente a 1 AC1.

                                                                                                                                                            El motor sigue siendo discreto. El tiempo no avanza por sí solo; avanza cuando se atiende una entidad o se dispara un evento. Lo que hacemos es generar muchos eventos pequeños, muy seguidos.

                                                                                                                                                             

                                                                                                                                                            Ejemplo: Llenado de un depósito

                                                                                                                                                            Vamos a llenar un depósito con un caudal variable (por ejemplo, siguiendo una función seno para hacerlo más dinámico).

                                                                                                                                                            Para ello, usamos un timer, que lanza un bloque de código cada 0.1 unidades de tiempo. No genera transacciones ni estadísticas: solo ejecuta.

                                                                                                                                                            SYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}

                                                                                                                                                            Cada vez que se activa el PROCEDURE, haremos esto:

                                                                                                                                                            1. Calcular un caudal según una función oscilante, definida previamente con FUNCTION TYPE:MATH.

                                                                                                                                                            2. Añadir ese caudal al nivel actual del depósito.

                                                                                                                                                            3. Actualizar visualmente el nivel.

                                                                                                                                                            4. Mostrar el valor en pantalla.

                                                                                                                                                            5. Si el depósito se llena, detener la simulación.

                                                                                                                                                             

                                                                                                                                                            En ese caso, cada ciclo calculaba el caudal y lo sumaba al nivel con una fórmula sencilla:

                                                                                                                                                            SAVEVALUE nivel, X$nivel + X$caudal * 0.1
                                                                                                                                                            

                                                                                                                                                            Este método se llama método de Euler, y es la forma más simple de integración numérica. 

                                                                                                                                                            Estima que el intervalo de tiempo entre AC1$ y AC1$ + 0.1 el depósito se va a llenar con la cantidad de caudal en el momento AC1$ multiplicado por el intervalo 0.1

                                                                                                                                                            En el siguiente paso, volverá a hacer el mismo cálculo, usando el nuevo valor del caudal en ese instante. Y así sucesivamente, sumando pequeñas cantidades como si fuera una suma de rectángulos: eso es, en esencia, una integral.

                                                                                                                                                             


                                                                                                                                                            Example:
                                                                                                                                                            /*
                                                                                                                                                            
                                                                                                                                                             Sistema continuo dentro del discreto
                                                                                                                                                             
                                                                                                                                                            */
                                                                                                                                                            SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                            SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}
                                                                                                                                                            SYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}
                                                                                                                                                            
                                                                                                                                                            Graphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:"aviso"}
                                                                                                                                                            
                                                                                                                                                            Function {Name:fcCaudal, Type:Math, Expression:"0.6 + SIN(A) * 4"}
                                                                                                                                                            
                                                                                                                                                            INITIAL nivel, 0
                                                                                                                                                            INITIAL caudal, 1.2
                                                                                                                                                            INITIAL fase, 0
                                                                                                                                                            INITIAL constante, 0.6
                                                                                                                                                            
                                                                                                                                                            include ./library_graphics/speedometer.lib
                                                                                                                                                            
                                                                                                                                                            START 100
                                                                                                                                                            
                                                                                                                                                            PROCEDURE PRE_RUN
                                                                                                                                                            	assign config,{title:"Depósito"
                                                                                                                                                                		,x:100,y:60 
                                                                                                                                                                        ,width:100 ,height:180
                                                                                                                                                            	       	,min_value: 0
                                                                                                                                                                		,max_value: 100
                                                                                                                                                                        ,"color":"#ff0000"}
                                                                                                                                                                        
                                                                                                                                                            	call deposito.speedometer.init,V$config
                                                                                                                                                            
                                                                                                                                                            	assign config,{title:"Caudal"
                                                                                                                                                                		,x:300,y:60 
                                                                                                                                                                        ,width:100 ,height:180
                                                                                                                                                            	       	,min_value: -10
                                                                                                                                                                		,max_value: 10
                                                                                                                                                                        ,"color":"orange"}
                                                                                                                                                                        
                                                                                                                                                            	call caudal.speedometer.init,V$config
                                                                                                                                                            
                                                                                                                                                            TERMINATE_VE 
                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                            
                                                                                                                                                            PROCEDURE llenado
                                                                                                                                                                ; Oscilar caudal como una función del tiempo simulado
                                                                                                                                                            ;    SAVEVALUE caudal, X$constante + SIN(X$fase) * 4
                                                                                                                                                                SAVEVALUE caudal, FN$(fcCaudal,X$fase)
                                                                                                                                                            
                                                                                                                                                            
                                                                                                                                                                ; Aumentamos la fase suavemente
                                                                                                                                                                SAVEVALUE fase, X$fase + 0.1
                                                                                                                                                            
                                                                                                                                                                ; Sumamos el caudal al nivel, según el intervalo de tiempo
                                                                                                                                                                SAVEVALUE nivel, X$nivel + X$caudal * 0.1
                                                                                                                                                            
                                                                                                                                                                ; Actualizamos visualmente la línea del depósito
                                                                                                                                                            
                                                                                                                                                            
                                                                                                                                                                ; Valores redondeados para mostrar
                                                                                                                                                                ASSIGN tCaudal, round(X$caudal, 2)
                                                                                                                                                                ASSIGN tNivel, round(X$nivel, 2)
                                                                                                                                                            	CALL deposito.speedometer.set, P$tNivel
                                                                                                                                                            	CALL caudal.speedometer.set, P$tCaudal
                                                                                                                                                                
                                                                                                                                                                ; Visualización del estado
                                                                                                                                                                IF (X$nivel>=100)
                                                                                                                                                                    MOVE {name:aviso, text:"¡DEPÓSITO LLENO!"}
                                                                                                                                                                    STOP
                                                                                                                                                                ELSE
                                                                                                                                                                    MOVE {name:aviso, text:"Nivel: P$tNivel - Caudal: P$tCaudal"}
                                                                                                                                                                ENDIF
                                                                                                                                                            TERMINATE
                                                                                                                                                            ENDPROCEDURE 1
                                                                                                                                                            
                                                                                                                                                            • + Recolección de datos PLOT

                                                                                                                                                              En el capítulo anterior vimos cómo GPSS-Plus puede simular comportamientos aparentemente continuos mediante el uso de TIMERs y pasos de tiempo muy pequeños. Observamos, por ejemplo, cómo se llenaba un depósito en pantalla, simulando un proceso constante.

                                                                                                                                                              Ahora vamos un paso más allá:
                                                                                                                                                              No solo vamos a ver lo que ocurre… vamos a registrarlo y graficarlo automáticamente para su análisis posterior.

                                                                                                                                                              Porque, en muchos casos, no basta con observar cómo se mueve algo: necesitamos tener una curva, un gráfico, un historial del comportamiento.
                                                                                                                                                              Para eso existe en GPSS-Plus el sistema de trazado de datos:

                                                                                                                                                              PLOTTER y PLOT

                                                                                                                                                              • PLOTTER {}: declara una tabla gráfica donde se irán guardando valores.

                                                                                                                                                              • PLOT: añade un punto a esa tabla, usando el tiempo actual u otra variable como eje X.

                                                                                                                                                              Cada punto añadido es como un “fotograma” del sistema, y al terminar la simulación, GPSS-Plus dibuja una curva con todos los puntos registrados.

                                                                                                                                                              Ejemplo de uso

                                                                                                                                                              En nuestro caso vamos a registrar:

                                                                                                                                                              PLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal, X:TIME_AC1}
                                                                                                                                                              

                                                                                                                                                              Esto significa:

                                                                                                                                                              • Vamos a graficar dos series de valores: nivel y caudal.

                                                                                                                                                              • El eje horizontal (X) será el tiempo (AC1$), llamado TIME_AC1.

                                                                                                                                                              Para añadir puntos en cada instante:

                                                                                                                                                              PLOT thePlot, X$nivel, X$caudal
                                                                                                                                                              

                                                                                                                                                              Esto se ejecuta dentro del PROCEDURE, en cada llamada, y va dibujando la historia del proceso.

                                                                                                                                                              "NIVEL se obtiene acumulando el CAUDAL. El sistema simula una integración continua."
                                                                                                                                                              "Cada punto corresponde a un instante generado automáticamente por el TIMER."

                                                                                                                                                               

                                                                                                                                                               


                                                                                                                                                              Example:
                                                                                                                                                              /*
                                                                                                                                                              
                                                                                                                                                               Recolección de datos PLOT
                                                                                                                                                               
                                                                                                                                                              */
                                                                                                                                                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                              
                                                                                                                                                              SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}
                                                                                                                                                              SYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}
                                                                                                                                                              
                                                                                                                                                              PLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal, X:TIME_AC1}
                                                                                                                                                              
                                                                                                                                                              Graphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:"aviso"}
                                                                                                                                                              
                                                                                                                                                              
                                                                                                                                                              Function {Name:fcCaudal, Type:Math, Expression:"0.6 + SIN(A) * 4"}
                                                                                                                                                              
                                                                                                                                                              INITIAL nivel, 0
                                                                                                                                                              INITIAL caudal, 1.2
                                                                                                                                                              INITIAL fase, 0
                                                                                                                                                              
                                                                                                                                                              include ./library_graphics/speedometer.lib
                                                                                                                                                              
                                                                                                                                                              START 100
                                                                                                                                                              
                                                                                                                                                              PROCEDURE PRE_RUN
                                                                                                                                                              	assign config,{title:"Depósito"
                                                                                                                                                                  		,x:100,y:60 
                                                                                                                                                                          ,width:100 ,height:180
                                                                                                                                                              	       	,min_value: 0
                                                                                                                                                                  		,max_value: 100
                                                                                                                                                                          ,"color":"#ff0000"}
                                                                                                                                                                          
                                                                                                                                                              	call deposito.speedometer.init,V$config
                                                                                                                                                              
                                                                                                                                                              	assign config,{title:"Caudal"
                                                                                                                                                                  		,x:300,y:60 
                                                                                                                                                                          ,width:100 ,height:180
                                                                                                                                                              	       	,min_value: -10
                                                                                                                                                                  		,max_value: 10
                                                                                                                                                                          ,"color":"orange"}
                                                                                                                                                                          
                                                                                                                                                              	call caudal.speedometer.init,V$config
                                                                                                                                                              
                                                                                                                                                              TERMINATE_VE 
                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                              
                                                                                                                                                              PROCEDURE llenado
                                                                                                                                                                  ; Oscilar caudal como una función del tiempo simulado
                                                                                                                                                                  SAVEVALUE caudal, FN$(fcCaudal,X$fase)
                                                                                                                                                              
                                                                                                                                                                  ; Aumentamos la fase
                                                                                                                                                                  SAVEVALUE fase, X$fase + 0.1
                                                                                                                                                              
                                                                                                                                                                  ; Sumar caudal al nivel
                                                                                                                                                                  SAVEVALUE nivel, X$nivel + X$caudal * 0.1
                                                                                                                                                              
                                                                                                                                                                  ; Registrar los datos en el ploter
                                                                                                                                                                  PLOT thePlot, X$nivel, X$caudal
                                                                                                                                                              
                                                                                                                                                                  ; Mostrar valores redondeados
                                                                                                                                                                  ASSIGN tCaudal, round(X$caudal, 2)
                                                                                                                                                                  ASSIGN tNivel, round(X$nivel, 2)
                                                                                                                                                              
                                                                                                                                                                  ; Actualizar visualmente el nivel en pantalla
                                                                                                                                                              	CALL deposito.speedometer.set, P$tNivel
                                                                                                                                                              	CALL caudal.speedometer.set, P$tCaudal
                                                                                                                                                              
                                                                                                                                                              	IF (X$nivel>=100)
                                                                                                                                                                      MOVE {name:aviso, text:"¡DEPÓSITO LLENO!"}
                                                                                                                                                                      STOP
                                                                                                                                                                  ELSE
                                                                                                                                                                      MOVE {name:aviso, text:"Nivel: P$tNivel - Caudal: P$tCaudal"}
                                                                                                                                                                  ENDIF
                                                                                                                                                              TERMINATE
                                                                                                                                                              ENDPROCEDURE 1
                                                                                                                                                              
                                                                                                                                                              
                                                                                                                                                              • + Mejora de resultados con INTEGRATE

                                                                                                                                                                En los capítulos anteriores vimos cómo GPSS-Plus permite simular sistemas con comportamiento aparentemente continuo gracias a la ejecución frecuente de procedimientos temporizados (TIMER) y el uso de SAVEVALUE para acumular valores como el llenado de un depósito.

                                                                                                                                                                Hasta ahora usábamos una fórmula simple para simular el llenado:

                                                                                                                                                                SAVEVALUE nivel, X$nivel + X$caudal * 0.1

                                                                                                                                                                Este método es efectivo, pero básico. Asume que el caudal es constante durante cada intervalo de tiempo, lo cual no es del todo cierto si el caudal varía rápidamente, por ejemplo, con una función seno como es el caso.

                                                                                                                                                                Y cuando hablamos de intervalo, en este caso hablamos de ese "0.1". No es lo mismo SIN(0) que SIN(0.1).Y si queremos una estimación más precisa del valor medio, probablemente sería mejor usar SIN(0.05), es decir, el punto central del intervalo.

                                                                                                                                                                ¿Qué ocurre si el caudal varía durante el paso de tiempo?

                                                                                                                                                                Cuando el valor que estás acumulando cambia dentro del mismo intervalo, el sistema no es perfectamente exacto: estás sumando un valor medio aproximado, no el real.

                                                                                                                                                                Para mejorar esto, GPSS-Plus incorpora el bloque INTEGRATE, que utiliza un método de integración numérica llamado Runge-Kutta de cuarto orden (RK4) para calcular una estimación mucho más precisa.

                                                                                                                                                                ¿Qué hace INTEGRATE?

                                                                                                                                                                INTEGRATE { EXPRESSION: "SIN(T) + 2", DT: 0.1, SAVEVALUE: caudal_RK4 }

                                                                                                                                                                Este bloque:

                                                                                                                                                                 

                                                                                                                                                                 

                                                                                                                                                                • Evalúa la expresión en cuatro puntos clave del intervalo: al inicio (X), dos veces en el punto medio (X + DT/2), y al final (X + DT).

                                                                                                                                                                • Aplica el método de Runge-Kutta de cuarto orden (RK4), combinando esos valores con pesos específicos para estimar con precisión la variación de la función en ese tramo.

                                                                                                                                                                • Guarda el resultado en el SAVEVALUE indicado, como mejor estimación del valor medio de la expresión en ese intervalo.

                                                                                                                                                                El valor resultante será una mejor estimación del caudal medio en ese intervalo de tiempo, y lo podemos usar para llenar el depósito con más precisión.

                                                                                                                                                                 

                                                                                                                                                                Comparando el método tradicional (Euler) y RK4

                                                                                                                                                                Vamos a construir un ejemplo que utiliza ambos métodos en paralelo:

                                                                                                                                                                • caudal: usando el método simple

                                                                                                                                                                • caudal_RK4: usando integración numérica RK4

                                                                                                                                                                • nivel: acumulado con el caudal clásico

                                                                                                                                                                • nivel_RK4: acumulado con el caudal RK4

                                                                                                                                                                Además, graficaremos las 4 curvas usando PLOTTER para comparar visualmente los resultados.

                                                                                                                                                                ¿Qué verás en la gráfica?

                                                                                                                                                                • nivel_RK4 sube ligeramente más suavemente y más rápido que nivel, porque tiene en cuenta que el caudal crece dentro del intervalo.

                                                                                                                                                                • caudal_RK4 se ajusta mejor a los picos y valles que caudal, que solo calcula un punto.

                                                                                                                                                                • La línea blanca en pantalla (nivel RK4) va un poco por delante de la línea azul (nivel simple).

                                                                                                                                                                ¿Cuándo es válido usar RK4?

                                                                                                                                                                El método Runge-Kutta de cuarto orden (RK4), que implementa el bloque INTEGRATE en GPSS-Plus, es un algoritmo numérico diseñado para resolver ecuaciones diferenciales ordinarias de la forma:

                                                                                                                                                                dy/dt = f(t)

                                                                                                                                                                Y lo hace suponiendo que la función f(t) es continua y suave en el intervalo que se está integrando.

                                                                                                                                                                Pero… GPSS-Plus es un sistema de eventos discretos

                                                                                                                                                                Esto significa que hay muchas variables (X$nivel, X$entidadesEnCola, X$facilidad, etc.) que pueden cambiar bruscamente, sin continuidad ni suavidad.

                                                                                                                                                                Esos cambios dependen de eventos que se disparan cuando otras entidades llegan, bloquean recursos, o terminan.

                                                                                                                                                                Por tanto:

                                                                                                                                                                No se debe usar INTEGRATE con expresiones que dependan de elementos del sistema discreto.

                                                                                                                                                                INTEGRATE con RK4 es una herramienta potente y precisa, siempre que se utilice con funciones puramente matemáticas. No es una simulación mágica del futuro: es un estimador inteligente de una función bien definida.

                                                                                                                                                                 

                                                                                                                                                                 


                                                                                                                                                                Example:
                                                                                                                                                                /*
                                                                                                                                                                
                                                                                                                                                                 Mejora de resultados con INTEGRATE
                                                                                                                                                                 
                                                                                                                                                                */
                                                                                                                                                                SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}
                                                                                                                                                                SYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}
                                                                                                                                                                
                                                                                                                                                                PLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal,X:TIME_AC1}
                                                                                                                                                                PLOTTER {NAME:thePlot_RK4, Y_0:nivel, Y_1:caudal,X:TIME_AC1}
                                                                                                                                                                PLOTTER {NAME:thePlot_ALL, Y_0:nivel, Y_1:caudal, Y_2:nivel_RK4, Y_3:caudal_RK4,X:TIME_AC1}
                                                                                                                                                                
                                                                                                                                                                Graphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:"aviso"}
                                                                                                                                                                Graphic {NAME:aviso_RK4,Type:TEXT,X:291,Y:302,Text:"Aviso2"}
                                                                                                                                                                
                                                                                                                                                                Graphic {NAME:Cubo1,Type:LINE,POINTS:"[100,100],[100,500],[200,500],[200,100]"
                                                                                                                                                                	, Close:1
                                                                                                                                                                    , color:#00FFFF
                                                                                                                                                                    , Fcolor:#FFFFFF}
                                                                                                                                                                Graphic {NAME:Line1,Type:LINE,fcolor:#FF6666, X1:102,Y1:100,X2:198,Y2:100}
                                                                                                                                                                
                                                                                                                                                                Graphic {NAME:Line_RK4,Type:LINE,color:#333300, X1:98,Y1:100,X2:202,Y2:100}
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                INITIAL nivel, 0
                                                                                                                                                                INITIAL caudal, 1.2
                                                                                                                                                                INITIAL nivel_RK4, 0
                                                                                                                                                                INITIAL caudal_RK4, 1.2
                                                                                                                                                                INITIAL fase, 0
                                                                                                                                                                INITIAL constante, 0.6
                                                                                                                                                                
                                                                                                                                                                START 100
                                                                                                                                                                
                                                                                                                                                                ;*****************************************************
                                                                                                                                                                PROCEDURE llenado
                                                                                                                                                                    ; Oscilar caudal como una función del tiempo simulado
                                                                                                                                                                    SAVEVALUE caudal, X$constante + SIN(X$fase) * 4
                                                                                                                                                                    ; Aumentamos la fase suavemente
                                                                                                                                                                    SAVEVALUE fase, X$fase + 0.1
                                                                                                                                                                    ; Integración continua del nivel
                                                                                                                                                                   
                                                                                                                                                                    INTEGRATE  { EXPRESSION: "X$constante + SIN(T) * 4", METHOD: RK4, DT: 0.1, SAVEVALUE: caudal_RK4 }
                                                                                                                                                                    
                                                                                                                                                                
                                                                                                                                                                    SAVEVALUE nivel, X$nivel + X$caudal * 0.1
                                                                                                                                                                    SAVEVALUE nivel_RK4, X$nivel_RK4 + X$caudal_RK4 * 0.1
                                                                                                                                                                
                                                                                                                                                                	PLOT thePlot,X$nivel,X$caudal
                                                                                                                                                                	PLOT thePlot_RK4,X$nivel_RK4,X$caudal_RK4
                                                                                                                                                                	PLOT thePlot_ALL,X$nivel,X$caudal,X$nivel_RK4,X$caudal_RK4
                                                                                                                                                                
                                                                                                                                                                	MOVE {NAME:Line1, X1:98,Y1:(100+X$nivel*4) ,X2:202,Y2:(100+X$nivel*4)}
                                                                                                                                                                	MOVE {NAME:Line_RK4, X1:98,Y1:(100+X$nivel_RK4*4) ,X2:202,Y2:(100+X$nivel_RK4*4)}
                                                                                                                                                                
                                                                                                                                                                    ASSIGN tCaudal, round(X$caudal, 2)
                                                                                                                                                                    ASSIGN tNivel, round(X$nivel, 2)
                                                                                                                                                                
                                                                                                                                                                	ASSIGN tCaudal_RK4, round(X$caudal_RK4, 2)
                                                                                                                                                                    ASSIGN tNivel_RK4, round(X$nivel_RK4, 2)
                                                                                                                                                                    
                                                                                                                                                                    ; Visualización del estado
                                                                                                                                                                    IF (X$nivel>=100)
                                                                                                                                                                        MOVE {name:aviso, text:"¡DEPÓSITO LLENO!"}
                                                                                                                                                                        stop
                                                                                                                                                                    ELSE
                                                                                                                                                                        MOVE {name:aviso, text:"Nivel: P$tNivel - Caudal: P$tCaudal"}
                                                                                                                                                                        MOVE {name:aviso_RK4, text:"Nivel: P$tNivel_RK4 - Caudal: P$tCaudal_RK4"}
                                                                                                                                                                    ENDIF
                                                                                                                                                                TERMINATE
                                                                                                                                                                ENDPROCEDURE 1
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                
                                                                                                                                                                • + Movimiento con aceleración constante

                                                                                                                                                                  Este ejemplo muestra cómo simular el movimiento de un vehículo en un circuito circular utilizando una aceleración constante. Es el primer paso hacia la construcción de modelos físicos simples, como los que se emplean en videojuegos o simulaciones básicas de sistemas mecánicos.

                                                                                                                                                                  ¿Qué se simula?

                                                                                                                                                                  • El coche comienza detenido (velocidad 0).

                                                                                                                                                                  • Se le aplica una aceleración constante positiva hasta alcanzar una velocidad máxima (en este caso, 4 radianes por segundo).

                                                                                                                                                                  • Al llegar a ese límite, se invierte la aceleración para que el coche frene suavemente hasta una velocidad mínima (0.2 rad/s).

                                                                                                                                                                  • Luego vuelve a acelerar... y el ciclo se repite.

                                                                                                                                                                  ¿Cómo se calcula?

                                                                                                                                                                  Se emplea el bloque INTEGRATE para sumar la aceleración en cada paso de tiempo:

                                                                                                                                                                  INTEGRATE {EXPRESSION: X$aceleracion, DT: 0.1, SAVEVALUE: deltaVel}
                                                                                                                                                                  

                                                                                                                                                                  En este caso, al ser una aceleración constante:

                                                                                                                                                                  deltaVel = X$aceleracion * 0.1
                                                                                                                                                                  deltaVel = ±0.01 × 0.1 = ±0.001

                                                                                                                                                                  Esto nos da el incremento de velocidad en ese intervalo, y luego simplemente se acumula:

                                                                                                                                                                  SAVEVALUE velocidad, X$velocidad + X$deltaVel

                                                                                                                                                                  Resultado visual y gráfico

                                                                                                                                                                  • El coche se mueve suavemente en la pista circular, ganando y perdiendo velocidad.

                                                                                                                                                                  • El gráfico resultante (PLOTTER) muestra dos curvas:

                                                                                                                                                                    • Aceleración: una onda cuadrada que alterna entre +0.01 y -0.01.

                                                                                                                                                                    • Velocidad: una onda triangular que sube y baja en respuesta a la aceleración.

                                                                                                                                                                  Este comportamiento es el típico de un sistema con control básico de velocidad, y es una base para la aceleración variable, resistencia, o control adaptativo.

                                                                                                                                                                   


                                                                                                                                                                  Example:
                                                                                                                                                                  /*
                                                                                                                                                                  
                                                                                                                                                                   Movimiento con aceleración constante
                                                                                                                                                                   
                                                                                                                                                                  */
                                                                                                                                                                  SYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:5}
                                                                                                                                                                  SYSTEM {TYPE:ON_TIMER, TRIGGER:moverCoche, INTERVAL: 0.01}
                                                                                                                                                                  
                                                                                                                                                                  ; --- Variables iniciales ---
                                                                                                                                                                  INITIAL velocidad, 0.0            ; Velocidad angular
                                                                                                                                                                  INITIAL angulo, 0               ; Posición angular (en radianes)
                                                                                                                                                                  INITIAL aceleracion, 0.01       ; Aceleración angular constante
                                                                                                                                                                  INITIAL radio, 200              ; Radio del circuito circular
                                                                                                                                                                  
                                                                                                                                                                  ; --- Funciones gráficas ---
                                                                                                                                                                  Graphic {NAME:txtVel, Type:TEXT, X:510, Y:120, TEXT:"Velocidad:"}
                                                                                                                                                                  Graphic {NAME:coche, Type:LINE, X1:-10, Y1:-10,  X2:-10, Y2:10,  X3:10, Y3:10,  X4:10, Y4:-10,  COLOR:#FF0000, close:1}
                                                                                                                                                                  Graphic {NAME:pista, Type:ARC, X:300, Y:300, RADIUS:X$radio, CLOSE:0, COLOR:#999999}
                                                                                                                                                                  
                                                                                                                                                                  ; --- Gráfica de evolución ---
                                                                                                                                                                  PLOTTER {NAME:curva, Y_0:aceleracion,  Y_1:velocidad,X:TIME}
                                                                                                                                                                  
                                                                                                                                                                  START 500
                                                                                                                                                                  
                                                                                                                                                                  
                                                                                                                                                                  ; --- Procedimiento de movimiento ---
                                                                                                                                                                  PROCEDURE moverCoche
                                                                                                                                                                      IF (X$velocidad >= 4)
                                                                                                                                                                          SAVEVALUE aceleracion, -0.1  ; Empieza a frenar
                                                                                                                                                                      endif
                                                                                                                                                                      if (X$velocidad <= 0.2)
                                                                                                                                                                          SAVEVALUE aceleracion, 0.1   ; Vuelve a acelerar
                                                                                                                                                                      ENDIF
                                                                                                                                                                  
                                                                                                                                                                      ; Calcular incremento de velocidad
                                                                                                                                                                      INTEGRATE {EXPRESSION: X$aceleracion, DT: 0.01, SAVEVALUE: deltaVel}
                                                                                                                                                                  
                                                                                                                                                                      ; Acumular en velocidad total
                                                                                                                                                                      SAVEVALUE velocidad, X$velocidad + X$deltaVel
                                                                                                                                                                  
                                                                                                                                                                      ; Sumar velocidad al ángulo
                                                                                                                                                                      SAVEVALUE angulo, X$angulo + X$velocidad * 0.05
                                                                                                                                                                  
                                                                                                                                                                      ; Calcular posición
                                                                                                                                                                      ASSIGN rad, X$angulo
                                                                                                                                                                      ASSIGN posX, 300 + X$radio * COS(P$rad)
                                                                                                                                                                      ASSIGN posY, 300 + X$radio * SIN(P$rad)
                                                                                                                                                                  
                                                                                                                                                                      MOVE {NAME:coche, X:P$posX, Y:P$posY}
                                                                                                                                                                  
                                                                                                                                                                      ASSIGN tVel, round(X$velocidad, 2)
                                                                                                                                                                      MOVE {NAME:txtVel, TEXT: "Velocidad: P$tVel rad/s"}
                                                                                                                                                                  
                                                                                                                                                                  	PLOT curva, X$aceleracion, X$velocidad
                                                                                                                                                                  TERMINATE
                                                                                                                                                                  ENDPROCEDURE
                                                                                                                                                                  
                                                                                                                                                                  
                                                                                                                                                                  
                                                                                                                                                                  
                                                                                                                                                                  
                                                                                                                                                                  • + Movimiento con aceleración variable

                                                                                                                                                                    En este ejemplo damos un paso más en la simulación de movimiento circular, haciendo que la aceleración del vehículo evolucione de forma dinámica.

                                                                                                                                                                    Hasta ahora habíamos utilizado una aceleración constante o un cambio inmediato entre aceleración positiva y negativa. Sin embargo, en el mundo real, un coche acelera poco a poco, y frena con más fuerza, sobre todo si deja de acelerar y retiene motor. Eso es lo que queremos modelar aquí.

                                                                                                                                                                    ¿Qué modelamos?

                                                                                                                                                                    Simulamos un vehículo que:

                                                                                                                                                                    • Acelera lentamente cuando parte desde velocidad cero.

                                                                                                                                                                    • Frena más bruscamente cuando alcanza una velocidad alta.

                                                                                                                                                                    • Circula por una pista circular.

                                                                                                                                                                    • Muestra gráficamente su posición y velocidad angular.

                                                                                                                                                                    • Registra la evolución en dos gráficas: aceleración y velocidad.

                                                                                                                                                                     

                                                                                                                                                                    ¿Qué elementos intervienen?

                                                                                                                                                                    • aceleracion: valor actual de aceleración angular.

                                                                                                                                                                    • deltaA: ritmo de cambio de la aceleración (puede ser positivo o negativo).

                                                                                                                                                                    • INTEGRATE: calcula cuánto cambia la velocidad con la aceleración actual.

                                                                                                                                                                    • velocidad: cambia suavemente en función de la aceleración.

                                                                                                                                                                    • angulo: se incrementa según la velocidad, generando el movimiento.

                                                                                                                                                                    • coche: se mueve sobre la pista en función del ángulo.

                                                                                                                                                                    • PLOTTER: representa cómo varían la aceleración y la velocidad a lo largo del tiempo.

                                                                                                                                                                    ¿Cómo se gestiona la aceleración?

                                                                                                                                                                    1. Mientras la velocidad es baja, el sistema incrementa poco a poco la aceleración (deltaA positivo y suave).

                                                                                                                                                                    2. Cuando la aceleración supera un umbral (por ejemplo, 0.04), se invierte el signo de deltaA y se frena rápidamente.

                                                                                                                                                                    3. Al llegar a velocidad cero, se reinicia el ciclo: se corta la aceleración, se resetea velocidad, y se vuelve a acelerar.

                                                                                                                                                                    De esta forma se obtiene un patrón cíclico muy natural, parecido al de un motor térmico: aceleración progresiva y frenado más brusco.

                                                                                                                                                                     

                                                                                                                                                                    Resultados gráficos

                                                                                                                                                                    • La aceleración forma una curva en dientes de sierra: sube lentamente, y cae rápidamente.

                                                                                                                                                                    • La velocidad sigue una forma suave y ondulada: acelera poco a poco y baja más rápido.

                                                                                                                                                                    • El coche cambia de color: verde cuando acelera, rojo cuando frena.

                                                                                                                                                                     

                                                                                                                                                                     

                                                                                                                                                                     

                                                                                                                                                                     


                                                                                                                                                                    Example:
                                                                                                                                                                    /*
                                                                                                                                                                    
                                                                                                                                                                     Movimiento con aceleración variable
                                                                                                                                                                     
                                                                                                                                                                    */
                                                                                                                                                                    SYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:6}
                                                                                                                                                                    SYSTEM {TYPE:ON_TIMER, TRIGGER:moverCoche, INTERVAL: 0.1}
                                                                                                                                                                    
                                                                                                                                                                    ; --- Variables iniciales ---
                                                                                                                                                                    INITIAL velocidad, 0.0 ; Velocidad angular
                                                                                                                                                                    INITIAL angulo, 0 ; Posición angular (en radianes)
                                                                                                                                                                    INITIAL aceleracion, 0.01 ; Aceleración angular
                                                                                                                                                                    INITIAL radio, 200 ; Radio del circuito circular
                                                                                                                                                                    INITIAL deltaA, 0.0002
                                                                                                                                                                    ; --- Funciones gráficas ---
                                                                                                                                                                    Graphic {NAME:txtVel, Type:TEXT, X:510, Y:120, TEXT:"Velocidad:"}
                                                                                                                                                                    Graphic {NAME:coche, Type:LINE, X1:-10, Y1:-10, X2:-10, Y2:10, X3:10, Y3:10, X4:10, Y4:-10, COLOR:green, close:1}
                                                                                                                                                                    Graphic {NAME:pista, Type:ARC, X:300, Y:300, RADIUS:X$radio, CLOSE:0, COLOR:#999999}
                                                                                                                                                                    
                                                                                                                                                                    ; --- Gráfica de evolución ---
                                                                                                                                                                    PLOTTER {NAME:curva, Y_0:aceleracion, Y_1:velocidad,X:TIME}
                                                                                                                                                                    
                                                                                                                                                                    START 500
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    PROCEDURE moverCoche
                                                                                                                                                                     ;-------------------------------
                                                                                                                                                                     ; AJUSTE SUAVE DE LA ACELERACIÓN
                                                                                                                                                                     ;-------------------------------
                                                                                                                                                                    
                                                                                                                                                                     ; Control: si velocidad muy alta → reducir aceleración poco a poco
                                                                                                                                                                     IF (X$aceleracion > 0.04)
                                                                                                                                                                     SAVEVALUE deltaA, -0.003
                                                                                                                                                                     SAVEVALUE aceleracion, 0.04
                                                                                                                                                                     MOVE {name: coche,color:red}
                                                                                                                                                                     ENDIF
                                                                                                                                                                    
                                                                                                                                                                     IF (X$velocidad <= 0.0)
                                                                                                                                                                     SAVEVALUE deltaA, 0.0002
                                                                                                                                                                     SAVEVALUE aceleracion, 0
                                                                                                                                                                     SAVEVALUE velocidad, 0.0
                                                                                                                                                                     MOVE {name: coche,color:green}
                                                                                                                                                                     ENDIF
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                     ;---------------------------------
                                                                                                                                                                     ; ACTUALIZAR VELOCIDAD Y POSICIÓN
                                                                                                                                                                     ;---------------------------------
                                                                                                                                                                     
                                                                                                                                                                     ; Modificar aceleración suavemente según deltaA
                                                                                                                                                                     SAVEVALUE aceleracion, X$aceleracion + X$deltaA
                                                                                                                                                                     INTEGRATE {EXPRESSION: X$aceleracion, DT: 0.1, SAVEVALUE: deltaVel}
                                                                                                                                                                     SAVEVALUE velocidad, X$velocidad + X$deltaVel
                                                                                                                                                                    
                                                                                                                                                                     ; Actualizar ángulo (posición) según velocidad angular
                                                                                                                                                                     SAVEVALUE angulo, X$angulo + X$velocidad * 0.1
                                                                                                                                                                    
                                                                                                                                                                     ;--------------------------------
                                                                                                                                                                     ; CALCULAR POSICIÓN Y MOVER COCHE
                                                                                                                                                                     ;--------------------------------
                                                                                                                                                                     ASSIGN rad, X$angulo
                                                                                                                                                                     ASSIGN posX, 300 + X$radio * COS(P$rad)
                                                                                                                                                                     ASSIGN posY, 300 + X$radio * SIN(P$rad)
                                                                                                                                                                     MOVE {NAME:coche, X:P$posX, Y:P$posY}
                                                                                                                                                                    
                                                                                                                                                                     ;--------------------------
                                                                                                                                                                     ; MOSTRAR ESTADO Y PLOTTERS
                                                                                                                                                                     ;--------------------------
                                                                                                                                                                     ASSIGN tVel, round(X$velocidad, 2)
                                                                                                                                                                     ASSIGN tAcel, round(X$aceleracion, 4)
                                                                                                                                                                     MOVE {NAME:txtVel, TEXT: "Velocidad: P$tVel rad/s \n Aceleración: P$tAcel"}
                                                                                                                                                                    
                                                                                                                                                                     ; Gráficas para análisis
                                                                                                                                                                     PLOT curva, X$aceleracion, X$velocidad
                                                                                                                                                                    TERMINATE
                                                                                                                                                                    ENDPROCEDURE
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    
                                                                                                                                                                    • + Simulación híbrida

                                                                                                                                                                      En los capítulos anteriores vimos cómo simular el llenado de un depósito con funciones continuas, cómo registrar su evolución con gráficos (PLOTTER) y cómo mejorar la precisión usando integración numérica (INTEGRATE con RK4). Ahora es el momento de integrar todo ese conocimiento en un sistema realista y complejo, en el que lo continuo y lo discreto conviven.

                                                                                                                                                                      Ejemplo: estación solar con usuarios conectados

                                                                                                                                                                      Este ejemplo representa un sistema híbrido en el que:

                                                                                                                                                                      • Hay una fuente de energía solar (modelo continuo)

                                                                                                                                                                      • Hay usuarios discretos que llegan, deciden cargar y se conectan si lo necesitan

                                                                                                                                                                      Parte continua: generación solar

                                                                                                                                                                      La energía solar se modela como una función senoidal que simula el paso del día: una oscilación desde 0 hasta 1.5, a lo largo de 1440 minutos (24 horas). Cada unidad de tiempo representa un minuto, y cada ciclo de TIMER añade energía a la batería.

                                                                                                                                                                      INTEGRATE { 
                                                                                                                                                                        EXPRESSION: "0.75 + 0.75 * SIN((T / 1440) * 6.2832)", 
                                                                                                                                                                        DT: 1, 
                                                                                                                                                                        SAVEVALUE: caudalSolar 
                                                                                                                                                                      }

                                                                                                                                                                      Esta expresión genera una onda suave con un máximo de 1.5 y un mínimo de 0, centrada en 0.75. El resultado se suma al nivel de batería hasta un máximo de 100.

                                                                                                                                                                      Además, un PLOTTER recoge los datos en cada ciclo:

                                                                                                                                                                      PLOTTER {NAME:bateriaPlot, Y_0:nivelBateria, Y_1:caudalSolar, X:TIME_AC1}

                                                                                                                                                                      Así se podrá ver la evolución de la batería y el caudal solar durante todo el día.

                                                                                                                                                                      Parte discreta: usuarios que solicitan carga

                                                                                                                                                                      Mientras tanto, los usuarios llegan de forma aleatoria usando GENERATE. Cada uno decide si necesita cargar su batería, usando una función de tipo Poisson para modelar la decisión:

                                                                                                                                                                      Function {Name:decisionCarga, TYPE:POISSON, LAMBDA:1/3}
                                                                                                                                                                      

                                                                                                                                                                      Si deciden cargar:

                                                                                                                                                                      1. Se mueven hasta el punto de carga.

                                                                                                                                                                      2. Se conectan al recurso RESTROOM cargador, que simula un sistema de carga simultánea.

                                                                                                                                                                      3. Permanecen dentro del RESTROOM, mientras la batería central les transfiere energía (si tiene suficiente).

                                                                                                                                                                      4. Cuando alcanzan su nivel de carga requerido (usoPorCarga), son liberados automáticamente mediante WAKE.

                                                                                                                                                                      Coordinación de ambos mundos

                                                                                                                                                                      Cada vez que se ejecuta el ciclo de carga (cicloSolar):

                                                                                                                                                                      • Se calcula el nuevo caudal solar.

                                                                                                                                                                      • Se aumenta el nivel de batería con ese caudal.

                                                                                                                                                                      • Se recorre la lista de usuarios conectados (FOREACH) y se les transfiere carga si hay energía disponible.

                                                                                                                                                                      • Si un usuario ya ha sido cargado completamente, se libera del recurso.

                                                                                                                                                                      Visualmente, todo esto se representa con:

                                                                                                                                                                      • Un depósito que sube y baja según el nivel de batería.

                                                                                                                                                                      • Un arco que representa la intensidad solar actual.

                                                                                                                                                                      • Un gráfico de evolución con los datos recolectados en cada paso.

                                                                                                                                                                      Visualización e informe final

                                                                                                                                                                      Al finalizar la simulación, el gráfico bateriaPlot muestra la evolución de:

                                                                                                                                                                      • Nivel de batería (serie Y_0)

                                                                                                                                                                      • Caudal solar (serie Y_1)

                                                                                                                                                                      Se puede observar cómo:

                                                                                                                                                                      • La batería se recarga durante el día y se vacía durante la noche.

                                                                                                                                                                      • Si hay muchos usuarios conectados, la batería puede agotarse antes de que vuelva a salir el sol.

                                                                                                                                                                      • El número de usuarios simultáneos conectados se puede monitorizar con un contador clásico (por ejemplo, TABULATE cargador si se desea).

                                                                                                                                                                      Conclusión

                                                                                                                                                                      Este tipo de simulación es lo que se denomina un modelo híbrido continuo/discreto. Combina:

                                                                                                                                                                      • Una evolución continua del entorno (como el sol)

                                                                                                                                                                      • Una lógica discreta de eventos, decisiones y ocupación de recursos (usuarios)

                                                                                                                                                                      Este tipo de modelado es ideal para simular:

                                                                                                                                                                      • Energía y redes eléctricas

                                                                                                                                                                      • Producción intermitente y consumo variable

                                                                                                                                                                      • Procesos biológicos o químicos

                                                                                                                                                                      • Infraestructuras compartidas con restricciones

                                                                                                                                                                       

                                                                                                                                                                       


                                                                                                                                                                      Example:
                                                                                                                                                                      /*
                                                                                                                                                                      
                                                                                                                                                                       Simulación híbrida
                                                                                                                                                                       
                                                                                                                                                                      */
                                                                                                                                                                      SYSTEM {type:OPTIONS, TIME_DECIMALS:0, SPEED:5}         ; Tiempo con dos decimales y simulación visual lenta
                                                                                                                                                                      SYSTEM {TYPE:ON_TIMER, TRIGGER:cicloSolar, INTERVAL: 1}   ; Ejecutar procedimiento 'cicloSolar' cada 1 unidad de tiempo
                                                                                                                                                                      
                                                                                                                                                                      ;--- Gráficas de resultados ---
                                                                                                                                                                      PLOTTER {NAME:bateriaPlot, Y_0:nivelBateria, Y_1:caudalSolar, X:TIME_AC1}    ; Curvas: nivel batería y caudal solar
                                                                                                                                                                      
                                                                                                                                                                      ;--- Función aleatoria de decisión para usuarios ---
                                                                                                                                                                      Function {Name:decisionCarga, TYPE:POISSON, LAMBDA:1/4}                 ; Probabilidad de decidir cargar batería
                                                                                                                                                                      
                                                                                                                                                                      ;--- Posiciones gráficas ---
                                                                                                                                                                      Position {NAME:Pos1,X:431,Y:511}                           ; Entrada al punto de carga
                                                                                                                                                                      Position {NAME:Pos2,X:690,Y:510}                           ; Salida
                                                                                                                                                                      Position {NAME:PosRecarga,X:578,Y:173}                     ; Punto de recarga (visual)
                                                                                                                                                                      
                                                                                                                                                                      ;--- Recurso de recarga de usuarios ---
                                                                                                                                                                      Restroom {NAME:Cargador,X:555,Y:360,R_BIN_SIZE:1,R_BIN_COUNT:40}  ; Permite 40 unidades de carga concurrentes
                                                                                                                                                                      
                                                                                                                                                                      ;--- Indicadores visuales ---
                                                                                                                                                                      Graphic {NAME:tNivelBateria,Type:TEXT,X:329,Y:305,Text:"tNivel"}   ; Texto: nivel batería
                                                                                                                                                                      Graphic {NAME:gNivelCaudal,TYPE:ARC,X:411,Y:173,
                                                                                                                                                                      fCOLOR:#FFFF99,RADIUS:50,START_ANGLE:0,END_ANGLE:45,CLOSE:1} ; Semicírculo: intensidad solar
                                                                                                                                                                      Graphic {NAME:tCaudal,Type:TEXT,X:411,Y:173,Text:"tCaudal"}        ; Texto: valor de caudal
                                                                                                                                                                      Graphic {NAME:Cubo1,Type:LINE
                                                                                                                                                                      ,POINTS:"[100,100],[100,500],[ 200,500],[200,100]"
                                                                                                                                                                      ,Close:1,fcolor:#666666} ; Depósito batería
                                                                                                                                                                      Graphic {NAME:Line1,Type:LINE,color:#00FFFF, X1:98,Y1:100,X2:202,Y2:100}        ; Línea de nivel de batería
                                                                                                                                                                      
                                                                                                                                                                      ;--- Variables iniciales ---
                                                                                                                                                                      INITIAL nivelBateria, 50
                                                                                                                                                                      INITIAL caudalSolar, 0
                                                                                                                                                                      INITIAL usoPorCarga, 15
                                                                                                                                                                      
                                                                                                                                                                      START 2000 
                                                                                                                                                                      
                                                                                                                                                                      ;==============================
                                                                                                                                                                      ; PROCEDIMIENTO CICLO SOLAR
                                                                                                                                                                      ;==============================
                                                                                                                                                                      PROCEDURE cicloSolar
                                                                                                                                                                          ; Caudal solar: función sinusoidal normalizada entre 0 y 1.5
                                                                                                                                                                          INTEGRATE { EXPRESSION: "0.75 + 0.75 * SIN((T / 1440) * 6.2832)", DT: 1, SAVEVALUE: caudalSolar }
                                                                                                                                                                      
                                                                                                                                                                          ; Incrementar batería (con límite 100)
                                                                                                                                                                          SAVEVALUE nivelBateria, MIN(100, X$nivelBateria + X$caudalSolar)
                                                                                                                                                                      
                                                                                                                                                                          ; Recorrer entidades conectadas al cargador
                                                                                                                                                                          FOREACH NUMERO,IN_RESOURCE,Cargador
                                                                                                                                                                              if (X$nivelBateria>1)
                                                                                                                                                                                  ; Aumentar batería interna del usuario
                                                                                                                                                                                  assign actual,P$(bateria,P$NUMERO) + 0.5
                                                                                                                                                                                  assign bateria,P$actual,P$NUMERO
                                                                                                                                                                      
                                                                                                                                                                                  ; Restar batería del sistema
                                                                                                                                                                                  savevalue nivelBateria,X$nivelBateria - 0.5
                                                                                                                                                                      
                                                                                                                                                                                  ; Si ya tiene batería suficiente, liberar al usuario
                                                                                                                                                                                  if (P$actual>X$usoPorCarga)
                                                                                                                                                                                      wake Cargador,0,P$NUMERO
                                                                                                                                                                                  endif
                                                                                                                                                                              endif
                                                                                                                                                                          ENDFOREACH
                                                                                                                                                                      
                                                                                                                                                                          ; Gráfico solar: arco proporcional al caudal
                                                                                                                                                                          ASSIGN anguloSol, round((X$caudalSolar / 1.5) * 360, 1)
                                                                                                                                                                          MOVE {NAME: gNivelCaudal, END_ANGLE: P$anguloSol}
                                                                                                                                                                      
                                                                                                                                                                          ; Actualizar línea visual del nivel de batería
                                                                                                                                                                          MOVE {NAME:Line1, X1:98,Y1:(100+X$nivelBateria*4) ,X2:202,Y2:(100+X$nivelBateria*4)}
                                                                                                                                                                      
                                                                                                                                                                          ; Mostrar valores redondeados
                                                                                                                                                                          assign tnivelBateriaRound,round(X$nivelBateria,2)
                                                                                                                                                                          assign tCaudalRound,round(X$caudalSolar,2)
                                                                                                                                                                          move {name:tNivelBateria,text:"Nivel actual: P$tNivelBateriaRound"}
                                                                                                                                                                          move {name:tCaudal,text:"Caudal: P$tCaudalRound / 1.5"}
                                                                                                                                                                      
                                                                                                                                                                          ; Guardar datos para informe
                                                                                                                                                                          PLOT bateriaPlot, X$nivelBateria, X$caudalSolar
                                                                                                                                                                      TERMINATE
                                                                                                                                                                      ENDPROCEDURE 1
                                                                                                                                                                      
                                                                                                                                                                      ;==============================
                                                                                                                                                                      ; USUARIOS
                                                                                                                                                                      ;==============================
                                                                                                                                                                      GENERATE 5,5 {NAME:userGen, X:294, Y:514, ECOLOR:#000000} ; Llega un usuario cada ~5 unidades
                                                                                                                                                                      
                                                                                                                                                                      assign bateria,X$usoPorCarga                 ; Define su necesidad de carga
                                                                                                                                                                      
                                                                                                                                                                      ASSIGN decision, FN$decisionCarga           ; Decide si necesita recargar (Poisson)
                                                                                                                                                                      ADVANCE 10 {to:Pos1}                        ; Tiempo de conexión
                                                                                                                                                                      
                                                                                                                                                                      IF (P$decision>=1)
                                                                                                                                                                          assign bateria,0                        ; Inicializa contador de carga
                                                                                                                                                                          ADVANCE 10 {to:Cargador}                ; Se mueve al punto de carga
                                                                                                                                                                          rest Cargador                           ; Espera a estar cargado
                                                                                                                                                                      ENDIF
                                                                                                                                                                      
                                                                                                                                                                      ADVANCE 10 {to:Pos2}                        ; Sale
                                                                                                                                                                      TERMINATE 1
                                                                                                                                                                      
                                                                                                                                                                      
                                                                                                                                                                      
                                                                                                                                                                    • + Season 11: Sistemas No Lineales
                                                                                                                                                                      • + Sistemas No Lineales
                                                                                                                                                                        • + Sistemas No Lineales

                                                                                                                                                                          ¿Qué es un sistema no lineal?

                                                                                                                                                                          Los sistemas lineales son aquellos que se resuelven con ecuaciones proporcionales y directas: si duplicas la entrada, se duplica la salida. Un ejemplo clásico es:

                                                                                                                                                                          El padre tiene el doble de edad que su hijo, y el hijo es 20 años menor que el padre.

                                                                                                                                                                          Este tipo de sistemas se resuelven paso a paso, de forma secuencial o lineal.

                                                                                                                                                                          Pero en la realidad, muchos sistemas son no lineales, es decir, las variables están tan interrelacionadas que no pueden resolverse por separado. Requieren ser resueltas simultáneamente.

                                                                                                                                                                          Ejemplo: Depósitos de agua unidos por un tubo

                                                                                                                                                                          Supongamos dos depósitos de agua, uno con 10 litros y otro con 5 litros, unidos por un tubo.

                                                                                                                                                                          Podríamos escribir:

                                                                                                                                                                          volumen_A_Nuevo = volumen_A - flujo * DT ; DT es el incremento de tiempo
                                                                                                                                                                          volumen_B_Nuevo = volumen_B + flujo * DT
                                                                                                                                                                          flujo = K * (volumen_A - volumen_B)      ; K es una constante sobre la capacidad de fluir del tubo

                                                                                                                                                                          Donde:

                                                                                                                                                                          • K es una constante que define la permeabilidad del tubo
                                                                                                                                                                          • DT es el paso de tiempo (delta t)

                                                                                                                                                                          Esto puede funcionar bien para dos depósitos. Pero si conectamos un tercer depósito en serie, el sistema ya no responde correctamente. A veces, el depósito central parece no fluir hacia el tercero, a pesar de tener diferencia de presión. Si intentamos ver la cantidad de flujo en el instante 0 entre el segundo y tercer depósito sería 0 mientras que en realidad, sí hay flujo.

                                                                                                                                                                          Eso se debe a que las ecuaciones están acopladas. Resolverlas por separado no funciona.

                                                                                                                                                                           

                                                                                                                                                                          Resolver sistemas no lineales

                                                                                                                                                                          La solución es tratarlos como un conjunto de ecuaciones no lineales y resolverlas todas juntas.

                                                                                                                                                                          GPSS-Plus automatiza esto mediante el recurso DINAMIC que en cierto modo se parece a una Facility pero en lugar de gestionar entidades y colas, gestiona valores matemáticos.

                                                                                                                                                                          Este recurso implementa:

                                                                                                                                                                          • Matrices Jacobianas (para linealizar el sistema en cada iteración)
                                                                                                                                                                          • Método de Newton-Raphson (para encontrar la solución iterativamente)

                                                                                                                                                                          Declaración típica:

                                                                                                                                                                          INITIAL dynamic_config, {
                                                                                                                                                                            EXPRESSIONS: [
                                                                                                                                                                              "PIPE_FLUJO + PIPE_K * (TANK1_PRESION - TANK2_PRESION)",
                                                                                                                                                                              "TANK1_PRESION - TANK1_PRESION_PREV - PIPE_FLUJO * DT",
                                                                                                                                                                              "TANK2_PRESION - TANK2_PRESION_PREV + PIPE_FLUJO * DT"
                                                                                                                                                                            ],
                                                                                                                                                                            STATES: ["PIPE_FLUJO", "TANK1_PRESION", "TANK2_PRESION"],
                                                                                                                                                                            VARIABLES: ["PIPE_K", "TANK1_PRESION_PREV", "TANK2_PRESION_PREV"]
                                                                                                                                                                          }  
                                                                                                                                                                          
                                                                                                                                                                          INITIAL dynamic_values, {
                                                                                                                                                                            PIPE_K: 0.1,
                                                                                                                                                                            TANK1_PRESION_PREV: 10,
                                                                                                                                                                            TANK2_PRESION_PREV: 1
                                                                                                                                                                          }
                                                                                                                                                                          
                                                                                                                                                                          DYNAMIC {
                                                                                                                                                                            name: sys,
                                                                                                                                                                            CONFIG: V$dynamic_config,
                                                                                                                                                                            VALUES: V$dynamic_values,
                                                                                                                                                                            X: 300,
                                                                                                                                                                            Y: 300,
                                                                                                                                                                            TOLERANCE: 1e-6, ; valor por defecto
                                                                                                                                                                            MAX_ITER: 10     ; valor por defecto
                                                                                                                                                                          }

                                                                                                                                                                          El bloque DYNAMIC permite declarar:

                                                                                                                                                                          • EXPRESSIONS: las ecuaciones del sistema
                                                                                                                                                                          • STATES: variables a resolver (como presión, velocidad, temperatura)
                                                                                                                                                                          • VARIABLES: parámetros auxiliares (constantes o valores anteriores)

                                                                                                                                                                          El sufijo _PREV en las variables se gestiona automáticamente: GPSS-Plus actualiza su valor en cada iteración.

                                                                                                                                                                          Resolución con SOLVE:

                                                                                                                                                                          SOLVE {
                                                                                                                                                                            name: "sys",
                                                                                                                                                                            DT: 0.1,
                                                                                                                                                                            SAVEVALUE: "resultado"
                                                                                                                                                                          }
                                                                                                                                                                          

                                                                                                                                                                          Ejecución típica:

                                                                                                                                                                          PROCEDURE agente.init
                                                                                                                                                                          
                                                                                                                                                                            timeout cambiar_deposito, 10 ; si se desea cambiar algún parámetro en tiempo de ejecución
                                                                                                                                                                          
                                                                                                                                                                            while (1==1)
                                                                                                                                                                          
                                                                                                                                                                              SOLVE { name: "sys", DT: 0.1, SAVEVALUE: "resultado" }
                                                                                                                                                                          
                                                                                                                                                                              SAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)
                                                                                                                                                                              SAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)
                                                                                                                                                                              SAVEVALUE PIPE_FLUJO,    X$(resultado.PIPE_FLUJO)
                                                                                                                                                                          
                                                                                                                                                                              PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION
                                                                                                                                                                              PLOT flujo, X$PIPE_FLUJO
                                                                                                                                                                          
                                                                                                                                                                              IF (ABS(X$PIPE_FLUJO) < 0.001)
                                                                                                                                                                                stop
                                                                                                                                                                              ENDIF
                                                                                                                                                                          
                                                                                                                                                                              advance 0.1, 0
                                                                                                                                                                            endwhile
                                                                                                                                                                          
                                                                                                                                                                            stop
                                                                                                                                                                          ENDPROCEDURE

                                                                                                                                                                          Eventualmente, se pueden alterar los valores de VARIABLES con el uso de DYNAMIC_SET:

                                                                                                                                                                              assign nuevosParams,{TANK2_PRESION_PREV:10}
                                                                                                                                                                              dynamic_set sys,V$nuevosParams
                                                                                                                                                                          

                                                                                                                                                                          En la sección del depuración se peude observar cómo cambian los valores de STATES y la configuración completa del DYNAMIC.

                                                                                                                                                                          El resultado del PLOTTER es esclarecedor y muestra claramente cómo se equilibran los niveles de los depósitos con el paso del tiempo. 

                                                                                                                                                                           

                                                                                                                                                                          En resumen

                                                                                                                                                                          • En sistemas acoplados, como el flujo entre múltiples depósitos, necesitamos resolver ecuaciones simultáneamente.
                                                                                                                                                                          • DYNAMIC permite modelar esos sistemas físicos con expresiones simples.
                                                                                                                                                                          • GPSS-Plus automatiza la actualización de estados, la integración en el tiempo, y el control de convergencia.

                                                                                                                                                                          Con esto, puedes modelar fácilmente fenómenos como:

                                                                                                                                                                          • Equilibrio hidráulico
                                                                                                                                                                          • Transferencia de calor
                                                                                                                                                                          • Corriente eléctrica
                                                                                                                                                                          • Sistemas acoplados de cualquier tipo

                                                                                                                                                                           

                                                                                                                                                                           

                                                                                                                                                                           

                                                                                                                                                                           


                                                                                                                                                                          Example:
                                                                                                                                                                          /*
                                                                                                                                                                          
                                                                                                                                                                           Sistemas No Lineales
                                                                                                                                                                           
                                                                                                                                                                          */
                                                                                                                                                                          SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                          SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     
                                                                                                                                                                          
                                                                                                                                                                          PLOTTER {NAME:presiones, Y_0:tank1,  Y_1:tank2,  X:TIME_AC1}   
                                                                                                                                                                          PLOTTER {NAME:flujo, Y_0:flujo, X:TIME_AC1}   
                                                                                                                                                                          
                                                                                                                                                                          Graphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:"tTexto"}   ; Texto: nivel batería
                                                                                                                                                                          
                                                                                                                                                                          INITIAL TANK1_PRESION, 10
                                                                                                                                                                          INITIAL TANK2_PRESION, 1
                                                                                                                                                                          INITIAL K, 0.1
                                                                                                                                                                          
                                                                                                                                                                          INITIAL dynamic_config, {
                                                                                                                                                                              EXPRESSIONS: [
                                                                                                                                                                                  "PIPE_FLUJO + PIPE_K * (TANK1_PRESION - TANK2_PRESION)",
                                                                                                                                                                                  "TANK1_PRESION - TANK1_PRESION_PREV - PIPE_FLUJO * DT",
                                                                                                                                                                                  "TANK2_PRESION - TANK2_PRESION_PREV + PIPE_FLUJO * DT"
                                                                                                                                                                                ],
                                                                                                                                                                                STATES: [    "PIPE_FLUJO",    "TANK1_PRESION",    "TANK2_PRESION"  ],
                                                                                                                                                                                VARIABLES: [    "PIPE_K",    "TANK1_PRESION_PREV",    "TANK2_PRESION_PREV" ]
                                                                                                                                                                              }  
                                                                                                                                                                          
                                                                                                                                                                          INITIAL dynamic_values, { PIPE_K: X$K
                                                                                                                                                                          			, TANK1_PRESION_PREV:X$TANK1_PRESION
                                                                                                                                                                                      , TANK2_PRESION_PREV:X$TANK2_PRESION 
                                                                                                                                                                                      }
                                                                                                                                                                          
                                                                                                                                                                          DYNAMIC {name:sys
                                                                                                                                                                          	,CONFIG:V$dynamic_config
                                                                                                                                                                              ,VALUES:V$dynamic_values
                                                                                                                                                                          	,X:300,Y:300
                                                                                                                                                                              , TOLERANCE: 1e-6,  MAX_ITER: 10
                                                                                                                                                                          }
                                                                                                                                                                          
                                                                                                                                                                          START 1000 
                                                                                                                                                                          
                                                                                                                                                                          include ./library_graphics/tank.lib
                                                                                                                                                                          
                                                                                                                                                                          ;==============================================================
                                                                                                                                                                          
                                                                                                                                                                          PROCEDURE agente.init
                                                                                                                                                                          
                                                                                                                                                                          timeout cambiar_deposito,10
                                                                                                                                                                              advance 0.1
                                                                                                                                                                          while (1==1)
                                                                                                                                                                          move {name:tTexto, text:"flujo: P$rFlujo Tot: T1: P$rH1  T2: P$rH2 "}
                                                                                                                                                                          
                                                                                                                                                                          SOLVE {  name:"sys",  DT: 0.1,  SAVEVALUE: "resultado"}
                                                                                                                                                                          
                                                                                                                                                                              SAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)
                                                                                                                                                                              SAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)
                                                                                                                                                                              SAVEVALUE PIPE_FLUJO,  X$(resultado.PIPE_FLUJO)
                                                                                                                                                                          
                                                                                                                                                                              PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION
                                                                                                                                                                              PLOT flujo, X$PIPE_FLUJO
                                                                                                                                                                          
                                                                                                                                                                          	assign rFlujo,round(X$PIPE_FLUJO,3)
                                                                                                                                                                          	assign rH1,round(X$TANK1_PRESION,3)
                                                                                                                                                                          	assign rH2,round(X$TANK2_PRESION,3)
                                                                                                                                                                              
                                                                                                                                                                              move {name:tTexto, text:"flujo: P$rFlujo Tot: T1: P$rH1  T2: P$rH2 "}
                                                                                                                                                                          
                                                                                                                                                                          	CALL tank1.tank.set, P$rH1
                                                                                                                                                                          	CALL tank2.tank.set, P$rH2
                                                                                                                                                                          
                                                                                                                                                                          
                                                                                                                                                                              IF (ABS(X$PIPE_FLUJO) < 0.001)
                                                                                                                                                                                  stop
                                                                                                                                                                              ENDIF
                                                                                                                                                                          
                                                                                                                                                                          
                                                                                                                                                                          
                                                                                                                                                                          advance 0.1,0
                                                                                                                                                                          endwhile
                                                                                                                                                                          
                                                                                                                                                                          stop
                                                                                                                                                                          ENDPROCEDURE
                                                                                                                                                                          ;====================================================================
                                                                                                                                                                          
                                                                                                                                                                          PROCEDURE PRE_RUN
                                                                                                                                                                          
                                                                                                                                                                              call crear_depositos
                                                                                                                                                                              
                                                                                                                                                                              TIMEOUT agente.init,0
                                                                                                                                                                          
                                                                                                                                                                          	TERMINATE_VE 
                                                                                                                                                                          ENDPROCEDURE
                                                                                                                                                                          ;=================================================
                                                                                                                                                                          PROCEDURE crear_depositos
                                                                                                                                                                          
                                                                                                                                                                          	assign config,{title:"TANK 1"
                                                                                                                                                                              		,x:100,y:50 
                                                                                                                                                                                      ,width:50 ,height:180
                                                                                                                                                                                      ,value:0
                                                                                                                                                                                      ,max_value:10
                                                                                                                                                                                      ,"color":"#ff0000"}
                                                                                                                                                                                      
                                                                                                                                                                          	timeout tank1.tank.init,0,V$config
                                                                                                                                                                          
                                                                                                                                                                          	assign config,{title:"TANK 2"
                                                                                                                                                                              		,x:400,y:50 
                                                                                                                                                                                      ,width:50 ,height:180
                                                                                                                                                                                      ,value:0
                                                                                                                                                                                      ,max_value:10
                                                                                                                                                                                      ,"color":"#ff0000"}
                                                                                                                                                                                      
                                                                                                                                                                          	timeout tank2.tank.init,0,V$config
                                                                                                                                                                          
                                                                                                                                                                          	
                                                                                                                                                                          ENDPROCEDURE
                                                                                                                                                                          
                                                                                                                                                                          ;=================================================
                                                                                                                                                                          
                                                                                                                                                                          PROCEDURE cambiar_deposito
                                                                                                                                                                          
                                                                                                                                                                              assign nuevosParams,{TANK2_PRESION_PREV:10}
                                                                                                                                                                              dynamic_set sys,V$nuevosParams
                                                                                                                                                                          
                                                                                                                                                                              TERMINATE_VE
                                                                                                                                                                          ENDPROCEDURE
                                                                                                                                                                          
                                                                                                                                                                          • + Clásico masa-muelle-amortiguador

                                                                                                                                                                            Vamos a ver cómo se resuelve este clásico que solo requiere de las ecuaciones y posicionar correctamente la masa con respecto a los componentes.

                                                                                                                                                                            El ejemplo muestra cómo usar los gráficos 3D.


                                                                                                                                                                            Example:
                                                                                                                                                                            /*
                                                                                                                                                                            
                                                                                                                                                                             Masa-muelle-amortiguador
                                                                                                                                                                             
                                                                                                                                                                            */
                                                                                                                                                                            SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                            SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                            SYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:30, CAMERA:0}
                                                                                                                                                                            UI {TYPE: BUTTON, id:botonA,TEXT: "Pulsar", LABEL: "Pulsar", TRIGGER: Pulsar}
                                                                                                                                                                            UI {
                                                                                                                                                                              TYPE: SLIDER, ID: unSlider, LABEL: "Velocidad",
                                                                                                                                                                              VALUE: 15, MIN: 1, MAX: 50, STEP: 1,
                                                                                                                                                                              TRIGGER: capturarVelocidad
                                                                                                                                                                            }
                                                                                                                                                                            
                                                                                                                                                                            PLOTTER {NAME:V_X, Y_0:V,  Y_1:X,  X:TIME_AC1}   
                                                                                                                                                                            
                                                                                                                                                                            Graphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}
                                                                                                                                                                            Graphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}
                                                                                                                                                                            Graphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}
                                                                                                                                                                            
                                                                                                                                                                            Graphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:"tTexto"} 
                                                                                                                                                                            GRAPHIC {NAME:muelle, TYPE:OBJECT, src:SPRING, X:0, Y:15, Z:0, DEPTH:10, width:10, height:10, opacity:0.6}
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            GRAPHIC {NAME:masa, TYPE:SPHERE, X:0, Y:3, Z:0, radius:3, color:red}
                                                                                                                                                                            
                                                                                                                                                                            GRAPHIC {NAME:amortiguador, TYPE:BOX, X:0, Y:0, Z:0, WIDTH:2, HEIGHT:20, DEPTH:2, color:blue}
                                                                                                                                                                            GRAPHIC {NAME:amortiguadorB, TYPE:BOX, X:0, Y:10, Z:0, WIDTH:1.6, HEIGHT:20, DEPTH:1.6, color:cyan}
                                                                                                                                                                            ; FIJACIÓN inferior (visual)
                                                                                                                                                                            GRAPHIC {NAME:soporte, TYPE:BOX, X:0, Y:-1, Z:0, WIDTH:20, HEIGHT:2, DEPTH:20, color:yellow}
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            INITIAL mech_config, {
                                                                                                                                                                              EXPRESSIONS: [
                                                                                                                                                                                "F_spring + F_damper + F_inertial",       ; suma de fuerzas = 0 (equilibrio dinámico)
                                                                                                                                                                            
                                                                                                                                                                                "F_spring - K * X",                       ; resorte
                                                                                                                                                                                "F_damper - C * V",                       ; amortiguador
                                                                                                                                                                                "F_inertial - M * (V - V_PREV) / DT",     ; fuerza de inercia (aceleración)
                                                                                                                                                                                
                                                                                                                                                                                "X - X_PREV - DT * V"                     ; integración explícita: posición
                                                                                                                                                                              ],
                                                                                                                                                                              STATES: [
                                                                                                                                                                                "X",           ; posición
                                                                                                                                                                                "V",           ; velocidad
                                                                                                                                                                                "F_spring",    ; fuerza del muelle
                                                                                                                                                                                "F_damper",    ; fuerza de amortiguamiento
                                                                                                                                                                                "F_inertial"   ; fuerza de inercia
                                                                                                                                                                              ],
                                                                                                                                                                              VARIABLES: [
                                                                                                                                                                                "K", "C", "M", "X_PREV", "V_PREV"
                                                                                                                                                                              ]
                                                                                                                                                                            }
                                                                                                                                                                            
                                                                                                                                                                            INITIAL mech_values, {
                                                                                                                                                                              K: 20,          ; constante del muelle
                                                                                                                                                                              C: 0.005,         ; coef. de amortiguamiento
                                                                                                                                                                              M: 1,           ; masa
                                                                                                                                                                              X_PREV: 0,      ; posición inicial
                                                                                                                                                                              V_PREV: 30       ; velocidad inicial
                                                                                                                                                                            }
                                                                                                                                                                            
                                                                                                                                                                            DYNAMIC {
                                                                                                                                                                              name: sys,
                                                                                                                                                                              CONFIG: V$mech_config,
                                                                                                                                                                              VALUES: V$mech_values,
                                                                                                                                                                              X: 300,
                                                                                                                                                                              Y: 300,
                                                                                                                                                                              TOLERANCE: 1e-6,
                                                                                                                                                                              MAX_ITER: 10
                                                                                                                                                                            }
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            START 1000 
                                                                                                                                                                            
                                                                                                                                                                            include ./library_graphics/speedometer.lib
                                                                                                                                                                            
                                                                                                                                                                            ;==============================================================
                                                                                                                                                                            
                                                                                                                                                                            PROCEDURE agente.init
                                                                                                                                                                                
                                                                                                                                                                            while (1==1)
                                                                                                                                                                            
                                                                                                                                                                                SOLVE {  name:"sys",  DT: 0.05,  SAVEVALUE: "resultado"}
                                                                                                                                                                            
                                                                                                                                                                                SAVEVALUE V, X$(resultado.V)
                                                                                                                                                                                SAVEVALUE X, X$(resultado.X)
                                                                                                                                                                                SAVEVALUE I_cap ,  X$(resultado.I_cap)
                                                                                                                                                                            
                                                                                                                                                                                assign x_masa,30 + X$X
                                                                                                                                                                                ; === Posición de la masa
                                                                                                                                                                                MOVE {name:masa, Y:P$x_masa}
                                                                                                                                                                            
                                                                                                                                                                                ; === Estirar muelle desde base [0,0,0] hasta la masa
                                                                                                                                                                                MOVE {name:muelle, STRETCH_BETWEEN:"[0,0,0],[0,P$x_masa,0]",rotate_y:P$x_masa*200}
                                                                                                                                                                            
                                                                                                                                                                                ; === Estirar amortiguador desde base también
                                                                                                                                                                                MOVE {name:amortiguador, MOVE_BETWEEN:"[0,0,0],[0,P$x_masa,0]",y:P$x_masa -10}
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                                PLOT V_X, X$V, X$X
                                                                                                                                                                            
                                                                                                                                                                            	assign rX,round(X$X,3)
                                                                                                                                                                            	assign rV,round(X$V,3)
                                                                                                                                                                                
                                                                                                                                                                            	CALL tank1.speedometer.set, P$rV
                                                                                                                                                                            	CALL tank2.speedometer.set, P$rX
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                                IF (ABS(X$V) < 0.001)
                                                                                                                                                                                    stop
                                                                                                                                                                                ENDIF
                                                                                                                                                                            
                                                                                                                                                                            advance 0.05,0
                                                                                                                                                                            
                                                                                                                                                                            endwhile
                                                                                                                                                                            
                                                                                                                                                                            stop
                                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                                            ;====================================================================
                                                                                                                                                                            
                                                                                                                                                                            PROCEDURE PRE_RUN
                                                                                                                                                                            
                                                                                                                                                                                CALL crear_indicadores
                                                                                                                                                                                
                                                                                                                                                                                TIMEOUT agente.init,0
                                                                                                                                                                            
                                                                                                                                                                            	TERMINATE_VE 
                                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                                            ;=================================================
                                                                                                                                                                            PROCEDURE crear_indicadores
                                                                                                                                                                            
                                                                                                                                                                            	assign config,{title:"V"
                                                                                                                                                                                		,x:20,y:15 
                                                                                                                                                                                        ,width:5 ,height:18
                                                                                                                                                                                        ,value:0
                                                                                                                                                                                        ,min_value:-30
                                                                                                                                                                                        ,max_value:30
                                                                                                                                                                                        ,"color":"#ff0000"
                                                                                                                                                                                        ,font:"4px"}
                                                                                                                                                                                        
                                                                                                                                                                            	call tank1.speedometer.init,V$config
                                                                                                                                                                            
                                                                                                                                                                            	assign config,{title:"X"
                                                                                                                                                                                		,x:50,y:15 
                                                                                                                                                                                        ,width:5 ,height:18
                                                                                                                                                                                        ,value:0
                                                                                                                                                                                        ,min_value:-10
                                                                                                                                                                                        ,max_value:10
                                                                                                                                                                                        ,"color":"blue"
                                                                                                                                                                                        ,font:"4px"}
                                                                                                                                                                                        
                                                                                                                                                                            	call tank2.speedometer.init,V$config
                                                                                                                                                                            
                                                                                                                                                                            	
                                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                                            
                                                                                                                                                                            ;=================================================
                                                                                                                                                                            
                                                                                                                                                                            PROCEDURE Pulsar
                                                                                                                                                                            	savevalue velocidad_ui,max(X$velocidad_ui,15)
                                                                                                                                                                                assign nuevosParams,{V_PREV:-X$velocidad_ui}
                                                                                                                                                                                dynamic_set sys,V$nuevosParams
                                                                                                                                                                            
                                                                                                                                                                                TERMINATE_VE
                                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                                            
                                                                                                                                                                            PROCEDURE capturarVelocidad
                                                                                                                                                                            
                                                                                                                                                                                savevalue velocidad_ui,P$PARAM_B
                                                                                                                                                                            
                                                                                                                                                                                TERMINATE_VE
                                                                                                                                                                            ENDPROCEDURE
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                            
                                                                                                                                                                          • + No Lineales Componentes
                                                                                                                                                                            • + Por componentes: COMPOSITOR

                                                                                                                                                                              En simulaciones complejas, a menudo necesitamos modelar sistemas formados por múltiples componentes que interactúan entre sí, como depósitos conectados por tuberías, circuitos eléctricos o sistemas mecánicos acoplados. Para ello, GPSS-Plus utiliza el concepto de composición de sistemas mediante la COMPOSICIÓN.

                                                                                                                                                                              ¿Qué hace la composición?

                                                                                                                                                                              La composición permite fusionar múltiples DYNAMIC individuales en un solo sistema dinámico. Esta fusión se realiza conectando sus variables según un esquema común de nodos, y resolviendo de forma simultánea todas sus ecuaciones internas.

                                                                                                                                                                               

                                                                                                                                                                              Estructura de un sistema compuesto

                                                                                                                                                                              Veamos un ejemplo práctico: tres depósitos conectados por dos tuberías, donde el depósito central tiene dos entradas. El esquema sería:

                                                                                                                                                                              TANK1 --- PIPE12 --- TANK2 --- PIPE23 --- TANK3

                                                                                                                                                                              Este sistema se define así:

                                                                                                                                                                              INITIAL LIQUID_SYSTEM, {
                                                                                                                                                                                COMPONENTS: ["pipe12", "pipe23", "tank1", "tank2", "tank3"],
                                                                                                                                                                                CONNECTIONS: [
                                                                                                                                                                                  { NODE: "n1", CONNECTIONS: ["tank1.A","pipe12.A"] },
                                                                                                                                                                                  { NODE: "n2", CONNECTIONS: ["pipe12.B", "tank2.A"] },
                                                                                                                                                                                  { NODE: "n3", CONNECTIONS: ["pipe23.A", "tank2.B"] },
                                                                                                                                                                                  { NODE: "n4", CONNECTIONS: ["pipe23.B", "tank3.A"] }
                                                                                                                                                                                ]
                                                                                                                                                                              }

                                                                                                                                                                              Cada NODE indica un punto de conexión física entre uno o más componentes. A cada NODE se le asignará una variable de esfuerzo compartida (como presión o voltaje), y cada componente aportará sus propias ecuaciones internas y de flujo.

                                                                                                                                                                              Definiendo los componentes

                                                                                                                                                                              Tubería (PIPE):

                                                                                                                                                                              INITIAL PIPE_CONFIG, {
                                                                                                                                                                                EFFORTS: {
                                                                                                                                                                                  A: { NAME: "PresionA", UNIT: "Pa" },
                                                                                                                                                                                  B: { NAME: "PresionB", UNIT: "Pa" }
                                                                                                                                                                                },
                                                                                                                                                                                ROLES: {
                                                                                                                                                                                  QA: { ROLE: "FLOW", EXPOSED: ["A"] },
                                                                                                                                                                                  QB: { ROLE: "FLOW", EXPOSED: ["B"] },
                                                                                                                                                                                  K: { ROLE: "CONST" }
                                                                                                                                                                                },
                                                                                                                                                                                EXPRESSIONS: [
                                                                                                                                                                                  "QA + K * (PresionA - PresionB)",     ; Flujo desde A a B
                                                                                                                                                                                  "QA + QB"                             ; Conservación de flujo interna
                                                                                                                                                                                ]
                                                                                                                                                                              }

                                                                                                                                                                              Todas y cada una de las variables que aparecen en EXPRESSIONS deben quedar defidas claramente en uno de los dos grupos.

                                                                                                                                                                              • EFFORT: A: { NAME: "PresionA", UNIT: "Pa" } : Define qué variables de esfuerzo se igualarán con otros componentes conectados en el mismo nodo. Por ejemplo, un tanque tendrá una presión en ese nodo, que se igualará con todas las presiones en ese mismo nodo. Se debe precisar la unidad para tener clara la compatibilidad. En este caso, Pascales (Pa). 
                                                                                                                                                                              • ROLES: QA: { ROLE: "FLOW", EXPOSED: ["A"] }: Se define el ROLE que va a tener el resto de las variables del sistema.
                                                                                                                                                                                • FLOW: Variables de flujo que se traslada en el interior de cada componente y el puerto en el que se expone. FLOW puede exponerse en un puerto (una sola malla) o en dos (creando dos mallas distintas y una ecuación de conservación).
                                                                                                                                                                                • CONST: Constante del sistema de valor numérico.
                                                                                                                                                                                • STATE: variable que se requiere sea calculada por el sistema.

                                                                                                                                                                               

                                                                                                                                                                              Tanque de dos puertos:

                                                                                                                                                                              INITIAL TANK_2_PORTS_CONFIG, {"EFFORTS": {
                                                                                                                                                                                "A": { "NAME": "PresionA", "UNIT": "Pa" },
                                                                                                                                                                                "B": { "NAME": "PresionB", "UNIT": "Pa" }
                                                                                                                                                                              },
                                                                                                                                                                              "ROLES": {
                                                                                                                                                                                "PresionA_PREV": { "ROLE": "const", "UNIT": "Pa" },
                                                                                                                                                                                "IN1": { "ROLE": "flow", "EXPOSED": ["A"] },
                                                                                                                                                                                "IN2": { "ROLE": "flow", "EXPOSED": ["B"] }
                                                                                                                                                                              },
                                                                                                                                                                              "EXPRESSIONS": [
                                                                                                                                                                                "PresionA - PresionA_PREV - (IN1 + IN2) * DT",  ; Conservación de masa/volumen
                                                                                                                                                                                "PresionA - PresionB"                           ; Misma presión en los dos puertos
                                                                                                                                                                              ]
                                                                                                                                                                              }

                                                                                                                                                                              Composición final del sistema

                                                                                                                                                                              Después de declarar componentes y conexiones, simplemente se usa en COMANDO:

                                                                                                                                                                              DYNAMIC {name:sys, compositor:V$LIQUID_SYSTEM, X:712, Y:54}

                                                                                                                                                                              Este DYNAMIC genera automáticamente todas las ecuaciones necesarias combinando:

                                                                                                                                                                              • Variables de effort compartidas por nodo.

                                                                                                                                                                              • Variables de flow conectadas por puerto.

                                                                                                                                                                              • Reglas de conservación (de masa, energía, etc.)

                                                                                                                                                                              El resultado que se puede ver en la zona de debug sería:

                                                                                                                                                                              EXPRESSIONS: 
                                                                                                                                                                              1: FLOW_1 + PIPE12_K * (PA_N1 - PA_N2) 
                                                                                                                                                                              2: FLOW_1 + FLOW_2 
                                                                                                                                                                              3: FLOW_3 + PIPE23_K * (PA_N3 - PA_N4) 
                                                                                                                                                                              4: FLOW_3 + FLOW_4 
                                                                                                                                                                              5: PA_N1 - TANK1_PRESION_PREV - FLOW_1 * DT 
                                                                                                                                                                              6: PA_N2 - TANK2_PRESIONA_PREV - (FLOW_2 + FLOW_3) * DT 
                                                                                                                                                                              7: PA_N2 - PA_N3 
                                                                                                                                                                              8: PA_N4 - TANK3_PRESION_PREV - FLOW_4 * DT

                                                                                                                                                                              Donde vemos que las variables de esfuerzo se han igualado con una para cada nodo y las de flujo se han numerado por malla.

                                                                                                                                                                               

                                                                                                                                                                              Observaciones finales

                                                                                                                                                                              • Cada componente puede reutilizarse fácilmente con distintos valores.

                                                                                                                                                                              • Es posible construir bibliotecas completas de componentes (.lib) y combinarlas para construir modelos complejos sin reescribir expresiones.

                                                                                                                                                                              • El sistema cuida automáticamente los valores previos (_PREV), nombres unificados y mallas internas.


                                                                                                                                                                              Example:
                                                                                                                                                                              /*
                                                                                                                                                                              
                                                                                                                                                                               Por componentes: COMPOSITOR
                                                                                                                                                                               
                                                                                                                                                                              */
                                                                                                                                                                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                              SYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:5}     
                                                                                                                                                                              PLOTTER {NAME:presiones, Y_0:tank1,  Y_1:tank2,  Y_2:tank3,  X:TIME_AC1}   
                                                                                                                                                                              PLOTTER {NAME:flujo, Y_0:flujo, X:TIME_AC1}   
                                                                                                                                                                              
                                                                                                                                                                              Graphic {NAME:tTexto,Type:TEXT,X:376,Y:579,Text:"tTexto"}   ; Texto: nivel batería
                                                                                                                                                                              
                                                                                                                                                                              include ./library_componets_liquid/liquid.lib
                                                                                                                                                                              
                                                                                                                                                                              INITIAL PIPE12_VALUES, { K: 0.1 }
                                                                                                                                                                              INITIAL PIPE23_VALUES, { K: 0.1 }
                                                                                                                                                                              INITIAL TANK1_VALUES, { Presion_PREV: 10 }
                                                                                                                                                                              INITIAL TANK2_VALUES, { PresionA_PREV: 0 }
                                                                                                                                                                              INITIAL TANK3_VALUES, { Presion_PREV: 3 }
                                                                                                                                                                              
                                                                                                                                                                              
                                                                                                                                                                              DYNAMIC {name:tank1,  config:V$TANK_CONFIG,         VALUES:V$TANK1_VALUES, X:113, Y:491}
                                                                                                                                                                              DYNAMIC {name:tank2,  config:V$TANK_2_PORTS_CONFIG, VALUES:V$TANK2_VALUES, X:396, Y:389}
                                                                                                                                                                              DYNAMIC {name:tank3,  config:V$TANK_CONFIG,         VALUES:V$TANK3_VALUES, X:696, Y:342}
                                                                                                                                                                              DYNAMIC {name:pipe12,  config:V$PIPE_CONFIG,        VALUES:V$PIPE12_VALUES, X:189, Y:333}
                                                                                                                                                                              DYNAMIC {name:pipe23,  config:V$PIPE_CONFIG,        VALUES:V$PIPE23_VALUES, X:603, Y:512}
                                                                                                                                                                              
                                                                                                                                                                              
                                                                                                                                                                              INITIAL LIQUID_SYSTEM, {
                                                                                                                                                                                COMPONENTS: ["pipe12", "pipe23", "tank1", "tank2", "tank3"],
                                                                                                                                                                                CONNECTIONS: [
                                                                                                                                                                                  { NODE: "n1", CONNECTIONS: ["tank1.A","pipe12.A"] },
                                                                                                                                                                                  { NODE: "n2", CONNECTIONS: ["pipe12.B", "tank2.A"] },
                                                                                                                                                                                  { NODE: "n3", CONNECTIONS: ["pipe23.A", "tank2.B"] },
                                                                                                                                                                                  { NODE: "n4", CONNECTIONS: ["pipe23.B", "tank3.A"] }
                                                                                                                                                                                ]
                                                                                                                                                                              }
                                                                                                                                                                              
                                                                                                                                                                              DYNAMIC {name:sys, compositor:V$LIQUID_SYSTEM, X:712, Y:54}
                                                                                                                                                                              
                                                                                                                                                                              START 1000 
                                                                                                                                                                              
                                                                                                                                                                              include ./library_graphics/tank.lib
                                                                                                                                                                              
                                                                                                                                                                              ;==============================================================
                                                                                                                                                                              
                                                                                                                                                                              PROCEDURE agente.init
                                                                                                                                                                              
                                                                                                                                                                              timeout cambiar_deposito,2
                                                                                                                                                                                  advance 0.1
                                                                                                                                                                              while (1==1)
                                                                                                                                                                              
                                                                                                                                                                                  SOLVE {  name:"sys",  DT: 0.01,  SAVEVALUE: "resultado"}
                                                                                                                                                                              
                                                                                                                                                                                 
                                                                                                                                                                                  SAVEVALUE TANK1_PRESION, X$(resultado.tank1_Presion)
                                                                                                                                                                                  SAVEVALUE TANK2_PRESION, X$(resultado.tank2_PresionA)
                                                                                                                                                                                  SAVEVALUE TANK3_PRESION, X$(resultado.tank3_Presion)
                                                                                                                                                                                  SAVEVALUE PIPE12,  X$(resultado.pipe12_QA)
                                                                                                                                                                                  SAVEVALUE PIPE23,  X$(resultado.pipe23_QA)
                                                                                                                                                                              
                                                                                                                                                                                  PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION, X$TANK3_PRESION
                                                                                                                                                                                  PLOT flujo, X$PIPE_FLUJO
                                                                                                                                                                              
                                                                                                                                                                              	assign rFlujo12,round(X$PIPE12,3)
                                                                                                                                                                              	assign rFlujo23,round(X$PIPE23,3)
                                                                                                                                                                              	assign rH1,round(X$TANK1_PRESION,3)
                                                                                                                                                                              	assign rH2,round(X$TANK2_PRESION,3)
                                                                                                                                                                              	assign rH3,round(X$TANK3_PRESION,3)
                                                                                                                                                                                  
                                                                                                                                                                                  move {name:tTexto, text:"AC1$ flujos: P$rFlujo12 ----- P$rFlujo23 "}
                                                                                                                                                                              
                                                                                                                                                                              	CALL tank1.tank.set, P$rH1
                                                                                                                                                                              	CALL tank2.tank.set, P$rH2
                                                                                                                                                                              	CALL tank3.tank.set, P$rH3
                                                                                                                                                                              
                                                                                                                                                                              
                                                                                                                                                                                  IF (ABS(X$PIPE_FLUJO) < 0.001)
                                                                                                                                                                                      ;stop
                                                                                                                                                                                  ENDIF
                                                                                                                                                                              
                                                                                                                                                                              advance 0.01,0
                                                                                                                                                                              
                                                                                                                                                                              endwhile
                                                                                                                                                                              
                                                                                                                                                                              stop
                                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                                              ;====================================================================
                                                                                                                                                                              
                                                                                                                                                                              PROCEDURE PRE_RUN
                                                                                                                                                                              
                                                                                                                                                                                  CALL crear_depositos
                                                                                                                                                                                  
                                                                                                                                                                                  TIMEOUT agente.init,0
                                                                                                                                                                              
                                                                                                                                                                              	TERMINATE_VE 
                                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                                              ;=================================================
                                                                                                                                                                              PROCEDURE crear_depositos
                                                                                                                                                                              
                                                                                                                                                                              	assign config,{title:"TANK 1"
                                                                                                                                                                                  		,x:100,y:50 
                                                                                                                                                                                          ,width:50 ,height:180
                                                                                                                                                                                          ,value:0
                                                                                                                                                                                          ,max_value:10
                                                                                                                                                                                          ,"color":"#ff0000"}
                                                                                                                                                                                          
                                                                                                                                                                              	timeout tank1.tank.init,0,V$config
                                                                                                                                                                              
                                                                                                                                                                              	assign config,{title:"TANK 2"
                                                                                                                                                                                  		,x:300,y:50 
                                                                                                                                                                                          ,width:50 ,height:180
                                                                                                                                                                                          ,value:0
                                                                                                                                                                                          ,max_value:10
                                                                                                                                                                                          ,"color":"#ff0000"}
                                                                                                                                                                                          
                                                                                                                                                                              	timeout tank2.tank.init,0,V$config
                                                                                                                                                                              
                                                                                                                                                                              	assign config,{title:"TANK 3"
                                                                                                                                                                                  		,x:500,y:50 
                                                                                                                                                                                          ,width:50 ,height:180
                                                                                                                                                                                          ,value:0
                                                                                                                                                                                          ,max_value:10
                                                                                                                                                                                          ,"color":"#ff0000"}
                                                                                                                                                                                          
                                                                                                                                                                              	timeout tank3.tank.init,0,V$config
                                                                                                                                                                              
                                                                                                                                                                              	
                                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                                              
                                                                                                                                                                              ;=================================================
                                                                                                                                                                              
                                                                                                                                                                              PROCEDURE cambiar_deposito
                                                                                                                                                                              
                                                                                                                                                                                  assign nuevosParams,{tank3_Presion_PREV:10}
                                                                                                                                                                                  dynamic_set sys,V$nuevosParams
                                                                                                                                                                              
                                                                                                                                                                                  TERMINATE_VE
                                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                                              
                                                                                                                                                                              
                                                                                                                                                                              
                                                                                                                                                                              • + Librerías de componentes

                                                                                                                                                                                ¿Qué es una librería?

                                                                                                                                                                                Una librería es un conjunto de componentes predefinidos con sus configuraciones y comportamientos matemáticos ya listos para usarse. En el caso de los componentes de GPSS-Plus se limita a guardar los CONFIG de cada elemento. Por ejemplo:

                                                                                                                                                                                initial PIPE_CONFIG, {
                                                                                                                                                                                  TYPE: "PIPE",
                                                                                                                                                                                  EXPRESSIONS: [ "FLUJO + K * (PRESION_A - PRESION_B)" ],
                                                                                                                                                                                  STATES: ["FLUJO"],
                                                                                                                                                                                  VARIABLES: ["K"],
                                                                                                                                                                                  OWNED_VARS: { A: ["FLUJO"], B: ["-FLUJO"] },
                                                                                                                                                                                  REQUIRED_VARS: { A: ["PRESION_A"], B: ["PRESION_B"] }
                                                                                                                                                                                }

                                                                                                                                                                                Solo necesitamos realizar un INCLUDE del fichero que la contiene:

                                                                                                                                                                                include ./library_componets_liquid/liquid.lib
                                                                                                                                                                                

                                                                                                                                                                                Y usamos esos config como si los hubiñesemos escrito en el código principal.

                                                                                                                                                                                initial pipe1_data, { K: 0.1 }
                                                                                                                                                                                dynamic { name: pipe1, config: V$PIPE_CONFIG, values: V$pipe1_data, x: 100, y: 300 }
                                                                                                                                                                                
                                                                                                                                                                                initial tank1_data, { PRESION_PREV: 10 }
                                                                                                                                                                                dynamic { name: tank1, config: V$TANK_CONFIG, values: V$tank1_data, x: 50, y: 400 }
                                                                                                                                                                                
                                                                                                                                                                                initial tank2_data, { PRESION_PREV: 5 }
                                                                                                                                                                                dynamic { name: tank2, config: V$TANK_CONFIG, values: V$tank2_data, x: 200, y: 400 }
                                                                                                                                                                                
                                                                                                                                                                                

                                                                                                                                                                                Ventajas de usar librerías

                                                                                                                                                                                • Reutilización y estandarización.

                                                                                                                                                                                • Reducción de errores.

                                                                                                                                                                                • Modelado más rápido y con mayor calidad.

                                                                                                                                                                                 


                                                                                                                                                                                Example:
                                                                                                                                                                                SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                SYSTEM {type:OPTIONS, TIME_DECIMALS:3, SPEED:1,pause:0}     
                                                                                                                                                                                
                                                                                                                                                                                PLOTTER {NAME:movimiento, Y_0:tank1,  Y_1:tank2,  X:TIME_AC1}   
                                                                                                                                                                                Graphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:"tTexto"} 
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                include ./library_componets_liquid/liquid.lib
                                                                                                                                                                                
                                                                                                                                                                                INITIAL pipe1_DATA, { K: 0.1 }
                                                                                                                                                                                DYNAMIC {name:pipe1,config:V$pipe_CONFIG,VALUES:V$pipe1_DATA,X:373,Y:418}
                                                                                                                                                                                
                                                                                                                                                                                INITIAL tank1_DATA, { PRESION_PREV: 10 }
                                                                                                                                                                                DYNAMIC {name:tank1,config:V$tank_CONFIG,VALUES:V$tank1_DATA,X:123,Y:357}
                                                                                                                                                                                
                                                                                                                                                                                INITIAL tank2_DATA, { PRESION_PREV: 1 }
                                                                                                                                                                                DYNAMIC {name:tank2,config:V$tank_CONFIG,VALUES:V$tank2_DATA,X:624,Y:347}
                                                                                                                                                                                
                                                                                                                                                                                initial COMPOSITOR, {
                                                                                                                                                                                   COMPONENTS: [ "pipe1","tank1","tank2"],
                                                                                                                                                                                   CONNECTIONS: [ "TANK1.A=PIPE1.A", "TANK2.A=PIPE1.B"]
                                                                                                                                                                                   }
                                                                                                                                                                                
                                                                                                                                                                                DYNAMIC {name:sys,compositor:V$COMPOSITOR,X:347,Y:164}
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                START 1
                                                                                                                                                                                
                                                                                                                                                                                include ./library_graphics/tank.lib
                                                                                                                                                                                
                                                                                                                                                                                ;==============================================================
                                                                                                                                                                                
                                                                                                                                                                                PROCEDURE agente.init
                                                                                                                                                                                
                                                                                                                                                                                timeout cambiar_deposito,100
                                                                                                                                                                                timeout cambiar_deposito2,200
                                                                                                                                                                                    
                                                                                                                                                                                while (1==1)
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                SOLVE {  name:"sys",  DT: 0.1,  SAVEVALUE: "resultado"}
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                SAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)
                                                                                                                                                                                SAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)
                                                                                                                                                                                SAVEVALUE PIPE1_FLUJO,  X$(resultado.PIPE1_FLUJO)
                                                                                                                                                                                
                                                                                                                                                                                PLOT movimiento, X$TANK1_PRESION, X$TANK2_PRESION
                                                                                                                                                                                
                                                                                                                                                                                	assign rFlujo,round(X$PIPE_FLUJO,5)
                                                                                                                                                                                	assign rH1,round(X$TANK1_PRESION,5)
                                                                                                                                                                                	assign rH2,round(X$TANK2_PRESION,5)
                                                                                                                                                                                    
                                                                                                                                                                                    move {name:tTexto, text:"flujo:\n P$rFlujo Tot: P$rH1 P$rH2 "}
                                                                                                                                                                                
                                                                                                                                                                                	CALL tank1.tank.set, X$TANK1_PRESION
                                                                                                                                                                                	CALL tank2.tank.set, X$TANK2_PRESION
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                    IF (ABS(X$PIPE1_FLUJO) < 0.001)
                                                                                                                                                                                        stop
                                                                                                                                                                                    ENDIF
                                                                                                                                                                                
                                                                                                                                                                                advance 1,0
                                                                                                                                                                                
                                                                                                                                                                                endwhile
                                                                                                                                                                                
                                                                                                                                                                                stop
                                                                                                                                                                                ENDPROCEDURE
                                                                                                                                                                                ;====================================================================
                                                                                                                                                                                
                                                                                                                                                                                PROCEDURE PRE_RUN
                                                                                                                                                                                
                                                                                                                                                                                    CALL crear_depositos
                                                                                                                                                                                    
                                                                                                                                                                                    TIMEOUT agente.init,0
                                                                                                                                                                                
                                                                                                                                                                                	TERMINATE_VE 
                                                                                                                                                                                ENDPROCEDURE
                                                                                                                                                                                ;=================================================
                                                                                                                                                                                PROCEDURE crear_depositos
                                                                                                                                                                                
                                                                                                                                                                                	assign config,{title:"TANK 1"
                                                                                                                                                                                    		,x:100,y:100 
                                                                                                                                                                                            ,width:50 ,height:180
                                                                                                                                                                                            ,value:X$TANK1_PRESION
                                                                                                                                                                                            ,max_value:10
                                                                                                                                                                                            ,"color":"#ff0000"}
                                                                                                                                                                                            
                                                                                                                                                                                	call tank1.tank.init,V$config
                                                                                                                                                                                
                                                                                                                                                                                	assign config,{title:"TANK 2"
                                                                                                                                                                                    		,x:500,y:100 
                                                                                                                                                                                            ,width:50 ,height:180
                                                                                                                                                                                            ,value:X$TANK2_PRESION
                                                                                                                                                                                            ,max_value:10
                                                                                                                                                                                            ,"color":"#ff0000"}
                                                                                                                                                                                            
                                                                                                                                                                                	call tank2.tank.init,V$config
                                                                                                                                                                                	
                                                                                                                                                                                ENDPROCEDURE
                                                                                                                                                                                
                                                                                                                                                                                ;=================================================
                                                                                                                                                                                
                                                                                                                                                                                PROCEDURE cambiar_deposito
                                                                                                                                                                                    assign nuevosParams,{PIPE1_K:0.3}
                                                                                                                                                                                    dynamic_set sys,V$nuevosParams
                                                                                                                                                                                    TERMINATE_VE
                                                                                                                                                                                ENDPROCEDURE
                                                                                                                                                                                
                                                                                                                                                                                PROCEDURE cambiar_deposito2
                                                                                                                                                                                    assign nuevosParams,{TANK2_PRESION_PREV:10}
                                                                                                                                                                                    dynamic_set sys,V$nuevosParams
                                                                                                                                                                                    TERMINATE_VE
                                                                                                                                                                                ENDPROCEDURE
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                                
                                                                                                                                                                              • + No Lineales Restricciones
                                                                                                                                                                                • + Introducción a las restricciones

                                                                                                                                                                                  Hemos visto varias fórmulas físicas, eléctricas y de fluidos descritas en DYNAMIC evolucionar en el SOLVE.

                                                                                                                                                                                  Resumidamente, introducíamos todas las fórmulas que debía satisfacer un sistema para ser resueltas a la vez. Así podíamos resolver, por ejemplo, el problema de los 3 tanques de líquidos conectados.

                                                                                                                                                                                  Veremos ahora una forma de utilizar ese mismo recurso para dar solución a la física con restricciones que no es otra cosa que lo que vemos en los videojuegos cuando dos objetos colisionan. De la misma manera que podemos simular múltiples objetos moviéndose entre ellos con ciertas restricciones de giro o distancia.
                                                                                                                                                                                  No introducimos un nuevo tipo de simulación, sino una forma distinta de describir otro problema.

                                                                                                                                                                                  Para hacernos una idea de cómo funciona, es como cualquier otra simulación, debemos observar qué sucede en el mundo real y tras ello, describirlo tal cual.
                                                                                                                                                                                  Los pasos suelen ser los siguientes:

                                                                                                                                                                                  1.- El objeto u objetos. Por simplificar, usaremos esferas y cubos perfectos. Pueden tener un peso y radio. Se sitúan en un lugar en el espacio.

                                                                                                                                                                                  2.- Su velocidad puntual. Debemos saber si está estático, con una cierta velocidad libre o está sometido a una fuerza que le confiere una aceleración. Es decir:

                                                                                                                                                                                  Integraremos la posición 
                                                                                                                                                                                  x=x+vΔt

                                                                                                                                                                                  Y si hay fuerzas involucradas, también las fuerzas entrarán en juego: 
                                                                                                                                                                                  v=v+aΔt


                                                                                                                                                                                  3.- La detección de la restricción. Si hablamos de un objeto que choca con una pared, debemos saberlo antes de efectuar alguna consecuencia. Así que se verificarán si estamos violando alguna restricción.

                                                                                                                                                                                  Si nuestro objeto fuese una esfera, verificaremos si choca o invade el espacio de:
                                                                                                                                                                                  Con pared: comparar coordenadas
                                                                                                                                                                                  Con esfera: distancia entre centros
                                                                                                                                                                                  Con caja: comparar intervalos (AABB)
                                                                                                                                                                                  Con formas más complejas: más geometría


                                                                                                                                                                                  4.- Las restricciones. Si hasta ahora hablamos de posición y velocidad del objeto, una restricción, sea la que sea, debe modificar ambas características. Si el objeto choca contra una pared, el incremento de x irá en la dirección contraria y la velocidad cambiará de signo. Una restricción no “impide”, corrige.

                                                                                                                                                                                  Cosa que no quita para que una restricción sea, por ejemplo, no superar cierta velocidad.

                                                                                                                                                                                  5.- Escalado. Cuando hay muchos objetos se hace inviable meter miles de objetos en el sistema y se realiza por partes.
                                                                                                                                                                                  Se requiere filtrar candidatos por métodos de rejillas o árboles..

                                                                                                                                                                                   

                                                                                                                                                                                  El ejemplo:

                                                                                                                                                                                  Una sola masa esférica que bota contra las paredes que lo encierran.

                                                                                                                                                                                  Mostramos las fórmulas de las velocidades constantes y sus integrales para obtener las posiciones.

                                                                                                                                                                                  Los estados son los 4 nombrados: Posición (x,y) y sus velocidades (vx,vy)

                                                                                                                                                                                  Y tras esto, las restricciones. Que observemos la primera:

                                                                                                                                                                                  {
                                                                                                                                                                                    "VARIABLE":"VX_PREV",
                                                                                                                                                                                    "EXPRESSION":"(X <= (MINX+R) and VX < 0) ? -VX*E : ((X >= (MAXX-R) and VX > 0) ? -VX*E : VX)"
                                                                                                                                                                                  }
                                                                                                                                                                                  


                                                                                                                                                                                  Significa qué vamos a hacer con una determinable variable si suceden ciertas condiciones. En este caso, qué le va a suceder a la variable "VX_PREV", que es uno de los estados previos a la siguiente iteración del solver.

                                                                                                                                                                                  Las variables *_PREV representan el estado que “hereda” el siguiente paso de SOLVE.

                                                                                                                                                                                  La salida será o dejarla como está en último termino (VX) o modificarla si suceden las condiciones establecidas.
                                                                                                                                                                                  Si vamos hacia la izquierda y hemos pasado el límite izquierdo, VX_PREV será -VX*E.
                                                                                                                                                                                  En caso contrario, comprobaremos la derecha siendo VX_PREV = -VX*E de nuevo.
                                                                                                                                                                                  Y como decíamos, si no hay restricciones que aplicar, VX_PREV será VX.

                                                                                                                                                                                  Nótese que "E" es la velocidad que conservo tras cada rebote, así que se usa como multiplicador para reducir la velocidad. Si E fuese 0, sería una parada en seco.


                                                                                                                                                                                  Example:
                                                                                                                                                                                  SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                  SYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:50, CAMERA:0}
                                                                                                                                                                                  
                                                                                                                                                                                  SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     
                                                                                                                                                                                  ;SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  Graphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}
                                                                                                                                                                                  Graphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}
                                                                                                                                                                                  Graphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}
                                                                                                                                                                                  
                                                                                                                                                                                  GRAPHIC {NAME:masa, TYPE:SPHERE, X:0, Y:3, Z:0, radius:3, color:red}
                                                                                                                                                                                  
                                                                                                                                                                                  GRAPHIC {NAME:soporteIz, TYPE:BOX, X:-1, Y:30, Z:0, WIDTH:2, HEIGHT:60, DEPTH:20, color:yellow}
                                                                                                                                                                                  GRAPHIC {NAME:soporteDe, TYPE:BOX, X:31, Y:30, Z:0, WIDTH:2, HEIGHT:60, DEPTH:20, color:yellow}
                                                                                                                                                                                  GRAPHIC {NAME:soporteSu, TYPE:BOX, X:15, Y:61, Z:0, WIDTH:30, HEIGHT:2, DEPTH:20, color:yellow}
                                                                                                                                                                                  GRAPHIC {NAME:soporteIn, TYPE:BOX, X:15, Y:-1, Z:0, WIDTH:30, HEIGHT:2, DEPTH:20, color:yellow}
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  INITIAL pelota2D_config, {
                                                                                                                                                                                    EXPRESSIONS: [
                                                                                                                                                                                      ; velocidad constante (sin fuerzas)
                                                                                                                                                                                      "VX - VX_PREV",
                                                                                                                                                                                      "VY - VY_PREV",
                                                                                                                                                                                  
                                                                                                                                                                                      ; integración explícita
                                                                                                                                                                                      "X - X_PREV - DT * VX",
                                                                                                                                                                                      "Y - Y_PREV - DT * VY"
                                                                                                                                                                                    ],
                                                                                                                                                                                    STATES: [
                                                                                                                                                                                      "X","Y","VX","VY"
                                                                                                                                                                                    ],
                                                                                                                                                                                    VARIABLES: [
                                                                                                                                                                                      "X_PREV","Y_PREV","VX_PREV","VY_PREV",
                                                                                                                                                                                      "R","E",
                                                                                                                                                                                      "MINX","MAXX","MINY","MAXY"
                                                                                                                                                                                    ],
                                                                                                                                                                                    
                                                                                                                                                                                    ; Las CONDITIONS corrigen el estado después del paso libre (mover → corregir).
                                                                                                                                                                                    
                                                                                                                                                                                    CONDITIONS: [
                                                                                                                                                                                      ; --- pared izquierda/derecha ---
                                                                                                                                                                                      {
                                                                                                                                                                                        "VARIABLE":"VX_PREV",
                                                                                                                                                                                        "EXPRESSION":"(X <= (MINX+R) and VX < 0) ? -VX*E : ((X >= (MAXX-R) and VX > 0) ? -VX*E : VX)"
                                                                                                                                                                                      },
                                                                                                                                                                                      {
                                                                                                                                                                                        "VARIABLE":"X_PREV",
                                                                                                                                                                                        "EXPRESSION":"(X <= (MINX+R) and VX < 0) ? (MINX+R) : ((X >= (MAXX-R) and VX > 0) ? (MAXX-R) : X)"
                                                                                                                                                                                      },
                                                                                                                                                                                  
                                                                                                                                                                                      ; --- pared abajo/arriba ---
                                                                                                                                                                                      {
                                                                                                                                                                                        "VARIABLE":"VY_PREV",
                                                                                                                                                                                        "EXPRESSION":"(Y <= (MINY+R) and VY < 0) ? -VY*E : ((Y >= (MAXY-R) and VY > 0) ? -VY*E : VY)"
                                                                                                                                                                                      },
                                                                                                                                                                                      {
                                                                                                                                                                                        "VARIABLE":"Y_PREV",
                                                                                                                                                                                        "EXPRESSION":"(Y <= (MINY+R) and VY < 0) ? (MINY+R) : ((Y >= (MAXY-R) and VY > 0) ? (MAXY-R) : Y)"
                                                                                                                                                                                      }
                                                                                                                                                                                    ]
                                                                                                                                                                                  }
                                                                                                                                                                                  
                                                                                                                                                                                  INITIAL pelota2D_values, {
                                                                                                                                                                                    R: 3,
                                                                                                                                                                                    E: 0.8,
                                                                                                                                                                                  
                                                                                                                                                                                    MINX: 0,  MAXX: 30,
                                                                                                                                                                                    MINY: 0,  MAXY: 60,
                                                                                                                                                                                  
                                                                                                                                                                                    X_PREV: 20,
                                                                                                                                                                                    Y_PREV: 20,
                                                                                                                                                                                    VX_PREV: 12,
                                                                                                                                                                                    VY_PREV: 7
                                                                                                                                                                                  }
                                                                                                                                                                                  
                                                                                                                                                                                  DYNAMIC {
                                                                                                                                                                                    name: box2d,
                                                                                                                                                                                    CONFIG: V$pelota2D_config,
                                                                                                                                                                                    VALUES: V$pelota2D_values,
                                                                                                                                                                                    X: 200,
                                                                                                                                                                                    Y: 200
                                                                                                                                                                                  }
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  START 1000 
                                                                                                                                                                                  
                                                                                                                                                                                  include ./library_graphics/speedometer.lib
                                                                                                                                                                                  
                                                                                                                                                                                  ;==============================================================
                                                                                                                                                                                  
                                                                                                                                                                                  PROCEDURE agente.init
                                                                                                                                                                                  
                                                                                                                                                                                     
                                                                                                                                                                                  while (1==1)
                                                                                                                                                                                  
                                                                                                                                                                                      SOLVE {  name:"box2d",  DT: 0.1,  SAVEVALUE: "resultado"}
                                                                                                                                                                                  
                                                                                                                                                                                      SAVEVALUE X, X$(resultado.X)
                                                                                                                                                                                      SAVEVALUE Y, X$(resultado.Y)
                                                                                                                                                                                  
                                                                                                                                                                                      ; === Posición de la masa
                                                                                                                                                                                      MOVE {name:masa, X:X$X , Y:X$Y}
                                                                                                                                                                                  
                                                                                                                                                                                  	assign vx,round(X$(resultado.VX),3)
                                                                                                                                                                                  	assign vy,round(X$(resultado.VY),3)
                                                                                                                                                                                  
                                                                                                                                                                                  	CALL indi1.speedometer.set, P$vx
                                                                                                                                                                                  	CALL indi2.speedometer.set, P$vy
                                                                                                                                                                                  
                                                                                                                                                                                  	; CONDICIÓN DE PARADA
                                                                                                                                                                                      ; IF (ABS(P$vx) < 0.001)
                                                                                                                                                                                      ;    stop
                                                                                                                                                                                      ; ENDIF
                                                                                                                                                                                  
                                                                                                                                                                                  advance 0.1,0
                                                                                                                                                                                  
                                                                                                                                                                                  endwhile
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  ENDPROCEDURE
                                                                                                                                                                                  ;====================================================================
                                                                                                                                                                                  
                                                                                                                                                                                  PROCEDURE PRE_RUN
                                                                                                                                                                                  
                                                                                                                                                                                      CALL crear_indicadores
                                                                                                                                                                                      
                                                                                                                                                                                      TIMEOUT agente.init,0
                                                                                                                                                                                  
                                                                                                                                                                                  	TERMINATE_VE 
                                                                                                                                                                                  ENDPROCEDURE
                                                                                                                                                                                  ;=================================================
                                                                                                                                                                                  PROCEDURE crear_indicadores
                                                                                                                                                                                  
                                                                                                                                                                                  	assign config,{title:"VX"
                                                                                                                                                                                      		,x:50,y:15 
                                                                                                                                                                                              ,width:5 ,height:18
                                                                                                                                                                                              ,value:0
                                                                                                                                                                                              ,min_value:-15
                                                                                                                                                                                              ,max_value:15
                                                                                                                                                                                              ,"color":"#ff0000"
                                                                                                                                                                                              ,font:"3px"}
                                                                                                                                                                                              
                                                                                                                                                                                  	call indi1.speedometer.init,V$config
                                                                                                                                                                                  
                                                                                                                                                                                  	assign config,{title:"VY"
                                                                                                                                                                                      		,x:80,y:15 
                                                                                                                                                                                              ,width:5 ,height:18
                                                                                                                                                                                              ,value:0
                                                                                                                                                                                              ,min_value:-15
                                                                                                                                                                                              ,max_value:15
                                                                                                                                                                                              ,"color":"blue"
                                                                                                                                                                                              ,font:"3px"}
                                                                                                                                                                                              
                                                                                                                                                                                  	call indi2.speedometer.init,V$config
                                                                                                                                                                                  
                                                                                                                                                                                  	
                                                                                                                                                                                  ENDPROCEDURE
                                                                                                                                                                                  
                                                                                                                                                                                  ;=================================================
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  
                                                                                                                                                                                  • + Restricciones en Sistemas Dinámicos

                                                                                                                                                                                    ¿Qué es una restricción?

                                                                                                                                                                                    En dinámica de sistemas, una restricción es una condición que debe cumplirse en todo momento durante la simulación. A diferencia de una expresión derivada del comportamiento natural de un componente, la restricción no cambia con el tiempo (no depende de la evolución de las variables), sino que impone una relación fija entre ellas.

                                                                                                                                                                                    Esto se conoce también como restricción algebraica y convierte el sistema en un sistema de ecuaciones diferenciales algebraicas (DAE).

                                                                                                                                                                                    Ejemplo: Dos masas unidas por una varilla rígida

                                                                                                                                                                                    Supongamos dos masas conectadas por muelles y amortiguadores, pero además unidas por una barra rígida que mantiene la distancia constante entre ambas.

                                                                                                                                                                                    Esta condición puede expresarse como:

                                                                                                                                                                                    X2 - X1 = L

                                                                                                                                                                                    Donde:

                                                                                                                                                                                    • X1 y X2 son las posiciones de las masas
                                                                                                                                                                                    • L es la longitud de la barra

                                                                                                                                                                                    Esta igualdad debe mantenerse durante toda la simulación. Para ello, se introduce una fuerza de restricción, calculada mediante un multiplicador de Lagrange (Lambda) que se añade a las ecuaciones del sistema.

                                                                                                                                                                                    Configuración del sistema

                                                                                                                                                                                    INITIAL mech_config, {
                                                                                                                                                                                      EXPRESSIONS: [
                                                                                                                                                                                    
                                                                                                                                                                                        ; Leyes de Newton para cada masa
                                                                                                                                                                                        "F1 - M1 * (V1 - V1_prev) / DT",
                                                                                                                                                                                        "F2 - M2 * (V2 - V2_prev) / DT",
                                                                                                                                                                                    
                                                                                                                                                                                        ; Fuerzas internas: muelle y amortiguador
                                                                                                                                                                                        "FD1 - C1 * V1",
                                                                                                                                                                                        "F_spring1 - K1 * X1",
                                                                                                                                                                                        "FD2 - C2 * V2",
                                                                                                                                                                                        "F_spring2 - K2 * (X2 - X1)",
                                                                                                                                                                                    
                                                                                                                                                                                        ; Fuerza de restricción impuesta por Lagrange
                                                                                                                                                                                        "F_restriction + Lambda",
                                                                                                                                                                                    
                                                                                                                                                                                        ; Suma de fuerzas sobre cada masa
                                                                                                                                                                                        "F1 + F_spring1 + FD1 - F_spring2 - F_restriction",
                                                                                                                                                                                        "F2 + F_spring2 + FD2 + F_restriction",
                                                                                                                                                                                    
                                                                                                                                                                                        ; Integración de posición
                                                                                                                                                                                        "X1 - X1_prev - DT * V1",
                                                                                                                                                                                        "X2 - X2_prev - DT * V2",
                                                                                                                                                                                    
                                                                                                                                                                                        ; ***** Restricción geométrica *****
                                                                                                                                                                                        "X2 - X1 - L"
                                                                                                                                                                                      ],
                                                                                                                                                                                    
                                                                                                                                                                                      STATES: [
                                                                                                                                                                                        "X1", "X2",
                                                                                                                                                                                        "V1", "V2",
                                                                                                                                                                                        "F1", "F2",
                                                                                                                                                                                        "FD1", "FD2",
                                                                                                                                                                                        "F_spring1", "F_spring2",
                                                                                                                                                                                        "F_restriction",
                                                                                                                                                                                        "Lambda"  ; variable de Lagrange
                                                                                                                                                                                      ],
                                                                                                                                                                                    
                                                                                                                                                                                      VARIABLES: [
                                                                                                                                                                                        "M1", "M2",
                                                                                                                                                                                        "K1", "K2",
                                                                                                                                                                                        "C1", "C2",
                                                                                                                                                                                        "L",
                                                                                                                                                                                        "X1_prev", "X2_prev",
                                                                                                                                                                                        "V1_prev", "V2_prev"
                                                                                                                                                                                      ]
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                    

                                                                                                                                                                                    Cómo actúa la restricción

                                                                                                                                                                                    La ecuación "X2 - X1 - L" es una ecuación de restricción.

                                                                                                                                                                                    El sistema debe resolver todas las ecuaciones simultáneamente (incluyendo la restricción), y para ello introduce Lambda, una variable interna que representa la fuerza necesaria para mantener esa condición en todo instante.

                                                                                                                                                                                    Esta fuerza luego se suma (o resta) a las ecuaciones de fuerza total en cada masa:

                                                                                                                                                                                    "F1 + ... - F_restriction"
                                                                                                                                                                                    "F2 + ... + F_restriction"

                                                                                                                                                                                    Esto asegura que si el sistema tiende a violar la restricción (por una aceleración, por ejemplo), la fuerza de restricción reacciona inmediatamente para mantenerla.

                                                                                                                                                                                    ¿Qué resuelve GPSS-Plus?

                                                                                                                                                                                    Todo esto se resuelve en el DYNAMIC, con el uso interno del método de Newton-Raphson y matrices jacobianas, que permiten resolver tanto variables dinámicas (X1, V1, etc.) como variables algebraicas (Lambda) en un mismo sistema.

                                                                                                                                                                                    Conclusión

                                                                                                                                                                                    Las restricciones permiten simular comportamientos realistas y complejos, como:

                                                                                                                                                                                    • Barras rígidas

                                                                                                                                                                                    • Pistones hidráulicos con volumen constante

                                                                                                                                                                                    • Relaciones geométricas en mecanismos

                                                                                                                                                                                    • Conservación de energía o momento

                                                                                                                                                                                    El uso de Lambda como multiplicador de Lagrange es una herramienta clave para introducirlas de forma explícita y controlada en tus sistemas dinámicos.

                                                                                                                                                                                     

                                                                                                                                                                                     


                                                                                                                                                                                    Example:
                                                                                                                                                                                    /*
                                                                                                                                                                                    
                                                                                                                                                                                     Restricciones en Sistemas Dinamicos
                                                                                                                                                                                     
                                                                                                                                                                                    */
                                                                                                                                                                                    
                                                                                                                                                                                    SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                    SYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     
                                                                                                                                                                                    
                                                                                                                                                                                    PLOTTER {NAME:posiciones, Y_0:X1,  Y_1:X2,  X:TIME_AC1}   
                                                                                                                                                                                    
                                                                                                                                                                                    Graphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:"tTexto"}
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    INITIAL mech_config, {
                                                                                                                                                                                      EXPRESSIONS: [
                                                                                                                                                                                        "F1 - M1 * (V1 - V1_PREV) / DT",
                                                                                                                                                                                        "FD1 - C1 * V1",
                                                                                                                                                                                        "F_spring1 - K1 * X1",
                                                                                                                                                                                    
                                                                                                                                                                                        "F_spring2 - K2 * (X2 - X1)",
                                                                                                                                                                                        "FD2 - C2 * V2",
                                                                                                                                                                                        "F2 - M2 * (V2 - V2_PREV) / DT",
                                                                                                                                                                                    
                                                                                                                                                                                        "F_restriction + Lambda",  ; fuerza de restricción (con signo)
                                                                                                                                                                                    
                                                                                                                                                                                        "F1 + F_spring1 + FD1 - F_spring2 - F_restriction",
                                                                                                                                                                                        "F2 + F_spring2 + FD2 + F_restriction",
                                                                                                                                                                                    
                                                                                                                                                                                        "X1 - X1_PREV - DT * V1",
                                                                                                                                                                                        "X2 - X2_PREV - DT * V2",
                                                                                                                                                                                    
                                                                                                                                                                                        "X2 - X1 - L"  ; restricción geométrica
                                                                                                                                                                                      ],
                                                                                                                                                                                    
                                                                                                                                                                                      STATES: [
                                                                                                                                                                                        "X1", "X2",
                                                                                                                                                                                        "V1", "V2",
                                                                                                                                                                                        "F1", "F2",
                                                                                                                                                                                        "FD1", "FD2",
                                                                                                                                                                                        "F_spring1", "F_spring2",
                                                                                                                                                                                        "F_restriction",
                                                                                                                                                                                        "Lambda"
                                                                                                                                                                                      ],
                                                                                                                                                                                    
                                                                                                                                                                                      VARIABLES: [
                                                                                                                                                                                        "M1", "M2",
                                                                                                                                                                                        "K1", "K2",
                                                                                                                                                                                        "C1", "C2",
                                                                                                                                                                                        "L",
                                                                                                                                                                                        "X1_PREV", "X2_PREV",
                                                                                                                                                                                        "V1_PREV", "V2_PREV"
                                                                                                                                                                                      ]
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    INITIAL mech_values, {
                                                                                                                                                                                      M1: 1.5,
                                                                                                                                                                                      M2: 1.5,
                                                                                                                                                                                    
                                                                                                                                                                                      K1: 5,
                                                                                                                                                                                      K2: 8,
                                                                                                                                                                                    
                                                                                                                                                                                      C1: 0.1,
                                                                                                                                                                                      C2: 0.1,
                                                                                                                                                                                    
                                                                                                                                                                                      X1_PREV: 1.5,
                                                                                                                                                                                      V1_PREV: 2,
                                                                                                                                                                                    
                                                                                                                                                                                      X2_PREV: 3.5,
                                                                                                                                                                                      V2_PREV: -1,
                                                                                                                                                                                    
                                                                                                                                                                                      L: 2   ; restricción de distancia entre masas
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    DYNAMIC {
                                                                                                                                                                                      name: sys,
                                                                                                                                                                                      CONFIG: V$mech_config,
                                                                                                                                                                                      VALUES: V$mech_values,
                                                                                                                                                                                      X: 300,
                                                                                                                                                                                      Y: 300,
                                                                                                                                                                                      TOLERANCE: 1e-6,
                                                                                                                                                                                      MAX_ITER: 10
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    START 1000 
                                                                                                                                                                                    
                                                                                                                                                                                    include ./library_graphics/speedometer.lib
                                                                                                                                                                                    
                                                                                                                                                                                    ;==============================================================
                                                                                                                                                                                    
                                                                                                                                                                                    PROCEDURE agente.init
                                                                                                                                                                                    
                                                                                                                                                                                        
                                                                                                                                                                                    while (1==1)
                                                                                                                                                                                    
                                                                                                                                                                                        SOLVE {  name:"sys",  DT: 0.1,  SAVEVALUE: "resultado"}
                                                                                                                                                                                    
                                                                                                                                                                                        SAVEVALUE X1, X$(resultado.X1)
                                                                                                                                                                                        SAVEVALUE X2, X$(resultado.X2)
                                                                                                                                                                                        SAVEVALUE V1, X$(resultado.V1)
                                                                                                                                                                                    
                                                                                                                                                                                        PLOT posiciones, X$X1, X$X2
                                                                                                                                                                                    
                                                                                                                                                                                    	assign rX1,round(X$X1,5)
                                                                                                                                                                                    	assign rX2,round(X$X2,5)
                                                                                                                                                                                        
                                                                                                                                                                                    	CALL tank1.speedometer.set, P$rX1
                                                                                                                                                                                    	CALL tank2.speedometer.set, P$rX2
                                                                                                                                                                                    
                                                                                                                                                                                        IF (ABS(X$V1) < 0.00001)
                                                                                                                                                                                            stop
                                                                                                                                                                                        ENDIF
                                                                                                                                                                                    
                                                                                                                                                                                    advance 0.1,0
                                                                                                                                                                                    
                                                                                                                                                                                    endwhile
                                                                                                                                                                                    
                                                                                                                                                                                    stop
                                                                                                                                                                                    ENDPROCEDURE
                                                                                                                                                                                    ;====================================================================
                                                                                                                                                                                    
                                                                                                                                                                                    PROCEDURE PRE_RUN
                                                                                                                                                                                    
                                                                                                                                                                                        CALL crear_indicadores
                                                                                                                                                                                        
                                                                                                                                                                                        TIMEOUT agente.init,0
                                                                                                                                                                                    
                                                                                                                                                                                    	TERMINATE_VE 
                                                                                                                                                                                    ENDPROCEDURE
                                                                                                                                                                                    ;=================================================
                                                                                                                                                                                    PROCEDURE crear_indicadores
                                                                                                                                                                                    
                                                                                                                                                                                    	assign config,{title:"X1"
                                                                                                                                                                                        		,x:100,y:50 
                                                                                                                                                                                                ,width:50 ,height:180
                                                                                                                                                                                                ,value:0
                                                                                                                                                                                                ,min_value:-5
                                                                                                                                                                                                ,max_value:5
                                                                                                                                                                                                ,"color":"#ff0000"}
                                                                                                                                                                                                
                                                                                                                                                                                    	call tank1.speedometer.init,V$config
                                                                                                                                                                                    
                                                                                                                                                                                    	assign config,{title:"X2"
                                                                                                                                                                                        		,x:400,y:50 
                                                                                                                                                                                                ,width:50 ,height:180
                                                                                                                                                                                                ,value:0
                                                                                                                                                                                                ,min_value:-5
                                                                                                                                                                                                ,max_value:5
                                                                                                                                                                                                ,"color":"purple"}
                                                                                                                                                                                                
                                                                                                                                                                                    	call tank2.speedometer.init,V$config
                                                                                                                                                                                    
                                                                                                                                                                                    	
                                                                                                                                                                                    ENDPROCEDURE
                                                                                                                                                                                    
                                                                                                                                                                                    ;=================================================
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                    
                                                                                                                                                                                • + Season 12: Acoplamiento físico y real
                                                                                                                                                                                  • + Acoplamiento con el mundo real

                                                                                                                                                                                    Hasta ahora, trabajábamos construyendo modelos que operaban dentro de un universo cerrado y controlado dentro del motor de GPSS-Plus. El motor de simulación decidía cuándo avanzaba el tiempo, qué ocurría en cada momento y cómo se comportaban los elementos del sistema. Era un entorno lógico, determinista y autónomo.

                                                                                                                                                                                    Vamos a romper con todo eso con la entrada en escena del mundo real. Ya nada será lo mismo: el enfoque del motor cambia, las entidades vivirán según otros patrones y los flujos dejarán de estar solo bajo nuestro control.

                                                                                                                                                                                    Las entidades no deberían avanzar porque lo decide un número aleatorio, sino porque un sensor físico ha detectado su presencia.

                                                                                                                                                                                    El tiempo

                                                                                                                                                                                    Una de las primeras consecuencias de esta apertura al exterior es que el tiempo deja de pertenecernos.

                                                                                                                                                                                    Ya no podemos acelerarlo ni pausarlo arbitrariamente. Si el motor quiere leer de un sensor, deberá esperar lo que ese sensor tarde en responder. Si la lectura de un fichero se retrasa, también lo hará la simulación. Por eso, al trabajar con el mundo real, la ejecución debe ser en tiempo real.

                                                                                                                                                                                    Lo que antes era solo un “momento”, ahora es estrictamente un segundo del reloj del sistema. Se declara así:

                                                                                                                                                                                    SYSTEM {TYPE:OPTIONS, REAL_TIME:1}

                                                                                                                                                                                    Un ADVANCE 10 significa exactamente espera 10 segundos reales.

                                                                                                                                                                                    A partir de ahí, no hay marcha atrás: todo ocurre al ritmo del mundo físico, y la simulación se convierte en una parte subordinada de la realidad.
                                                                                                                                                                                    El motor deja de ser un simulador puro y pasa a comportarse como un orquestador de procesos reales

                                                                                                                                                                                    La nueva arquitectura del sistema 

                                                                                                                                                                                    Podemos identificar ahora tres grandes componentes del sistema:

                                                                                                                                                                                    • GPSS-Plus y el recurso BRIDGER, que usarán las entidades para emitir y recibir órdenes.
                                                                                                                                                                                    • El mundo exterior, que puede incluir sensores, bases de datos, sistemas de archivos o dispositivos físicos o virtuales. Incluso otros motores de simulación.
                                                                                                                                                                                    • El middleware.js, que es un nuevo software bidireccional que sabrá comunicarse con el BRIDGER y cualquier elemento del mundo exterior.

                                                                                                                                                                                    EL BRIDGER:

                                                                                                                                                                                    Desde el punto de vista de una entidad, además del COMANDO BRIDGER, solo tiene 5 BLOQUES:

                                                                                                                                                                                    • BRIDGE_READ
                                                                                                                                                                                    • BRIDGE_WRITE
                                                                                                                                                                                    • BRIDGE_SUBSCRIBE
                                                                                                                                                                                    • BRIDGE_BROWSE
                                                                                                                                                                                    • BRIDGE_CALL

                                                                                                                                                                                     Estos BLOQUES son los que se comunican con el middleware.js

                                                                                                                                                                                     

                                                                                                                                                                                    EL MIDDLEWARE.js

                                                                                                                                                                                    Disponible en el capítulo de descargas.

                                                                                                                                                                                    Es un conjunto de archivos en javascript para ejecutar en entorno node.js que recibe las instrucciónes del BRIDGER y se las retransmite al dispositivo exterior y viceversa.

                                                                                                                                                                                    En resumen, middleware.js funciona como una capa de intercambio entre protocolos, transformando los cinco bloques definidos en GPSS-Plus en mensajes compatibles con MQTT (el más utilizado, basado en topics) y OPC-UA (el más completo, con estructuras jerárquicas y tipos de dato definidos).

                                                                                                                                                                                    Para las pruebas iniciales existe un servicio público operativo en:

                                                                                                                                                                                    wss://bridger.gpss-plus.com:3000

                                                                                                                                                                                     

                                                                                                                                                                                    EL mundo exterior

                                                                                                                                                                                    Son todos los sensores, actuadores, dispositivos y recursos externos.

                                                                                                                                                                                    Para pruebas, dispones de un cliente virtual OPC-UA que representa un conjunto de sensores y servicios simulados.

                                                                                                                                                                                    Su dirección es: opc.tcp://opcua.gpss-plus.com:4840

                                                                                                                                                                                    Contiene una báscula, un sensor de puerta, sensores de distancia, control PID, lectura y escritura de archivos, e incluso ejecución de programas en segundo plano.

                                                                                                                                                                                    Naturalmente, puedes modificar y ampliar el código para añadir tus propios servicios.

                                                                                                                                                                                    La topología:

                                                                                                                                                                                    Estos tres elementos se distribuyen de la siguiente manera:

                                                                                                                                                                                    gpss-plus          192.168.x.x           192.168.n.m
                                                                                                                                                                                    BRIDGER       <->  middleware.js    <->  Sensor temperatura 
                                                                                                                                                                                    192.168.a.b    |                         Sistema de Ficheros
                                                                                                                                                                                    (El navegador) |                         Base de datos MySQL
                                                                                                                                                                                                   |
                                                                                                                                                                                                   |                         192.168.n.m
                                                                                                                                                                                                   |   192.168.y.y           Generador de números aleatorios
                                                                                                                                                                                                  <->  middleware.js    <->  Red neuronal
                                                                                                                                                                                                   |                         Detector de movimiento OPC-UA
                                                                                                                                                                                                   |
                                                                                                                                                                                                   |
                                                                                                                                                                                                   |   192.168.z.z           192.168.n.m
                                                                                                                                                                                                  <->  middleware.js    <->  Semáforo entre sistemas
                                                                                                                                                                                                                         

                                                                                                                                                                                    En resumen, el navegador que ejecuta GPSS-Plus debe poder acceder a la IP donde esté instalado middleware.js, y este, a su vez, al dispositivo o servicio externo.
                                                                                                                                                                                    La configuración más estable es tener middleware.js en la misma red que los sensores.

                                                                                                                                                                                    En los siguientes capítulos veremos cómo manejar el BRIDGER paso a paso y construiremos nuestros propios dispositivos físicos.

                                                                                                                                                                                     

                                                                                                                                                                                    • + Los recursos externos

                                                                                                                                                                                      En GPSS-Plus llamamos recurso externo a cualquier elemento del sistema que no forma parte directamente del motor de simulación, pero con el que las entidades pueden interactuar.
                                                                                                                                                                                      Estos recursos pueden actuar como fuentes de datos o como actuadores reales dentro del entorno físico o digital.

                                                                                                                                                                                      ¿Qué cuenta como recurso externo?

                                                                                                                                                                                      Ejemplos típicos incluyen:

                                                                                                                                                                                      • Un archivo en el sistema de ficheros (FS).

                                                                                                                                                                                      • Una tabla MySQL o cualquier base de datos accesible por red.

                                                                                                                                                                                      • Un sensor físico, como un detector de temperatura o movimiento, conectado mediante OPC-UA o MQTT.

                                                                                                                                                                                      • Una API REST que devuelve estados o acepta comandos.

                                                                                                                                                                                      • Un dispositivo industrial o electrónico, como una báscula, un motor o una célula de carga.

                                                                                                                                                                                      En resumen, todo aquello que vive fuera del motor pero puede influir en él se considera un recurso externo.

                                                                                                                                                                                      Un solo mecanismo para todos

                                                                                                                                                                                      GPSS-Plus no diferencia entre un sensor y una base de datos.
                                                                                                                                                                                      Ambos son tratados de forma uniforme mediante el recurso BRIDGER, que actúa como interfaz universal de acceso.

                                                                                                                                                                                      Esto simplifica enormemente el modelo mental:
                                                                                                                                                                                      puedes sustituir un sensor real por una tabla MySQL, o por un archivo local, sin reescribir tu modelo, simplemente cambiando la configuración del BRIDGER.

                                                                                                                                                                                      El sensor de ejemplo: OPC-UA Virtual Device

                                                                                                                                                                                      OPC-UA (Open Platform Communications – Unified Architecture) es un estándar industrial para la comunicación estructurada entre dispositivos y sistemas.
                                                                                                                                                                                      Permite intercambiar nodos, atributos y eventos de manera segura, jerárquica y extensible.

                                                                                                                                                                                      En GPSS-Plus puedes conectarte tanto a un servidor OPC-UA real, como al dispositivo virtual incluido para pruebas y desarrollo sin hardware físico.

                                                                                                                                                                                      Este dispositivo virtual expone nodos simulados como:

                                                                                                                                                                                      • Sensor_Temp_1

                                                                                                                                                                                      • Motor_X.Status

                                                                                                                                                                                      • Contador_Piezas

                                                                                                                                                                                      • Puerta_Acceso_A.IsOpen

                                                                                                                                                                                      Podrás leer, escribir o suscribirte a sus cambios usando siempre el mismo mecanismo del BRIDGER.

                                                                                                                                                                                      En los siguientes capítulos veremos cómo:

                                                                                                                                                                                      • Detectar eventos, como la apertura de una puerta simulada.

                                                                                                                                                                                      • Acceder a sistemas de archivos o servicios en línea de solo lectura.

                                                                                                                                                                                      • Ejecutar aplicaciones o scripts remotos.

                                                                                                                                                                                      Tanto el dispositivo OPC-UA virtual como el middleware.js pueden descargarse y modificarse libremente para adaptarlos a tus propias necesidades o hardware real.

                                                                                                                                                                                      • + BRIDGE_SUBSCRIPTION obtener datos en tiempo real

                                                                                                                                                                                        Veamos un ejemplo concreto para entender cómo fluye la información entre GPSS-Plus, middleware.js y el servidor OPC-UA.

                                                                                                                                                                                        Vamos a modificar el comportamiento de un bloque GENERATE para que las entidades se creen cuando un sensor físico (simulado) detecte la apertura de una puerta —en lugar de hacerlo por parámetros de tiempo o probabilidad.

                                                                                                                                                                                        Creando el puente de comunicación

                                                                                                                                                                                        Lo primero es definir el BRIDGER, que será el enlace con el mundo exterior:

                                                                                                                                                                                        Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                        
                                                                                                                                                                                        BRIDGER {NAME:bridge1,X:333,Y:444
                                                                                                                                                                                            ,SERVER:"wss://bridger.gpss-plus.com:3000"
                                                                                                                                                                                            ,CLIENT:"opc.tcp://opcua.gpss-plus.com:4840"
                                                                                                                                                                                            ,OPTIONS:V$options
                                                                                                                                                                                            ,ON_ERROR:bridge1_on_error
                                                                                                                                                                                            }

                                                                                                                                                                                        Los parámetros principales son:

                                                                                                                                                                                        • NAME: identificador interno del BRIDGER (por ejemplo, bridge1).
                                                                                                                                                                                        • SERVER: dirección WebSocket segura donde escucha el middleware.js.
                                                                                                                                                                                        • CLIENT: dirección del servidor OPC-UA (normalmente en la misma red que el middleware).
                                                                                                                                                                                        • OPTIONS: parámetros adicionales (como usuario y contraseña) requeridos para la conexión.
                                                                                                                                                                                        • ON_ERROR: trigger que se ejecutará si ocurre un error en la comunicación.

                                                                                                                                                                                        Más adelante veremos que tanto el middleware.js como el cliente OPC-UA virtual pueden modificarse para adaptarse a otras topologías o protocolos.

                                                                                                                                                                                        Suscribiéndose a un sensor

                                                                                                                                                                                        Una vez definido el puente, debemos iniciar la conexión dentro del PRE_RUN del modelo, y suscribirnos a la variable que representa el estado de la puerta:

                                                                                                                                                                                         

                                                                                                                                                                                            BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                              , VARIABLE: "Sensor_Door_IsOpen"
                                                                                                                                                                                            , TRIGGER: on_open 
                                                                                                                                                                                            }

                                                                                                                                                                                        Esta instrucción mantiene un canal abierto con el sensor para recibir notificaciones automáticas cada vez que cambie su valor.

                                                                                                                                                                                        Donde de nuevo, los parámetros son mínimos:

                                                                                                                                                                                         

                                                                                                                                                                                         

                                                                                                                                                                                        • NAME: el nombre del BRIDGER que gestionará la comunicación.
                                                                                                                                                                                        • VARIABLE: el nombre exacto de la variable publicada por el sensor. (La variable expuesta que más adelante veremos cómo se obtiene)
                                                                                                                                                                                        • TRIGGER: el procedimiento que procesará cada actualización recibida.

                                                                                                                                                                                        Las suscripciones permiten recibir datos en tiempo real sin necesidad de ejecutar lecturas periódicas.

                                                                                                                                                                                        El procedimiento asociado

                                                                                                                                                                                        Cada vez que se recibe un nuevo valor, se ejecuta el trigger indicado.
                                                                                                                                                                                        El parámetro P$(PARAM_A.VALUE) contendrá el valor actual del sensor (0 o 1 en este caso). PARAM_B contiene el número de la entidad invocante y PARAM_C el tiempo.

                                                                                                                                                                                         

                                                                                                                                                                                        procedure on_open
                                                                                                                                                                                           move {name:Text1,TEXT:"DOOR P$(PARAM_A.VALUE)"}
                                                                                                                                                                                             if (P$(PARAM_A.VALUE.EXIST) && P$(PARAM_A.VALUE)==1)
                                                                                                                                                                                                NEW GEN1
                                                                                                                                                                                             endif
                                                                                                                                                                                           TERMINATE_VE
                                                                                                                                                                                        endprocedure
                                                                                                                                                                                        

                                                                                                                                                                                        En este ejemplo, cada vez que el sensor detecta que la puerta se abre (valor = 1), el sistema crea una nueva entidad GEN1, simulando la entrada de una persona.


                                                                                                                                                                                        Example:
                                                                                                                                                                                        SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                        SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                        
                                                                                                                                                                                        Facility {NAME:facility1,X:446,Y:514}
                                                                                                                                                                                        
                                                                                                                                                                                        Graphic {Type:TEXT,Name:Text1,X:376,Y:358}
                                                                                                                                                                                        Graphic {Type:TEXT,Name:TextError,X:376,Y:330,Text:"On error...."}
                                                                                                                                                                                        
                                                                                                                                                                                        Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                        
                                                                                                                                                                                        BRIDGER {NAME:bridge1,X:375,Y:398
                                                                                                                                                                                        	,SERVER:"wss://bridger.gpss-plus.com:3000"
                                                                                                                                                                                        	,CLIENT:"opc.tcp://opcua.gpss-plus.com:4840"
                                                                                                                                                                                            ,OPTIONS:V$options
                                                                                                                                                                                            ,on_error:bridge1_on_error
                                                                                                                                                                                            }
                                                                                                                                                                                        
                                                                                                                                                                                        
                                                                                                                                                                                        POSITION {NAME:POS1,X:264,Y:511}
                                                                                                                                                                                        
                                                                                                                                                                                        POSITION {NAME:POS2,X:658,Y:509}
                                                                                                                                                                                        
                                                                                                                                                                                        
                                                                                                                                                                                        START 100
                                                                                                                                                                                        
                                                                                                                                                                                        ;*****************************************************
                                                                                                                                                                                        
                                                                                                                                                                                        PROCEDURE PRE_RUN
                                                                                                                                                                                            BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                          	, VARIABLE: "Sensor_Door_IsOpen"
                                                                                                                                                                                            , TRIGGER: on_open 
                                                                                                                                                                                            }
                                                                                                                                                                                        
                                                                                                                                                                                        	TERMINATE_VE
                                                                                                                                                                                        ENDPROCEDURE
                                                                                                                                                                                        
                                                                                                                                                                                        ;*****************************************************
                                                                                                                                                                                        GENERATE 0,0,0,0 {NAME:GEN1,X:141,Y:351}
                                                                                                                                                                                            ADVANCE 5,0 {TO:POS1}
                                                                                                                                                                                            ADVANCE 5,0 {TO:facility1}
                                                                                                                                                                                            SEIZE facility1
                                                                                                                                                                                            advance 10,10
                                                                                                                                                                                            RELEASE facility1
                                                                                                                                                                                            ADVANCE 10,0 {TO:POS2}
                                                                                                                                                                                        ENDGENERATE 1
                                                                                                                                                                                        ;----------------------------------------------------------------------------
                                                                                                                                                                                        procedure on_open
                                                                                                                                                                                                move {name:Text1,TEXT:"DOOR P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                if (P$(PARAM_A.VALUE.EXIST) && P$(PARAM_A.VALUE)==1)
                                                                                                                                                                                            	NEW GEN1
                                                                                                                                                                                            	endif
                                                                                                                                                                                        	TERMINATE_VE
                                                                                                                                                                                        endprocedure
                                                                                                                                                                                        ;----------------------------------------------------------------------------
                                                                                                                                                                                        procedure bridge1_on_error
                                                                                                                                                                                            move {name:TextError,TEXT:"P$PARAM_A"}
                                                                                                                                                                                        	TERMINATE_VE
                                                                                                                                                                                        endprocedure
                                                                                                                                                                                        
                                                                                                                                                                                        
                                                                                                                                                                                        • + BRIDGE_BROWSE Vista del dispositivo

                                                                                                                                                                                          Hemos visto que en el OPC-UA virtual podíamos leer repetidamente el valores de la variable "Sensor_Door_IsOpen". 

                                                                                                                                                                                          Sin embargo, no siempre sabremos qué variables o métodos existen dentro de un dispositivo. Por ejemplo, un servicio de archivos podría listar directorios, o una base de datos podría exponer tablas cuyos nombres cambian.

                                                                                                                                                                                          Para eso existe el bloque BRIDGE_BROWSE, que permite consultar la estructura interna del dispositivo o servicio conectado.

                                                                                                                                                                                          ¿Por qué usar BRIDGE_BROWSE?

                                                                                                                                                                                          El comando BRIDGE_BROWSE sirve para descubrir:

                                                                                                                                                                                          • Qué variables están disponibles.
                                                                                                                                                                                          • Qué tipo de dato tiene cada una (Boolean, Double, String, etc.).
                                                                                                                                                                                          • Si son de lectura, escritura o ambas.
                                                                                                                                                                                          • Qué métodos u operaciones están expuestos.

                                                                                                                                                                                          En otras palabras, es el mapa de capacidades del dispositivo.

                                                                                                                                                                                          Por lo que de los 5 BLOQUES del BRIDGER vamos a ver qué se obtiene de BRIDGE_BROWSE.

                                                                                                                                                                                          • BRIDGE_READ
                                                                                                                                                                                          • BRIDGE_WRITE
                                                                                                                                                                                          • BRIDGE_SUBSCRIBE
                                                                                                                                                                                          • BRIDGE_BROWSE
                                                                                                                                                                                          • BRIDGE_CALL

                                                                                                                                                                                          BRIDGE_BROWSE se ejecuta automáticamente durante la primera conexión, pero también puede invocarse manualmente si se desea explorar un nuevo dispositivo o verificar qué ha cambiado.

                                                                                                                                                                                          Ejemplo de salida (debug o canvas)

                                                                                                                                                                                          Al ejecutar BRIDGE_BROWSE, el middleware devuelve un listado jerárquico de las variables accesibles:

                                                                                                                                                                                           

                                                                                                                                                                                          Sensor_Motion_Detected: ns=1;i=1000 [Boolean] 
                                                                                                                                                                                          Sensor_Door_IsOpen: ns=1;i=1001 [Boolean] 
                                                                                                                                                                                          PIDControl_CurrentTemperature: ns=1;i=1002 [Double] 
                                                                                                                                                                                          PIDControl_Setpoint: ns=1;i=1003 [Double] (writable) 
                                                                                                                                                                                          PIDControl_PIDOutput: ns=1;i=1004 [Double] 
                                                                                                                                                                                          Device_Motor_Enabled: ns=1;i=1005 [Boolean] (writable) 
                                                                                                                                                                                          System_Battery_BatteryLevel: ns=1;i=1006 [Double] 
                                                                                                                                                                                          Device_Scale_Weight: ns=1;i=1007 [Double] 
                                                                                                                                                                                          Device_Scale_Trigger: ns=1;i=1008 [Boolean] (writable) 
                                                                                                                                                                                          Device_Piston_IsExtended: ns=1;i=1009 [Boolean] 
                                                                                                                                                                                          Device_Piston_Trigger: ns=1;i=1010 [Boolean] (writable)
                                                                                                                                                                                          ...
                                                                                                                                                                                          ...

                                                                                                                                                                                          Cada línea muestra:

                                                                                                                                                                                          • El nombre de la variable.
                                                                                                                                                                                          • Su identificador interno (ns=1;i=1000, típico en OPC-UA).
                                                                                                                                                                                          • El tipo de dato.
                                                                                                                                                                                          • Si es o no escribible ((writable)).
                                                                                                                                                                                          • Si es un método ejecutable.

                                                                                                                                                                                           

                                                                                                                                                                                           

                                                                                                                                                                                          Usar BRIDGE_BROWSE manualmente

                                                                                                                                                                                          Antes de interactuar con un nuevo dispositivo, ejecuta BRIDGE_BROWSE una vez para conocer su estructura.
                                                                                                                                                                                          Después podrás usar BRIDGE_READ, BRIDGE_WRITE o BRIDGE_SUBSCRIBE de forma precisa sobre las variables que hayas identificado.


                                                                                                                                                                                          Example:
                                                                                                                                                                                          SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                          SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                          
                                                                                                                                                                                          Graphic {Type:TEXT,Name:Text1,X:653,Y:294}
                                                                                                                                                                                          Graphic {Type:TEXT,Name:TextError,X:440,Y:329,Text:"On error...."}
                                                                                                                                                                                          
                                                                                                                                                                                          Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                          
                                                                                                                                                                                          BRIDGER {NAME:bridge1,X:443,Y:405
                                                                                                                                                                                          	,SERVER:"wss://bridger.gpss-plus.com:3000"
                                                                                                                                                                                          	,CLIENT:"opc.tcp://opcua.gpss-plus.com:4840"
                                                                                                                                                                                              ,OPTIONS:V$options
                                                                                                                                                                                              ,on_error:bridge1_on_error
                                                                                                                                                                                              }
                                                                                                                                                                                          
                                                                                                                                                                                          
                                                                                                                                                                                          POSITION {NAME:POS1,X:209,Y:347}
                                                                                                                                                                                          
                                                                                                                                                                                          START 1
                                                                                                                                                                                          
                                                                                                                                                                                          ;*****************************************************
                                                                                                                                                                                          
                                                                                                                                                                                          PROCEDURE PRE_RUN
                                                                                                                                                                                              BRIDGE_BROWSE { NAME: bridge1
                                                                                                                                                                                              , TRIGGER: on_browse 
                                                                                                                                                                                              }
                                                                                                                                                                                          
                                                                                                                                                                                          	TERMINATE_VE
                                                                                                                                                                                          ENDPROCEDURE
                                                                                                                                                                                          
                                                                                                                                                                                          ;*****************************************************
                                                                                                                                                                                          GENERATE 10,0,0,1 {NAME:GEN1,X:141,Y:351}
                                                                                                                                                                                              ADVANCE 5,0 {TO:POS1}
                                                                                                                                                                                          ENDGENERATE 1
                                                                                                                                                                                          ;----------------------------------------------------------------------------
                                                                                                                                                                                          procedure on_browse
                                                                                                                                                                                                  move {name:Text1,TEXT:"BROWSE P$(PARAM_A)"}
                                                                                                                                                                                          	TERMINATE_VE
                                                                                                                                                                                          endprocedure
                                                                                                                                                                                          ;----------------------------------------------------------------------------
                                                                                                                                                                                          procedure bridge1_on_error
                                                                                                                                                                                              move {name:TextError,TEXT:"P$PARAM_A"}
                                                                                                                                                                                              TERMINATE_VE
                                                                                                                                                                                          endprocedure
                                                                                                                                                                                          
                                                                                                                                                                                          
                                                                                                                                                                                          • + BRIDGE_WRITE BRIDGE_READ el actuador

                                                                                                                                                                                            Hasta ahora hemos recibido datos del mundo exterior.
                                                                                                                                                                                            Ahora aprenderemos a enviar instrucciones y leer valores específicos desde GPSS-Plus hacia un dispositivo OPC-UA, MQTT o cualquier otro conectado por el middleware.js

                                                                                                                                                                                            Escribir en un dispositivo

                                                                                                                                                                                            Dar una orden a un actuador es tan simple como asignar un valor y enviarlo:

                                                                                                                                                                                             

                                                                                                                                                                                                ASSIGN values, { Device_Motor_Enabled: 1 }
                                                                                                                                                                                                BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}

                                                                                                                                                                                            Esto escribe en la variable Device_Motor_Enabled del dispositivo, encendiendo el motor (1 = ON, 0 = OFF).
                                                                                                                                                                                            La estructura del comando es siempre la misma:

                                                                                                                                                                                            • VALUES: un conjunto clave-valor con las variables a modificar.
                                                                                                                                                                                            • TRIGGER: el procedimiento que se ejecutará cuando el middleware confirme la entrega.

                                                                                                                                                                                             

                                                                                                                                                                                            Leer el estado de una variable

                                                                                                                                                                                            La lectura se realiza de forma análoga:

                                                                                                                                                                                             

                                                                                                                                                                                                ASSIGN values, ["Device_Motor_Enabled"]
                                                                                                                                                                                                BRIDGE_READ {name:bridge1, trigger:on_read, VALUES:V$values}

                                                                                                                                                                                            En este caso, la lista ["Device_Motor_Enabled"] indica qué variables queremos consultar.
                                                                                                                                                                                            El valor llegará de forma asíncrona, y se recibirá dentro del trigger correspondiente (on_read).

                                                                                                                                                                                            Ejecución asíncrona o síncrona

                                                                                                                                                                                            Por defecto, las operaciones READ y WRITE son asíncronas:
                                                                                                                                                                                            el motor GPSS-Plus continúa su ejecución sin detenerse a esperar la respuesta del dispositivo.

                                                                                                                                                                                            Esto es lo habitual en sistemas físicos, donde la comunicación puede tardar varios cientos de milisegundos.

                                                                                                                                                                                            Si el recurso es inmediato (por ejemplo, un fichero local o una base de datos interna), puedes forzar una operación síncrona con:

                                                                                                                                                                                             

                                                                                                                                                                                             BRIDGE_READ { name:bridge1, VALUES:V$values, SYNC:1, trigger:on_read }

                                                                                                                                                                                            De este modo, la entidad y el motor se bloquea hasta recibir la respuesta.
                                                                                                                                                                                            Debe usarse solo en contextos controlados, ya que detener el tiempo del simulador afecta a todo el sistema.

                                                                                                                                                                                             


                                                                                                                                                                                            Example:
                                                                                                                                                                                            SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                            
                                                                                                                                                                                            Graphic {Type:TEXT,Name:Text1,X:653,Y:294}
                                                                                                                                                                                            Graphic {Type:TEXT,Name:Text2,X:653,Y:194}
                                                                                                                                                                                            Graphic {Type:TEXT,Name:TextError,X:440,Y:329,Text:"On error...."}
                                                                                                                                                                                            
                                                                                                                                                                                            Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                            
                                                                                                                                                                                            BRIDGER {NAME:bridge1,X:443,Y:405
                                                                                                                                                                                            	,SERVER:"wss://bridger.gpss-plus.com:3000"
                                                                                                                                                                                            	,CLIENT:"opc.tcp://opcua.gpss-plus.com:4840"
                                                                                                                                                                                                ,OPTIONS:V$options
                                                                                                                                                                                                ,on_error:bridge1_on_error
                                                                                                                                                                                                }
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            POSITION {NAME:POS1,X:209,Y:347}
                                                                                                                                                                                            POSITION {NAME:POS2,X:409,Y:347}
                                                                                                                                                                                            
                                                                                                                                                                                            START 100
                                                                                                                                                                                            
                                                                                                                                                                                            ;*****************************************************
                                                                                                                                                                                            GENERATE 3,0,0,10 {NAME:GEN1,X:141,Y:351}
                                                                                                                                                                                            
                                                                                                                                                                                            	ASSIGN values, ["Device_Motor_Enabled"]
                                                                                                                                                                                            	BRIDGE_READ {name:bridge1, trigger:on_read, VALUES:V$values}
                                                                                                                                                                                            	ADVANCE 5,0 {TO:POS1}
                                                                                                                                                                                            
                                                                                                                                                                                            	if (D$N %2 == 0 )
                                                                                                                                                                                                ASSIGN values, { Device_Motor_Enabled: 0 }
                                                                                                                                                                                            	BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                                else
                                                                                                                                                                                                ASSIGN values, { Device_Motor_Enabled: 1 }
                                                                                                                                                                                            	BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                                endif
                                                                                                                                                                                                
                                                                                                                                                                                            	ADVANCE 5,0 {TO:POS2}
                                                                                                                                                                                            ENDGENERATE 1
                                                                                                                                                                                            ;----------------------------------------------------------------------------
                                                                                                                                                                                            procedure on_write
                                                                                                                                                                                                    move {name:Text1,TEXT:"AC1$ WRITE P$(PARAM_A)"}
                                                                                                                                                                                            	TERMINATE_VE
                                                                                                                                                                                            endprocedure
                                                                                                                                                                                            ;----------------------------------------------------------------------------
                                                                                                                                                                                            procedure on_read
                                                                                                                                                                                                    move {name:Text2,TEXT:"AC1$ READ P$(PARAM_A)  \n::  Valor: P$(PARAM_A.Device_Motor_Enabled)"}
                                                                                                                                                                                            	TERMINATE_VE
                                                                                                                                                                                            endprocedure
                                                                                                                                                                                            ;----------------------------------------------------------------------------
                                                                                                                                                                                            procedure bridge1_on_error
                                                                                                                                                                                                move {name:TextError,TEXT:"AC1$ P$PARAM_A"}
                                                                                                                                                                                               TERMINATE_VE
                                                                                                                                                                                            endprocedure
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            • + Ejemplo completo: control de pesaje y clasificación

                                                                                                                                                                                              En este ejemplo vamos a ver todos los elementos del sistema en funcionamiento.

                                                                                                                                                                                              El modelo representa una línea de producción automatizada en la que los paquetes llegan uno a uno, son pesados en una báscula, y según su peso son desviados hacia un camino u otro mediante un pistón neumático.

                                                                                                                                                                                              Descripción del proceso

                                                                                                                                                                                              1. Detección de entrada
                                                                                                                                                                                                Un sensor de puerta detecta la llegada de un nuevo paquete (simboliza un producto que entra en la zona).
                                                                                                                                                                                                Cada detección genera una nueva entidad en GPSS-Plus.

                                                                                                                                                                                              2. Control de flujo unitario
                                                                                                                                                                                                Un restroom actúa como liberador unitario, permitiendo que los paquetes pasen de uno en uno hacia la báscula.
                                                                                                                                                                                                El siguiente paquete solo avanza cuando el anterior ha terminado el pesaje.

                                                                                                                                                                                              3. Pesaje
                                                                                                                                                                                                Al llegar a la báscula, la entidad activa el sensor (Device_Scale_Trigger = 1).
                                                                                                                                                                                                Cuando el peso se estabiliza, el sistema asigna el valor leído a la entidad.

                                                                                                                                                                                              4. Clasificación
                                                                                                                                                                                                Si el peso supera un umbral (pesoLimite), se activa el pistón (Device_Piston_Trigger = 1) que desvía el paquete hacia la zona de pesados.
                                                                                                                                                                                                Si no, continúa recto hacia la zona de ligeros.

                                                                                                                                                                                              5. Sincronización con actuadores físicos
                                                                                                                                                                                                El pistón se retrae automáticamente.
                                                                                                                                                                                                GPSS-Plus se suscribe al estado del pistón (Device_Piston_IsExtended) para saber cuándo está listo para el siguiente ciclo.

                                                                                                                                                                                              Arquitectura del sistema:

                                                                                                                                                                                              [Sensor Puerta]  -> genera entidad
                                                                                                                                                                                                       ↓
                                                                                                                                                                                               [Liberador Unitario]
                                                                                                                                                                                                       ↓
                                                                                                                                                                                                     [Báscula]  ←→ OPC-UA Device_Scale_Weight
                                                                                                                                                                                                       ↓
                                                                                                                                                                                                 [Separador / Pistón]
                                                                                                                                                                                                       ↓
                                                                                                                                                                                               [Salida Ligeros] / [Salida Pesados]
                                                                                                                                                                                              
                                                                                                                                                                                              

                                                                                                                                                                                              Example:
                                                                                                                                                                                              SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                              SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                              
                                                                                                                                                                                              Restroom {NAME:LiberadorUnitario,X:155,Y:332}
                                                                                                                                                                                              
                                                                                                                                                                                              Queuer {NAME:zonaBascula,X:163,Y:32}
                                                                                                                                                                                              Graphic {NAME:lineZonaBascula,Type:LINE,color:#FF0000, X1:82,Y1:64,X2:260,Y2:65}
                                                                                                                                                                                              
                                                                                                                                                                                              Restroom {NAME:RestroomBascula,X:226,Y:110}
                                                                                                                                                                                              
                                                                                                                                                                                              initial pesoLimite,8
                                                                                                                                                                                              
                                                                                                                                                                                              Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                              
                                                                                                                                                                                              BRIDGER {NAME:bridge1,X:733,Y:556
                                                                                                                                                                                              	,SERVER:"wss://bridger.gpss-plus.com:3000"
                                                                                                                                                                                              	,CLIENT:"opc.tcp://opcua.gpss-plus.com:4840"
                                                                                                                                                                                                  ,OPTIONS:V$options
                                                                                                                                                                                                  ,on_error:bridge1_on_error
                                                                                                                                                                                                  }
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              POSITION {NAME:POS1,X:97,Y:437}
                                                                                                                                                                                              
                                                                                                                                                                                              POSITION {NAME:POS2,X:95,Y:183}
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              POSITION {NAME:PosBascula,X:225,Y:199}
                                                                                                                                                                                              POSITION {NAME:PosSeparador,X:387,Y:201}
                                                                                                                                                                                              POSITION {NAME:PosPesados,X:513,Y:302}
                                                                                                                                                                                              POSITION {NAME:PosPesados2,X:594,Y:302}
                                                                                                                                                                                              POSITION {NAME:PosLigeros,X:511,Y:197}
                                                                                                                                                                                              POSITION {NAME:PosLigeros2,X:592,Y:197}
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              Graphic {NAME:Text1,Type:TEXT,X:167,Y:484,Text:"On OPEN"}
                                                                                                                                                                                              Graphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:"Error"}
                                                                                                                                                                                              
                                                                                                                                                                                              Graphic {NAME:lineBascula,Type:LINE,color:#FF0000, X1:191,Y1:176,X2:263,Y2:176}
                                                                                                                                                                                              Graphic {NAME:txtBascula,Type:TEXT,X:226,Y:156,Text:"bascula"}
                                                                                                                                                                                              
                                                                                                                                                                                              Graphic {NAME:lineSeparador,Type:LINE,color:#FF0000, X1:363,Y1:201,X2:432,Y2:201}
                                                                                                                                                                                              Graphic {NAME:txtSeparador,Type:TEXT,X:394,Y:143,Text:"separador"}
                                                                                                                                                                                              
                                                                                                                                                                                              START 10
                                                                                                                                                                                              
                                                                                                                                                                                              ;*****************************************************
                                                                                                                                                                                              
                                                                                                                                                                                              PROCEDURE PRE_RUN
                                                                                                                                                                                                  BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                	, VARIABLE: "Sensor_Door_IsOpen"
                                                                                                                                                                                                  , TRIGGER: on_open 
                                                                                                                                                                                                  }
                                                                                                                                                                                              savevalue pesoActual,0
                                                                                                                                                                                              savevalue pesoAnterior,X$pesoActual
                                                                                                                                                                                              
                                                                                                                                                                                              	BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      , VARIABLE: "Device_Scale_Weight"
                                                                                                                                                                                                      , TRIGGER: on_subscription_bascula 
                                                                                                                                                                                                      }
                                                                                                                                                                                              	BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      , VARIABLE: "Device_Piston_IsExtended"
                                                                                                                                                                                                      , TRIGGER: on_subscription_piston 
                                                                                                                                                                                                      }
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              ASSIGN values, { Device_Scale_Trigger: 0 }
                                                                                                                                                                                              BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                               
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              ENDPROCEDURE
                                                                                                                                                                                              
                                                                                                                                                                                              ;*****************************************************
                                                                                                                                                                                              GENERATE 0,0,0,0 {NAME:GEN1,X:91,Y:547}
                                                                                                                                                                                              
                                                                                                                                                                                              ADVANCE 5,0 {TO:POS1,flow:1}
                                                                                                                                                                                              ADVANCE 5,0 {TO:LiberadorUnitario,flow:1}
                                                                                                                                                                                              if (R$(zonaBascula,in)>0)
                                                                                                                                                                                              	rest LiberadorUnitario
                                                                                                                                                                                              endif
                                                                                                                                                                                              queue zonaBascula
                                                                                                                                                                                              
                                                                                                                                                                                              ADVANCE 5,0 {TO:POS2,flow:1}
                                                                                                                                                                                              ADVANCE 5,0 {TO:PosBascula,flow:1}
                                                                                                                                                                                              
                                                                                                                                                                                              ASSIGN values, { Device_Scale_Trigger: 1 }
                                                                                                                                                                                              BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                              
                                                                                                                                                                                              rest RestroomBascula
                                                                                                                                                                                              depart zonaBascula
                                                                                                                                                                                              call liberarRestroomBascula
                                                                                                                                                                                              
                                                                                                                                                                                              ADVANCE 2
                                                                                                                                                                                              mod {subtitle:"P$peso Kg"}
                                                                                                                                                                                              if (P$peso > X$pesoLimite)
                                                                                                                                                                                                  mod {color:"red"}
                                                                                                                                                                                              else
                                                                                                                                                                                                  mod {color:"green"}
                                                                                                                                                                                              endif
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              ASSIGN values, { Device_Scale_Trigger: 0 }
                                                                                                                                                                                              BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                              
                                                                                                                                                                                              timeout moverPiston,4.3,P$peso
                                                                                                                                                                                              
                                                                                                                                                                                              ADVANCE 5,0 {TO:PosSeparador,flow:1}
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              if (P$peso > X$pesoLimite)
                                                                                                                                                                                                  ADVANCE 3,0 {TO:PosPesados}
                                                                                                                                                                                              	ADVANCE 3,0 {TO:PosPesados2,flow:1}
                                                                                                                                                                                              else
                                                                                                                                                                                                  ADVANCE 3,0 {TO:PosLigeros}
                                                                                                                                                                                               	ADVANCE 3,0 {TO:PosLigeros2,flow:1}
                                                                                                                                                                                              endif
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                                 
                                                                                                                                                                                              ENDGENERATE 1
                                                                                                                                                                                              ;----------------------------------------------------------------------------
                                                                                                                                                                                              
                                                                                                                                                                                              procedure on_open
                                                                                                                                                                                                     move {name:Text1,TEXT:"DOOR P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                     if (P$(PARAM_A.VALUE.EXIST))
                                                                                                                                                                                                     if (P$(PARAM_A.VALUE)==1)
                                                                                                                                                                                                  	NEW GEN1
                                                                                                                                                                                                  	endif
                                                                                                                                                                                                      endif
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              
                                                                                                                                                                                              procedure bridge1_on_error
                                                                                                                                                                                                  move {name:TextError,TEXT:"P$PARAM_A"}
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              ;-------------------------
                                                                                                                                                                                              procedure on_subscription_bascula
                                                                                                                                                                                              	savevalue pesoActual,P$(PARAM_A.VALUE)
                                                                                                                                                                                                  move {name:txtBascula,TEXT:"X$pesoActual Kg"}
                                                                                                                                                                                                  if (ABS(X$pesoAnterior-X$pesoActual)<=0.1 && X$pesoActual>1)
                                                                                                                                                                                              	    FOREACH entidad, IN_RESOURCE, RestroomBascula
                                                                                                                                                                                              	    	assign peso,round(X$pesoActual,1), P$entidad
                                                                                                                                                                                              		ENDFOREACH
                                                                                                                                                                                                  
                                                                                                                                                                                              	    wake RestroomBascula
                                                                                                                                                                                                  endif
                                                                                                                                                                                              	savevalue pesoAnterior,X$pesoActual
                                                                                                                                                                                              
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              ;-------------------------
                                                                                                                                                                                              procedure on_write
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              
                                                                                                                                                                                              procedure on_subscription_piston
                                                                                                                                                                                              	if (P$(PARAM_A.VALUE)==1)
                                                                                                                                                                                                  move {name:lineSeparador,Y2:220}
                                                                                                                                                                                                  else
                                                                                                                                                                                                  move {name:lineSeparador,Y2:200}
                                                                                                                                                                                                  endif
                                                                                                                                                                                              	move {name:txtSeparador,text:"Piston P$(PARAM_A.VALUE)"}
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              procedure liberarRestroomBascula
                                                                                                                                                                                              	assign hecho,0
                                                                                                                                                                                              	    FOREACH entidad, IN_RESOURCE, zonaBascula
                                                                                                                                                                                                          if (P$hecho==0)
                                                                                                                                                                                              	    	wake RestroomBascula,P$entidad
                                                                                                                                                                                                          endif
                                                                                                                                                                                                          assign hecho,1
                                                                                                                                                                                              		ENDFOREACH
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              procedure moverPiston
                                                                                                                                                                                                  if (P$PARAM_A>X$pesoLimite)
                                                                                                                                                                                                   ASSIGN values, { Device_Piston_Trigger: 1 }
                                                                                                                                                                                                   BRIDGE_WRITE { name:bridge1, VALUES:V$values, TRIGGER:on_write }
                                                                                                                                                                                              	assign tmp,"pesados"
                                                                                                                                                                                                  else 
                                                                                                                                                                                                  assign tmp,"ligeros"
                                                                                                                                                                                                  endif
                                                                                                                                                                                               ;   move {name:txtSeparador,text:"Piston P$(PARAM_A) Kg -- P$tmp"}
                                                                                                                                                                                              	TERMINATE_VE
                                                                                                                                                                                              endprocedure
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              • + Descargas e instalación componentes BRIDGER

                                                                                                                                                                                                Para ejecutar los ejemplos más avanzados y conectar GPSS-Plus con el mundo real, será necesario instalar tu propio middleware.js y, opcionalmente, el dispositivo virtual OPC-UA y los firmwares de microcontroladores.


                                                                                                                                                                                                middleware.js : Versión 1.03

                                                                                                                                                                                                El middleware actúa como capa intermedia entre GPSS-Plus y los sensores o actuadores físicos.
                                                                                                                                                                                                Debe ejecutarse en un entorno Node.js con comunicación segura (HTTPS / WSS).

                                                                                                                                                                                                Requisitos previos

                                                                                                                                                                                                • Node.js versión 18 o superior.
                                                                                                                                                                                                • Certificados SSL válidos (necesarios para el canal seguro).
                                                                                                                                                                                                • Conectividad de red con GPSS-Plus y los dispositivos OPC-UA o MQTT.

                                                                                                                                                                                                 

                                                                                                                                                                                                Instalación de certificados

                                                                                                                                                                                                 

                                                                                                                                                                                                Dependiendo de si lo ejecutarás en una red privada o en un dominio público, hay dos opciones:

                                                                                                                                                                                                a) Entorno local – usando mkcert

                                                                                                                                                                                                Ideal para pruebas en red interna.

                                                                                                                                                                                                Ejemplo para mcert sobre Debian:

                                                                                                                                                                                                # Crea una entrada de DNS en tui dominio a tu IP local. Ejemplo:
                                                                                                                                                                                                ip11.midominio.com -> 192.168.1.11
                                                                                                                                                                                                
                                                                                                                                                                                                # Instalar el paquete libnss3-tools (que contiene certutil)
                                                                                                                                                                                                sudo apt install libnss3-tools
                                                                                                                                                                                                
                                                                                                                                                                                                # Descarga mkcert
                                                                                                                                                                                                wget https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-arm
                                                                                                                                                                                                
                                                                                                                                                                                                mv mkcert-v1.4.4-linux-arm /usr/local/bin/mkcert
                                                                                                                                                                                                chmod +x /usr/local/bin/mkcert
                                                                                                                                                                                                mkcert -version (v1.4.4)
                                                                                                                                                                                                
                                                                                                                                                                                                # instalar el certificado raiz
                                                                                                                                                                                                mkcert -install
                                                                                                                                                                                                
                                                                                                                                                                                                # crea el certificado para tu subdominio particular de la entrada DNS creada:
                                                                                                                                                                                                mkcert ip11.tudomino.com 192.168.1.11
                                                                                                                                                                                                
                                                                                                                                                                                                # Mueve los certificados a una ubicación estándar:
                                                                                                                                                                                                mkdir -p /etc/ssl/localcerts
                                                                                                                                                                                                mv ip11.tudominio.com+1.pem /etc/ssl/localcerts/
                                                                                                                                                                                                mv ip11.tudominio.com+1-key.pem /etc/ssl/localcerts/
                                                                                                                                                                                                chown root:root /etc/ssl/localcerts/*
                                                                                                                                                                                                chmod 600 /etc/ssl/localcerts/*-key.pem
                                                                                                                                                                                                
                                                                                                                                                                                                #Para evitar errores de confianza, instala el archivo rootCA.pem (ubicado en ~/.local/share/mkcert) 
                                                                                                                                                                                                #como “Entidad de certificación raíz de confianza” en tu sistema.
                                                                                                                                                                                                 mkcert -CAROOT
                                                                                                                                                                                                /root/.local/share/mkcert (ruta probable de localicación del rectificado rootCA.pem)
                                                                                                                                                                                                

                                                                                                                                                                                                b) Servidor público – usando Let’s Encrypt

                                                                                                                                                                                                En servidores accesibles desde Internet, utiliza certificados reales de, por ejemplo let´s Encrypt

                                                                                                                                                                                                Edita el archivo middleware.js

                                                                                                                                                                                                Descarga y edita el archivo middleware.js para incorporar las rutas de los certificados.

                                                                                                                                                                                                const host = process.env.SERVER_HOST || 'ipXXX.your.domain';
                                                                                                                                                                                                
                                                                                                                                                                                                ...
                                                                                                                                                                                                
                                                                                                                                                                                                const certOptions = {
                                                                                                                                                                                                    cert: fs.readFileSync("/etc/letsencrypt/live/PATH_CHAIN/fullchain.pem"),
                                                                                                                                                                                                    key: fs.readFileSync("/etc/letsencrypt/live/PATH_PRIVKEY/privkey.pem")
                                                                                                                                                                                                };
                                                                                                                                                                                                OR
                                                                                                                                                                                                const certOptions = {
                                                                                                                                                                                                    cert: fs.readFileSync("/etc/ssl/localcerts/ipXXX.your.domain.pem"),
                                                                                                                                                                                                    key: fs.readFileSync("/etc/ssl/localcerts/ipXXX.your.domain-key.pem")
                                                                                                                                                                                                };

                                                                                                                                                                                                Ejecuta previniendo la instalación de las librerías necesarias:

                                                                                                                                                                                                npm install express ws cors aedes node-opcua mysql2 serialport mqtt
                                                                                                                                                                                                
                                                                                                                                                                                                npm install
                                                                                                                                                                                                
                                                                                                                                                                                                node middleware.js

                                                                                                                                                                                                La salida esperada es:

                                                                                                                                                                                                # node middleware.js
                                                                                                                                                                                                [INFO] [MQTT_EMBEDDED] Embedded broker listening on port 1883
                                                                                                                                                                                                [INFO] Server listening on https://your.domain:3000
                                                                                                                                                                                                
                                                                                                                                                                                                

                                                                                                                                                                                                Una vez en ejecución, puedes configurarlo como un servicio (systemd, pm2, etc.).


                                                                                                                                                                                                OPC-UA virtual : Versión 1.0

                                                                                                                                                                                                Este servidor OPC-UA virtual simula un conjunto de sensores y actuadores, ideal para desarrollo y pruebas sin hardware físico o el acceso a los recursos locales.

                                                                                                                                                                                                Ejecuta previniendo la instalación de las librerías necesarias:

                                                                                                                                                                                                npm install
                                                                                                                                                                                                npm modbus-serial
                                                                                                                                                                                                
                                                                                                                                                                                                
                                                                                                                                                                                                node index.js

                                                                                                                                                                                                La salida esperada es:

                                                                                                                                                                                                # node index.js
                                                                                                                                                                                                [-] RO: 1:Sensor_Motion_Detected
                                                                                                                                                                                                [-] RO: 1:Sensor_Door_IsOpen
                                                                                                                                                                                                [-] RO: 1:PIDControl_CurrentTemperature
                                                                                                                                                                                                [+] RW: 1:PIDControl_Setpoint
                                                                                                                                                                                                [-] RO: 1:PIDControl_PIDOutput
                                                                                                                                                                                                [+] RW: 1:Device_Motor_Enabled
                                                                                                                                                                                                [-] RO: 1:System_Battery_BatteryLevel
                                                                                                                                                                                                [-] RO: 1:Device_Scale_Weight
                                                                                                                                                                                                [+] RW: 1:Device_Scale_Trigger
                                                                                                                                                                                                [-] RO: 1:Device_Piston_IsExtended
                                                                                                                                                                                                [+] RW: 1:Device_Piston_Trigger
                                                                                                                                                                                                OPC-UA running:  opc.tcp://your.domain:4840
                                                                                                                                                                                                

                                                                                                                                                                                                Firmware para ESP32 : Versión 1.0

                                                                                                                                                                                                Firmware básico para microcontroladores ESP32, diseñado para enviar temperatura y humedad mediante MQTT.

                                                                                                                                                                                                Características:

                                                                                                                                                                                                • Lectura de sensores DHT11 / DHT22.
                                                                                                                                                                                                • Publicación periódica en topics MQTT.
                                                                                                                                                                                                • Compatible con el middleware.js - broker.

                                                                                                                                                                                                Instalación:

                                                                                                                                                                                                • Usa Arduino IDE o PlatformIO.
                                                                                                                                                                                                • Conecta el ESP32 por USB.
                                                                                                                                                                                                • Carga el firmware incluido en el paquete de descargas.
                                                                                                                                                                                                • Instala el driver serial si fuera necesario.
                                                                                                                                                                                                • En la cabecera del código existen más indicaciones.

                                                                                                                                                                                                 


                                                                                                                                                                                                 

                                                                                                                                                                                                Firmware para ESP8266 : Versión 1.0

                                                                                                                                                                                                Firmware alternativo más sencillo, que permite:

                                                                                                                                                                                                • Encender y apagar un LED por MQTT.
                                                                                                                                                                                                • Leer temperatura y humedad de un sensor DHT.

                                                                                                                                                                                                El proceso de instalación es idéntico al del ESP32.
                                                                                                                                                                                                Solo cambia la configuración del tipo de placa y puerto en Arduino IDE.

                                                                                                                                                                                                 

                                                                                                                                                                                                • + BRIDGE_CALL: Ejecución de Métodos Remotos

                                                                                                                                                                                                  Mientras que BRIDGE_READ y BRIDGE_WRITE se limitan a manejar variables de datos simples, BRIDGE_CALL permite a GPSS-Plus ejecutar métodos o funciones remotas definidas en el middleware o en dispositivos industriales.

                                                                                                                                                                                                  Esta capacidad es fundamental para tareas que exceden el ámbito de la simulación pura, tales como:

                                                                                                                                                                                                  • Acceder a bases de datos externas o al sistema de archivos del servidor (File System).
                                                                                                                                                                                                  • Realizar peticiones HTTP a APIs de terceros (consultar el clima, precios, stocks reales).
                                                                                                                                                                                                  • Ejecutar algoritmos complejos u operaciones matemáticas pesadas fuera del motor de simulación.

                                                                                                                                                                                                  Sincronización Asíncrona (Patrón Rest/Wake)

                                                                                                                                                                                                  Dado que estas llamadas ocurren a través de la red, la respuesta no es instantánea. Para evitar que la entidad continúe su camino antes de recibir el resultado, se utiliza el patrón Rest/Wake:

                                                                                                                                                                                                  1. La entidad lanza el BRIDGE_CALL.
                                                                                                                                                                                                  2. Inmediatamente después, entra en un estado de espera con rest esperaCall.
                                                                                                                                                                                                  3. Cuando el procedimiento de trigger recibe la respuesta del servidor, identifica a la entidad mediante P$(PARAM_B) y la libera con un wake.

                                                                                                                                                                                                  Este mecanismo permite modelar con total realismo las latencias de red y el procesamiento asíncrono en sistemas industriales.


                                                                                                                                                                                                  Example:
                                                                                                                                                                                                  SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                                  SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                                  
                                                                                                                                                                                                  initial txt_error," "
                                                                                                                                                                                                  
                                                                                                                                                                                                  Graphic {Type:TEXT,Name:Text1,X:295,Y:123}
                                                                                                                                                                                                  Graphic {Type:TEXT,Name:Text2,X:295,Y:95}
                                                                                                                                                                                                  Graphic {Type:TEXT,Name:Text3,X:295,Y:66}
                                                                                                                                                                                                  Graphic {Type:TEXT,Name:Text4,X:295,Y:35}
                                                                                                                                                                                                  Graphic {Type:TEXT,Name:TextError,X:192,Y:478,Text:"On error...."}
                                                                                                                                                                                                  
                                                                                                                                                                                                  RestRoom {name:esperaCall,x:543,y:488}
                                                                                                                                                                                                  
                                                                                                                                                                                                  Initial options,{user:"user", pass:"1234"}
                                                                                                                                                                                                  
                                                                                                                                                                                                  BRIDGER {NAME:bridge1,X:542,Y:434
                                                                                                                                                                                                  	,SERVER:"wss://tu-dominio.com:3000"
                                                                                                                                                                                                  	,CLIENT:"opc.tcp://tu-dominio.com:4840"
                                                                                                                                                                                                      ,OPTIONS:V$options
                                                                                                                                                                                                      ,on_error:bridge1_on_error
                                                                                                                                                                                                      }
                                                                                                                                                                                                  
                                                                                                                                                                                                  POSITION {NAME:POS1,X:183,Y:300}
                                                                                                                                                                                                  POSITION {NAME:POS2,X:422,Y:300}
                                                                                                                                                                                                  POSITION {NAME:POS3,X:704,Y:300}
                                                                                                                                                                                                  
                                                                                                                                                                                                  START 10
                                                                                                                                                                                                  
                                                                                                                                                                                                  PROCEDURE PRE_RUN
                                                                                                                                                                                                      ; Suscripciones para monitorizar errores de red o sistema
                                                                                                                                                                                                  	BRIDGE_SUBSCRIPTION { NAME: bridge1, VARIABLE: "HTTP_Request_Error", TRIGGER: bridge1_on_error }
                                                                                                                                                                                                  	BRIDGE_SUBSCRIPTION { NAME: bridge1, VARIABLE: "Topics_CurrentTime", TRIGGER: on_clock }
                                                                                                                                                                                                  
                                                                                                                                                                                                      ; Lectura de un archivo en el sistema de archivos remoto (FS)
                                                                                                                                                                                                   	ASSIGN params, [{}] 
                                                                                                                                                                                                  	BRIDGE_CALL {name:bridge1, trigger:on_read_file, params:V$params, method:"Methods_FS_Read"}
                                                                                                                                                                                                  
                                                                                                                                                                                                      ; Petición a una API externa vía HTTP
                                                                                                                                                                                                      ASSIGN params, [{ "method": "GET", "params": { "name": "Antonio" } }]
                                                                                                                                                                                                      BRIDGE_CALL { name:bridge1, trigger:on_http, params:V$params, method:"Methods_HTTP_Request" }
                                                                                                                                                                                                  
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  ENDPROCEDURE 1
                                                                                                                                                                                                  
                                                                                                                                                                                                  GENERATE 3,0,0,10 {NAME:GEN1,X:59,Y:300}
                                                                                                                                                                                                  	ADVANCE 3,0 {TO:POS1}
                                                                                                                                                                                                      ; Llamada a un método de cálculo (Suma) en el servidor
                                                                                                                                                                                                  	ASSIGN params, [{ "Num1": (D$N), "Num2": (0) }]
                                                                                                                                                                                                  	BRIDGE_CALL {name:bridge1, trigger:on_call, params:V$params, method:"Methods_Do_Sum"}
                                                                                                                                                                                                      
                                                                                                                                                                                                      ; Sincronización asíncrona: la entidad espera la respuesta
                                                                                                                                                                                                      rest esperaCall
                                                                                                                                                                                                      mod {subtitle:"Suma: P$(suma)"}
                                                                                                                                                                                                  	
                                                                                                                                                                                                      ADVANCE 4,4 {TO:POS2}
                                                                                                                                                                                                  	ASSIGN params, [{ "Num1": (D$N), "Num2": (D$N) }]
                                                                                                                                                                                                  	BRIDGE_CALL {name:bridge1, trigger:on_call, params:V$params, method:"Methods_Do_Sum"}
                                                                                                                                                                                                      rest esperaCall
                                                                                                                                                                                                      mod {subtitle:"Suma2: P$(suma)"}
                                                                                                                                                                                                      ADVANCE 4,4 {TO:POS3}
                                                                                                                                                                                                  ENDGENERATE 1
                                                                                                                                                                                                  
                                                                                                                                                                                                  ; --- Procedimientos de Respuesta ---
                                                                                                                                                                                                  procedure on_clock
                                                                                                                                                                                                      move {name:Text2,TEXT:"Hora Servidor: P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  endprocedure
                                                                                                                                                                                                  
                                                                                                                                                                                                  procedure on_read_file
                                                                                                                                                                                                      move {name:Text3,TEXT:"Contenido Archivo: P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  endprocedure
                                                                                                                                                                                                  
                                                                                                                                                                                                  procedure on_http
                                                                                                                                                                                                      move {name:Text4,TEXT:"Respuesta HTTP: P$(PARAM_A.VALUE.message)"}
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  endprocedure
                                                                                                                                                                                                  
                                                                                                                                                                                                  procedure on_call
                                                                                                                                                                                                  	move {name:Text1,TEXT:"Suma remota: P$(PARAM_A.sum) Entidad: P$(PARAM_B)"}
                                                                                                                                                                                                  	assign suma,P$(PARAM_A.sum),P$(PARAM_B)
                                                                                                                                                                                                      ; Despertamos a la entidad específica que hizo la llamada
                                                                                                                                                                                                      wake esperaCall,-1,P$(PARAM_B)
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  endprocedure
                                                                                                                                                                                                  
                                                                                                                                                                                                  procedure bridge1_on_error
                                                                                                                                                                                                  	savevalue txt_error,"X$txt_error\nP$(PARAM_A.VALUE)"
                                                                                                                                                                                                      move {name:TextError,TEXT:"X$txt_error"}
                                                                                                                                                                                                  	TERMINATE_VE
                                                                                                                                                                                                  endprocedure
                                                                                                                                                                                                  • + IoT con MQTT

                                                                                                                                                                                                    El protocolo MQTT de comunicaciones es un estándar de los dispositivos para IoT.

                                                                                                                                                                                                    Funciona por "topics", como las redes sociales. Un dispositivo como un termómetro envía la temperatura obtenida sin saber quién va a requerirla y leerla.

                                                                                                                                                                                                    Por lo tanto se requerirá que un "broker MQTT" esté constantemente escuchando lo que los sensores envíen y estar dispuesto a transmitírselo a quien los necesite.

                                                                                                                                                                                                    Un broker estándar es Mosquitto pero el middleware.js incorpora una labor muy parecida.

                                                                                                                                                                                                    Así que volvemos a tener la misma arquitectura de sistema:

                                                                                                                                                                                                    GPSS-Plus <-> (middleware.js + broker MQTT) <-> dispositivo

                                                                                                                                                                                                    En este punto pasamos a programar y utilizar hardware extra.

                                                                                                                                                                                                    No nos preocupemos demasiado, sí sería aconsejable tener ciertas nociones de programación en C pero en general es suficiente con pedir a nuestra IA favorita que nos construya aquello que queramos.

                                                                                                                                                                                                    Vamos a programar un microcontrolador ESP8266 con WIFI que gestione:

                                                                                                                                                                                                    • Un sensor de luz BH1750
                                                                                                                                                                                                    • Un sensor de humedad y temperatura DHT22
                                                                                                                                                                                                    • El encendido y apagado de un led protegido por una resistencia.

                                                                                                                                                                                                    Para ello, usaremos estos 5 elementos hardware y un software para guardar el firmware del ESP8266.

                                                                                                                                                                                                    Uno sencillo es "Arduino IDE" que contiene todas las librerías necesarias.

                                                                                                                                                                                                    Un apunte rápido del camino a seguir es:

                                                                                                                                                                                                    • 1.- Obtener el hardware
                                                                                                                                                                                                    • 2.- Descargar Arduino IDE
                                                                                                                                                                                                    • 3.- Instalar el driver necesario para conectar nuestro equipo a través USB simulando un puerto serial.
                                                                                                                                                                                                    • 4.- Compilar el código C del firmware Arduino IDE del capítulo de descargas. En el código fuente tienes más información.
                                                                                                                                                                                                    • 5.- Pasarlo al dispositivo a través del mismo Arduino IDE.
                                                                                                                                                                                                    • 6.- Enviar por el puerto serial en la misma consola de Arduino IDE los datos de nuestra WIFI y la IP del broker
                                                                                                                                                                                                    • 7.- Ejecutar GPSS-Plus y ver los datos de temperatura, humedad y ver encender y apagar el led.

                                                                                                                                                                                                    Tras esto, hecho uno, hechos todos. Todos los dispositivos en general funcionan a través de microcontroladores ESP32 o ESP8266 y otros y se conectan de la misma manera.

                                                                                                                                                                                                     

                                                                                                                                                                                                    En el ejemplo, podemos ver como realiza las 3 opciones de MQTT estándar:

                                                                                                                                                                                                    • Subscribirse a un sensor (leer la humedad).
                                                                                                                                                                                                    • Escribir en un comando (encender un led)
                                                                                                                                                                                                    • Comprobar el status (Last Will and Testament - LWT)

                                                                                                                                                                                                    Por lo que deberíamos ver los 3 datos de temperatura, humedad y luminosidad además del encendido y apagado del led.

                                                                                                                                                                                                    Debe entenderse que la comprobación de WRITE (Respuesta "OK") es algo que comunica el BRIDGER en la capa de red y no el dispositivo al que solo puede uno subscribirse.

                                                                                                                                                                                                    El browse no tiene contenido puesto que el broker no dispone de esa información.


                                                                                                                                                                                                    Example:
                                                                                                                                                                                                    SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                                    SYSTEM {TYPE:OPTIONS, REAL_TIME:1}
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    Initial options,{  }
                                                                                                                                                                                                    
                                                                                                                                                                                                    BRIDGER {NAME:bridge1,X:733,Y:556
                                                                                                                                                                                                    	,SERVER:"wss://ip10.tidominio.com:3000"
                                                                                                                                                                                                    	,CLIENT:"mqtt://localhost"
                                                                                                                                                                                                        ,OPTIONS:V$options
                                                                                                                                                                                                        ,on_error:bridge1_on_error
                                                                                                                                                                                                        }
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    POSITION {NAME:POS1,X:101,Y:378}
                                                                                                                                                                                                    
                                                                                                                                                                                                    POSITION {NAME:POS2,X:143,Y:115}
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    Graphic {NAME:txtSubscribeL,Type:TEXT,X:394,Y:465,Text:"On SUBS"}
                                                                                                                                                                                                    Graphic {NAME:txtSubscribeH,Type:TEXT,X:394,Y:435,Text:"On SUBS"}
                                                                                                                                                                                                    Graphic {NAME:txtSubscribeT,Type:TEXT,X:394,Y:405,Text:"On SUBS"}
                                                                                                                                                                                                    Graphic {NAME:txtSubscribeS,Type:TEXT,X:394,Y:375,Text:"On SUBS"}
                                                                                                                                                                                                    
                                                                                                                                                                                                    Graphic {NAME:TextBrowse,Type:TEXT,X:116,Y:283,Text:"On Browse"}
                                                                                                                                                                                                    Graphic {NAME:TextWrite,Type:TEXT,X:391,Y:59,Text:"On Write"}
                                                                                                                                                                                                    Graphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:"Error"}
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    START 10
                                                                                                                                                                                                    
                                                                                                                                                                                                    ;*****************************************************
                                                                                                                                                                                                    
                                                                                                                                                                                                    PROCEDURE PRE_RUN
                                                                                                                                                                                                        BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      	, VARIABLE: "devices/elprimero/sensor/humidity"
                                                                                                                                                                                                        , TRIGGER: on_humidity 
                                                                                                                                                                                                        }
                                                                                                                                                                                                        BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      	, VARIABLE: "devices/elprimero/sensor/lux"
                                                                                                                                                                                                        , TRIGGER: on_luz 
                                                                                                                                                                                                        }
                                                                                                                                                                                                        BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      	, VARIABLE: "devices/elprimero/sensor/temperature"
                                                                                                                                                                                                        , TRIGGER: on_temperature 
                                                                                                                                                                                                        }
                                                                                                                                                                                                        BRIDGE_SUBSCRIPTION { NAME: bridge1
                                                                                                                                                                                                      	, VARIABLE: "devices/elprimero/status"
                                                                                                                                                                                                        , TRIGGER: on_status 
                                                                                                                                                                                                        }
                                                                                                                                                                                                    
                                                                                                                                                                                                    ; Sin salida
                                                                                                                                                                                                    BRIDGE_BROWSE {name:bridge1, trigger:on_BROWSE }
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                     
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    ENDPROCEDURE
                                                                                                                                                                                                    
                                                                                                                                                                                                    ;*****************************************************
                                                                                                                                                                                                    GENERATE 5,0 {NAME:GEN1,X:91,Y:547}
                                                                                                                                                                                                    
                                                                                                                                                                                                    ADVANCE 5,0 {TO:POS1,flow:1}
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    if (D$N% 2== 1)
                                                                                                                                                                                                    ASSIGN values, {"devices/elprimero/commands/led": "on"}
                                                                                                                                                                                                    BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                                    else
                                                                                                                                                                                                    ASSIGN values, {"devices/elprimero/commands/led": "off"}
                                                                                                                                                                                                    BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}
                                                                                                                                                                                                    endif
                                                                                                                                                                                                    
                                                                                                                                                                                                    ADVANCE 5,0 {TO:POS2,flow:1}
                                                                                                                                                                                                    
                                                                                                                                                                                                    ENDGENERATE 1
                                                                                                                                                                                                    ;----------------------------------------------------------------------------
                                                                                                                                                                                                    procedure on_read
                                                                                                                                                                                                        move {name:TextRead,TEXT:"WIFI: P$(PARAM_A)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure bridge1_on_error
                                                                                                                                                                                                        move {name:TextError,TEXT:"P$PARAM_A"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    ;-------------------------
                                                                                                                                                                                                    procedure on_write
                                                                                                                                                                                                    move {name:TextWrite,TEXT:"WRITE T:AC1$: P$(PARAM_A.devices/elprimero/commands/led)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure on_luz
                                                                                                                                                                                                    	move {name:txtSubscribeL,text:"Subs LUX P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure on_humidity
                                                                                                                                                                                                    	move {name:txtSubscribeH,text:"Subs HUM P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure on_temperature
                                                                                                                                                                                                    	move {name:txtSubscribeT,text:"Subs TEM P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure on_status
                                                                                                                                                                                                    	move {name:txtSubscribeS,text:"Subs STA P$(PARAM_A.VALUE)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    procedure on_BROWSE
                                                                                                                                                                                                       move {name:TextBrowse,text:"on_BROWSE P$(PARAM_A)"}
                                                                                                                                                                                                    	TERMINATE_VE
                                                                                                                                                                                                    endprocedure
                                                                                                                                                                                                    
                                                                                                                                                                                                    
                                                                                                                                                                                                    • + OPC-UA Virtual Device

                                                                                                                                                                                                      OPC-UA Virtual Device Manual

                                                                                                                                                                                                      This document describes the virtual devices and sensors simulated by the Virtual OPC-UA Plant server.

                                                                                                                                                                                                      1. PIDControl

                                                                                                                                                                                                      Simulates a PID temperature control loop.

                                                                                                                                                                                                      • PIDControl_CurrentTemperature: Current temperature (read-only)
                                                                                                                                                                                                      • PIDControl_Setpoint: Target temperature (read-write)
                                                                                                                                                                                                      • PIDControl_PIDOutput: PID output signal (read-only)

                                                                                                                                                                                                      2. Sensor_Motion

                                                                                                                                                                                                      Simulates a motion detector.

                                                                                                                                                                                                      • Sensor_Motion_Detected: Boolean value indicating motion detected (read-only)

                                                                                                                                                                                                      3. Sensor_Door

                                                                                                                                                                                                      Simulates a door open/close sensor.

                                                                                                                                                                                                      • Sensor_Door_IsOpen: Boolean value indicating if the door is open (read-write)

                                                                                                                                                                                                      4. Sensor_Humidity

                                                                                                                                                                                                      Simulates an environmental humidity sensor.

                                                                                                                                                                                                      • Sensor_Humidity_Value: Humidity level in % (read-only)

                                                                                                                                                                                                      5. Sensor_Proximity

                                                                                                                                                                                                      Simulates a proximity sensor.

                                                                                                                                                                                                      • Sensor_Proximity_Distance: Distance to object in cm (read-only)

                                                                                                                                                                                                      6. Sensor_Light

                                                                                                                                                                                                      Simulates a light level sensor.

                                                                                                                                                                                                      • Sensor_Light_Lux: Light level in lux (read-only)

                                                                                                                                                                                                      7. Sensor_Gas

                                                                                                                                                                                                      Simulates a gas leakage detector.

                                                                                                                                                                                                      • Sensor_Gas_Concentration: Gas concentration in ppm (read-only)

                                                                                                                                                                                                      8. Sensor_Vibration

                                                                                                                                                                                                      Simulates a vibration sensor.

                                                                                                                                                                                                      • Sensor_Vibration_Level: Vibration intensity (read-only)

                                                                                                                                                                                                      9. Sensor_Smoke

                                                                                                                                                                                                      Simulates a smoke detector.

                                                                                                                                                                                                      • Sensor_Smoke_Detected: Boolean value indicating presence of smoke (read-only)

                                                                                                                                                                                                      10. Sensor_Battery

                                                                                                                                                                                                      Simulates a battery status monitor.

                                                                                                                                                                                                      • Sensor_Battery_Level: Battery level in % (read-only)

                                                                                                                                                                                                      11. Sensor_Network

                                                                                                                                                                                                      Simulates network signal strength.

                                                                                                                                                                                                      • Sensor_Network_Strength: Signal strength from 0 to 100 (read-only)

                                                                                                                                                                                                      12. System_PowerStatus

                                                                                                                                                                                                      Indicates if the system is powered.

                                                                                                                                                                                                      • System_PowerStatus_IsPowered: Boolean value indicating power status (read-only)

                                                                                                                                                                                                      Note: Writable variables are intended for use with commands such as OPC_WRITE or OPC_SUBSCRIPTION.


                                                                                                                                                                                                      Example:
                                                                                                                                                                                                      SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}
                                                                                                                                                                                                      
                                                                                                                                                                                                      OPC {NAME:opc1,X:100,Y:121,visible:1, TYPE:REAL,
                                                                                                                                                                                                        CLIENT:"opc.tcp://192.168.1.10:4840"}
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      POSITION {NAME:POS1,X:251,Y:335}
                                                                                                                                                                                                      POSITION {NAME:POS2,X:469,Y:341}
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      Graphic {NAME:Text1,Type:TEXT,X:425,Y:314,Text:"Entidad"}
                                                                                                                                                                                                      Graphic {NAME:Text2,Type:TEXT,X:424,Y:124,Text:"qqqqqq"}
                                                                                                                                                                                                      Graphic {NAME:Text3,Type:TEXT,X:425,Y:514,Text:"write"}
                                                                                                                                                                                                      Graphic {NAME:Text4,Type:TEXT,X:115,Y:398,Text:"door"}
                                                                                                                                                                                                      
                                                                                                                                                                                                      initial nAgente,0
                                                                                                                                                                                                      START 500
                                                                                                                                                                                                      
                                                                                                                                                                                                      ;*****************************************************
                                                                                                                                                                                                      
                                                                                                                                                                                                      PROCEDURE PRE_RUN
                                                                                                                                                                                                      ;	timeout agente.main,0
                                                                                                                                                                                                      
                                                                                                                                                                                                          OPC_SUBSCRIPTION { NAME: opc1
                                                                                                                                                                                                          	, VARIABLE: "PIDControl_CurrentTemperature"
                                                                                                                                                                                                              , TRIGGER: on_suscription 
                                                                                                                                                                                                              }
                                                                                                                                                                                                          OPC_SUBSCRIPTION { NAME: opc1
                                                                                                                                                                                                          	, VARIABLE: "Sensor_Door_IsOpen"
                                                                                                                                                                                                              , TRIGGER: puerta_abierta 
                                                                                                                                                                                                              }
                                                                                                                                                                                                      	TERMINATE_VE
                                                                                                                                                                                                      ENDPROCEDURE 1
                                                                                                                                                                                                      
                                                                                                                                                                                                      ;*****************************************************
                                                                                                                                                                                                      GENERATE 0,0,0,0 {NAME:GEN1,X:43,Y:300}
                                                                                                                                                                                                      
                                                                                                                                                                                                      ;OPC_BROWSE {      name:opc1,      savevalue:variables    }
                                                                                                                                                                                                      if (D$N==1)
                                                                                                                                                                                                          ASSIGN values, { PIDCONTROL_SETPOINT: 10 }
                                                                                                                                                                                                      ;    OPC_WRITE { NAME: opc1, VALUES: V$values }
                                                                                                                                                                                                          OPC_WRITE { NAME: opc1, VALUES: V$values ,async:1,trigger:on_write}
                                                                                                                                                                                                         
                                                                                                                                                                                                      endif
                                                                                                                                                                                                      
                                                                                                                                                                                                      ;move {name:Text1,TEXT:"Entidad Resultado X$(variables)"}
                                                                                                                                                                                                      OPC_READ {name:opc1, savevalue:temperatura  }
                                                                                                                                                                                                      ;OPC_READ {name:opc1, async:1,trigger:on_results }
                                                                                                                                                                                                      
                                                                                                                                                                                                      move {name:Text1,TEXT:"Resultado READ: X$(temperatura.Sensor_Proximity_DistanceInCm)"}
                                                                                                                                                                                                      	
                                                                                                                                                                                                      ADVANCE 20,0 {TO:pos1}
                                                                                                                                                                                                         
                                                                                                                                                                                                      ENDGENERATE 1
                                                                                                                                                                                                      
                                                                                                                                                                                                      procedure on_suscription
                                                                                                                                                                                                      	
                                                                                                                                                                                                          move {name:Text2,TEXT:"on_suscription AC1$:\n P$PARAM_A \N P$PARAM_B P$PARAM_C"}
                                                                                                                                                                                                          
                                                                                                                                                                                                          TERMINATE_VE
                                                                                                                                                                                                      endprocedure
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      procedure on_results
                                                                                                                                                                                                      	
                                                                                                                                                                                                          move {name:Text2,TEXT:"on_results AC1$:\n P$PARAM_A \N P$PARAM_B P$PARAM_C"}
                                                                                                                                                                                                          
                                                                                                                                                                                                          TERMINATE_VE
                                                                                                                                                                                                      endprocedure
                                                                                                                                                                                                      
                                                                                                                                                                                                      procedure on_write
                                                                                                                                                                                                             move {name:Text3,TEXT:"on_write AC1$:\n P$PARAM_A \N P$PARAM_B P$PARAM_C"}
                                                                                                                                                                                                      	TERMINATE_VE
                                                                                                                                                                                                      endprocedure
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      procedure puerta_abierta
                                                                                                                                                                                                             move {name:Text4,TEXT:"DOOR AC1$ ; P$PARAM_A"}
                                                                                                                                                                                                             if (P$(PARAM_A.value)==1) {debug:1}
                                                                                                                                                                                                          	NEW GEN1
                                                                                                                                                                                                          	endif
                                                                                                                                                                                                      	TERMINATE_VE
                                                                                                                                                                                                      endprocedure
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                      
                                                                                                                                                                                                    • + Spin-offs
                                                                                                                                                                                                      • + Interfaz de Usuario-Modelo UI
                                                                                                                                                                                                        • + Ejemplo de uso
                                                                                                                                                                                                          Este ejemplo muestra el uso de todos los tipos de elementos UI disponibles en GPSS-Plus. Al declarar bloques UI, se crea automáticamente una ventana flotante que contiene los controles definidos (botones, inputs, sliders, etc.). Cada control tiene un TRIGGER, que se ejecuta cuando el usuario interactúa con él. El trigger lanza una entidad virtual que recibe los datos y puede actuar sobre el modelo. Además, desde GPSS se puede modificar el valor de los controles en tiempo real utilizando SET_UI, lo que permite sincronizar la interfaz con el estado interno de la simulación. Solo necesitas declararles un ID para poder referenciarlos. Esta funcionalidad resulta especialmente útil para ajustar parámetros durante la simulación, probar escenarios, activar eventos manuales o construir entornos educativos interactivos. Todo el control es declarativo, y la lógica se mantiene en los procedimientos.
                                                                                                                                                                                                          Example:
                                                                                                                                                                                                          ;========== UI DECLARATIVO ==========
                                                                                                                                                                                                          UI {TYPE: BUTTON, id:botonA,TEXT: "Lanzar Entidad A", LABEL: "Botón A", TRIGGER: lanzarA}
                                                                                                                                                                                                          UI {TYPE: BUTTON, id:botonB, TEXT: "Lanzar Entidad B", LABEL: "Valores de fábrica", TRIGGER: lanzarB}
                                                                                                                                                                                                          
                                                                                                                                                                                                          UI {TYPE: INPUT, ID: unInput, LABEL: "Nombre", VALUE: "Alice", TRIGGER: capturarInput}
                                                                                                                                                                                                          UI {
                                                                                                                                                                                                            TYPE: SLIDER, ID: unSlider, LABEL: "Velocidad",
                                                                                                                                                                                                            VALUE: 1, MIN: 0.1, MAX: 2, STEP: 0.1,
                                                                                                                                                                                                            TRIGGER: capturarInput
                                                                                                                                                                                                          }
                                                                                                                                                                                                          UI {TYPE: SELECT, id:unSelect,LABEL: "Modo", OPTIONS: "Normal,Avanzado,Turbo", TRIGGER: capturarInput}
                                                                                                                                                                                                          UI {TYPE: CHECKBOX, id:unCheck,LABEL: "Activar turbo", VALUE: 1, TRIGGER: capturarInput}
                                                                                                                                                                                                          UI {TYPE: RADIO, id:unRadio, LABEL: "Color", OPTIONS: "Rojo,Verde,Azul", TRIGGER: capturarInput}
                                                                                                                                                                                                          
                                                                                                                                                                                                          ;========== GRÁFICOS ==========
                                                                                                                                                                                                          GRAPHIC {NAME:infoTexto, TYPE:TEXT, X:400, Y:100, TEXT:"Esperando..."}
                                                                                                                                                                                                          GRAPHIC {NAME:infoDato, TYPE:TEXT, X:400, Y:130, TEXT:"Ningún valor aún"}
                                                                                                                                                                                                          
                                                                                                                                                                                                          ;========== POSICIONES Y ANIMACIÓN ==========
                                                                                                                                                                                                          POSITION {NAME:ENTRADA, X:200, Y:220}
                                                                                                                                                                                                          POSITION {NAME:SALIDA, X:500, Y:220}
                                                                                                                                                                                                          
                                                                                                                                                                                                          START 100
                                                                                                                                                                                                          ;-----------------------------------------------------------------
                                                                                                                                                                                                          
                                                                                                                                                                                                          GENERATE 0,0,0,0 {NAME:GEN_A, X:102,Y:161,ECOLOR:#0099ff}
                                                                                                                                                                                                          ADVANCE 10,0 {TO:ENTRADA}
                                                                                                                                                                                                          ADVANCE 10,0 {TO:SALIDA}
                                                                                                                                                                                                          TERMINATE 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          GENERATE 0,0,0,0 {NAME:GEN_B, X:104,Y:406,ECOLOR:#ff9900}
                                                                                                                                                                                                          ADVANCE 10,0 {TO:ENTRADA}
                                                                                                                                                                                                          ADVANCE 10,0 {TO:SALIDA}
                                                                                                                                                                                                          SET_UI unSlider, 1.5
                                                                                                                                                                                                          SET_UI unInput, "Fin camino B"
                                                                                                                                                                                                          SET_UI unSelect, "turbo"
                                                                                                                                                                                                          SET_UI unRadio, "AZUL"
                                                                                                                                                                                                          SET_UI unCheck, 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          TERMINATE 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          ;========== PROCEDIMIENTOS ==========
                                                                                                                                                                                                          PROCEDURE lanzarA
                                                                                                                                                                                                            NEW GEN_A
                                                                                                                                                                                                            TERMINATE
                                                                                                                                                                                                          ENDPROCEDURE 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          PROCEDURE lanzarB
                                                                                                                                                                                                            NEW GEN_B
                                                                                                                                                                                                            TERMINATE
                                                                                                                                                                                                          ENDPROCEDURE 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          PROCEDURE capturarInput
                                                                                                                                                                                                            MOVE {NAME:infoTexto, TEXT:"ID = PP$A"}
                                                                                                                                                                                                            MOVE {NAME:infoDato, TEXT:"VALUE = PP$B"}
                                                                                                                                                                                                          
                                                                                                                                                                                                            ; Si es el input del nombre, actualiza UI desde GPSS
                                                                                                                                                                                                            IF "botonA",==,"PP$A"
                                                                                                                                                                                                              SET_UI velSlider, 1.5
                                                                                                                                                                                                            ENDIF
                                                                                                                                                                                                          
                                                                                                                                                                                                            TERMINATE
                                                                                                                                                                                                          ENDPROCEDURE 1
                                                                                                                                                                                                          
                                                                                                                                                                                                          
                                                                                                                                                                                                          
                                                                                                                                                                                                        • + Funciones de comportamiento
                                                                                                                                                                                                          • + Qué son las Behavior Functions

                                                                                                                                                                                                            El concepto parte de la abstracción de un recurso más grande que una simple FACILITY o STORAGE.

                                                                                                                                                                                                            Imagina que cada taller puede ser representado como una función gaussiana de tiempo de permanencia. En GPSS-Plus, si conocemos los resultados de configuraciones previas (ej. 5 empleados vs 8 empleados), no necesitamos repetir la simulación micro para un taller de 6 empleados.

                                                                                                                                                                                                            GPSS-Plus toma la decisión interpolada de los resultados previsibles:

                                                                                                                                                                                                            ADVANCE BF$(BFtalleres, 4, 2, 4)

                                                                                                                                                                                                            Este bloque consultará la función de comportamiento más cercana, interpolará los valores y creará dinámicamente una nueva función adaptada.

                                                                                                                                                                                                            Esta es la filosofía de las Behavior Functions: reutilizar conocimiento previo para simular infinitos escenarios nuevos a partir de la interpolación de datos.

                                                                                                                                                                                                            • + Una primera aplicación

                                                                                                                                                                                                              En este primer ejemplo práctico, vamos a aplicar el concepto de Behavior Functions (BF$) de forma sencilla.

                                                                                                                                                                                                              Hasta ahora, cuando necesitábamos modelar un tiempo aleatorio, usábamos funciones normales (FN$) previamente definidas.
                                                                                                                                                                                                              Con las Behavior Functions, podemos interpolar automáticamente los parámetros adecuados a partir de unos pocos ejemplos, sin tener que definir funciones exactas para cada caso.

                                                                                                                                                                                                              En este caso partimos del dos casos ya estudiados que hemos plasmado en las siguientes funciones de comportamiento que hemos llamado

                                                                                                                                                                                                              "tramo1D":

                                                                                                                                                                                                              GFunction {behavior: tramo1D, p0:10, b:20, Sigma1:2.5, Sigma2:2.5}
                                                                                                                                                                                                              GFunction {behavior: tramo1D, p0:20, b:40, Sigma1:3.0, Sigma2:3.5}
                                                                                                                                                                                                              

                                                                                                                                                                                                              Son dos funciones de distribución gaussianas, ambas con un único parámetro que las definió.
                                                                                                                                                                                                              Para la primera P0=10 y la segunda P0=20.
                                                                                                                                                                                                              Imaginemos que son, por ejemplo, el número de empleados en un taller. Con ese dato como parámetro, los datos recogidos en el sistema daban las variables de la función gaussiana b:20, Sigma1:2.5, Sigma2:2.5 y b:40, Sigma1:3.0, Sigma2:3.5 respectivamente.

                                                                                                                                                                                                              Es decir:
                                                                                                                                                                                                              P0=10 -> b:20, Sigma1:2.5, Sigma2:2.5
                                                                                                                                                                                                              P0=20 -> b:40, Sigma1:3.0, Sigma2:3.5

                                                                                                                                                                                                              Estas son nuestras Funciones de comportamiento.

                                                                                                                                                                                                              Por supuesto , puede que se recojan más posibilidades que harán que el cálculo gane en eficacia.

                                                                                                                                                                                                              Pues bien, ahora que tenemos como se comportan estos talleres, podremos predecir cómo se comportará cualquier otro del que conozcamos su parámetro configurador del comportamiento.

                                                                                                                                                                                                              Su P0. Así, para el caso de P0=15, es inmediato pensar que, para una sola dimensión de interpolación (un parámetro de configuración equivale a una interpolación lineal) la salida de la función gaussiana resultante será exactamente la media de cada uno de los parámetros estudiados, puesto que 15 es la mitad entre 10 y 20.

                                                                                                                                                                                                              Así: P0=15 -> b=30 , sigma1=2.75 , sigma2=3

                                                                                                                                                                                                              Para el caso de P0=30 los resultados serán: P0= 30 -> b=60 , sigma1=3.5 , sigma2=4.5

                                                                                                                                                                                                              Las Behavior Functions van a hacer este cálculo automáticamente.

                                                                                                                                                                                                              Para este ejemplo, simularemos el recorrido de entidades a través de tres recursos:

                                                                                                                                                                                                              El primer tramo utilizará una función tradicional (FN$), como hasta ahora.

                                                                                                                                                                                                              Una gaussiana con B=20 se centrará en ese tiempo.

                                                                                                                                                                                                              El segundo y tercer tramo utilizarán funciones generadas dinámicamente (BF$) a partir de un comportamiento interpolado.

                                                                                                                                                                                                              Los resultados en el informe deberán ser curvas centradas en 20, 60 y 30 respectivamente.

                                                                                                                                                                                                              Ahora podrás entender porqué, en los informes, tienes una aproximación de la fórmula de la función resultante del los datos estadísticos recogidos. Cuando hagas una simulación de un sistema complejo, estas fórmulas se convertirán en las funciones de comportamiento futuras.


                                                                                                                                                                                                              Example:
                                                                                                                                                                                                              SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                                                                                              
                                                                                                                                                                                                              Graphic {NAME:Text1,Type:TEXT,X:230,Y:496,Text:"FN$gName1: b=20 / sigma1=2.5 / sigma2=2.5"}
                                                                                                                                                                                                              
                                                                                                                                                                                                              Graphic {NAME:Text2,Type:TEXT,X:349,Y:100,Text:"BF$(tramo1D 30): b=40 / sigma1=3.5 / sigma2=4.5 (interpolado)"}
                                                                                                                                                                                                              
                                                                                                                                                                                                              Graphic {NAME:Text3,Type:TEXT,X:523,Y:397,Text:"BF$(tramo1D 15): b=30 / sigma1=2.75 / sigma2=3 (interpolado)"}
                                                                                                                                                                                                              
                                                                                                                                                                                                              
                                                                                                                                                                                                              Facility {NAME:VENTANILLA1,X:233,Y:444,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}
                                                                                                                                                                                                              Facility {NAME:VENTANILLA2,X:352,Y:152,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}
                                                                                                                                                                                                              Facility {NAME:VENTANILLA3,X:523,Y:354,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}
                                                                                                                                                                                                              
                                                                                                                                                                                                              POSITION {NAME:POS1,X:708,Y:268}
                                                                                                                                                                                                              
                                                                                                                                                                                                              Function {behavior: tramo1D, type: GAUSS, p0:10, Name:gName1, a:1, b:20, Sigma1:2.5, Sigma2:2.5}
                                                                                                                                                                                                              Function {behavior: tramo1D, type: GAUSS, p0:20, Name:gName2, a:1, b:40, Sigma1:3.0, Sigma2:3.5}
                                                                                                                                                                                                              
                                                                                                                                                                                                              START 2000
                                                                                                                                                                                                              
                                                                                                                                                                                                              ;*****************************************************
                                                                                                                                                                                                              GENERATE 2,0,0,0 {NAME:GEN1,X:56,Y:319}
                                                                                                                                                                                                              
                                                                                                                                                                                                              ADVANCE 30,10 {TO:VENTANILLA1}
                                                                                                                                                                                                              
                                                                                                                                                                                                              SEIZE VENTANILLA1
                                                                                                                                                                                                              ADVANCE FN$gName1
                                                                                                                                                                                                              ;ADVANCE BF$(tramo1D,10)
                                                                                                                                                                                                              RELEASE VENTANILLA1
                                                                                                                                                                                                              
                                                                                                                                                                                                              ADVANCE 20,10 {TO:VENTANILLA2}
                                                                                                                                                                                                              
                                                                                                                                                                                                              SEIZE VENTANILLA2
                                                                                                                                                                                                              ADVANCE BF$(tramo1D,30)
                                                                                                                                                                                                              RELEASE VENTANILLA2
                                                                                                                                                                                                              
                                                                                                                                                                                                              ADVANCE 20,10 {TO:VENTANILLA3}
                                                                                                                                                                                                              
                                                                                                                                                                                                              SEIZE VENTANILLA3
                                                                                                                                                                                                              ADVANCE BF$(tramo1D,15)
                                                                                                                                                                                                              RELEASE VENTANILLA3
                                                                                                                                                                                                              
                                                                                                                                                                                                              
                                                                                                                                                                                                              ADVANCE 20,0 {TO:POS1}
                                                                                                                                                                                                              
                                                                                                                                                                                                              
                                                                                                                                                                                                              TERMINATE 1
                                                                                                                                                                                                              
                                                                                                                                                                                                              
                                                                                                                                                                                                              
                                                                                                                                                                                                              • + Interpolación con múltiples parámetros

                                                                                                                                                                                                                Damos un paso más: usamos dos parámetros de entrada para modelar situaciones más complejas (Longitud y Meteorología).

                                                                                                                                                                                                                La interpolación se realiza mediante técnicas IDW (Inverse Distance Weighting). Esto permite que, dadas combinaciones intermedias de longitud y clima, GPSS-Plus genere de forma automática una función de tiempo adaptada.

                                                                                                                                                                                                                Este ejemplo muestra cómo construir sistemas de simulación versátiles y realistas con muy poca información de base, simplemente interpolando el espacio entre casos conocidos.


                                                                                                                                                                                                                Example:
                                                                                                                                                                                                                SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                                                                                                
                                                                                                                                                                                                                QUEUER {NAME:TRAMO1,X:66,Y:350,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                QUEUER {NAME:TRAMO2,X:276,Y:554,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                QUEUER {NAME:TRAMO3,X:664,Y:360,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                
                                                                                                                                                                                                                POSITION {NAME:POS1,X:115,Y:92}
                                                                                                                                                                                                                POSITION {NAME:POS2,X:146,Y:449}
                                                                                                                                                                                                                POSITION {NAME:POS3,X:432,Y:506}
                                                                                                                                                                                                                POSITION {NAME:POS4,X:756,Y:33}
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                Function {behavior: tramo2D, type:GAUSS, p0:50, p1:0, Name:gName1, a:1, b:40, Sigma1:3.0, Sigma2:3.0}
                                                                                                                                                                                                                Function {behavior: tramo2D, type:GAUSS, p0:50, p1:1, Name:gName2, a:1, b:60, Sigma1:5.0, Sigma2:5.0}
                                                                                                                                                                                                                Function {behavior: tramo2D, type:GAUSS, p0:100, p1:0, Name:gName3, a:1, b:80, Sigma1:5.0, Sigma2:5.0}
                                                                                                                                                                                                                Function {behavior: tramo2D, type:GAUSS, p0:100, p1:1, Name:gName4, a:1, b:120, Sigma1:10.0, Sigma2:10.0}
                                                                                                                                                                                                                
                                                                                                                                                                                                                START 2000
                                                                                                                                                                                                                
                                                                                                                                                                                                                ;*****************************************************
                                                                                                                                                                                                                GENERATE 2,0,0,0 {NAME:GEN1,X:55,Y:41}
                                                                                                                                                                                                                
                                                                                                                                                                                                                ADVANCE 30,10 {TO:POS1}
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                queue TRAMO1
                                                                                                                                                                                                                ADVANCE BF$(tramo2D,60,0.5) {TO:POS2}
                                                                                                                                                                                                                depart TRAMO1
                                                                                                                                                                                                                
                                                                                                                                                                                                                queue TRAMO2
                                                                                                                                                                                                                ADVANCE BF$(tramo2D,55,0.5) {TO:POS3}
                                                                                                                                                                                                                depart TRAMO2
                                                                                                                                                                                                                
                                                                                                                                                                                                                queue TRAMO3
                                                                                                                                                                                                                ADVANCE BF$(tramo2D,90,0.5) {TO:POS4}
                                                                                                                                                                                                                depart TRAMO3
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                TERMINATE 1
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                
                                                                                                                                                                                                                • + Los Behavior Procedures

                                                                                                                                                                                                                  Las funciones de comportamiento definen tanto el comportamiento hacia el interior de la entidad como hacia el exterior.

                                                                                                                                                                                                                  Imaginemos un tramo de carretera o un taller, una cosa es el tiempo que tarden las entidades en procesarse y otra los eventos que se producen como accidentes o rotura de las máquinas. En general, éstas serán una función de Poisson.

                                                                                                                                                                                                                  Así que lo fácil para interpretar tanto las de entrada como las de salida es sintetizar todas ellas en un único BEHAVIOR PROCEDURE.

                                                                                                                                                                                                                  Vamos a observar un ejemplo de tramos de carretera en los que los vehículos tardarán un determinado tiempo en recorrerlo y eventualmente tendrán que ir a repostar. Usaremos el SAVEVALUE "tramos" para sintetizar los parámetros de cada tramo del 0 al 2. Designaremos un número para el origen, destino, la distancia y la meteorología de cada tramo.

                                                                                                                                                                                                                  Además de definir las BEHAVIOR FUNCTIONS tanto Gaussiana para el tiempo de trayecto dependiendo de la distancia y meteorología, añadimos una Poisson para definir el lambda respecto de la distancia que haga que las entidades entren a repostar. Recordemos que en una distribución de Poisson, un lambda de 1/10 indica que regularmente, uno de cada 10 vehículos repostarán.

                                                                                                                                                                                                                  El BEHAVIOR PROCEDURE se encarga de todo y solo recibe dos parámetros, origen y destino.

                                                                                                                                                                                                                  Obtiene los nombres de los recursos y parámetros basados en esos números. Calcula si es necesario repostar y cuánto tiempo va a tardar en recorrer el tramo. Las estadísticas finales permiten observar el tiempo total de recorrido por tramo (acumulado en los QUEUERs), así como el uso y ocupación de las gasolineras (vía R_BIN_*).

                                                                                                                                                                                                                  Gracias a la flexibilidad de los Behavior Procedures, podemos encapsular tanto la lógica de simulación como las variaciones estadísticas y los eventos adicionales en un único bloque modular.

                                                                                                                                                                                                                  Esto permite replicar fácilmente comportamientos realistas en sistemas más grandes como cadenas logísticas, rutas de transporte o procesos industriales con condiciones variables.


                                                                                                                                                                                                                  Example:
                                                                                                                                                                                                                  ;SYSTEM {TYPE:OPTIONS,Speed:5}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  initial tramos, [
                                                                                                                                                                                                                  	{
                                                                                                                                                                                                                  	origen:1,
                                                                                                                                                                                                                  	destino:2,
                                                                                                                                                                                                                      Km:90,
                                                                                                                                                                                                                      Meteo:0.6
                                                                                                                                                                                                                      },
                                                                                                                                                                                                                  	{
                                                                                                                                                                                                                  	origen:2,
                                                                                                                                                                                                                  	destino:3,
                                                                                                                                                                                                                      Km:100,
                                                                                                                                                                                                                      Meteo:0.8
                                                                                                                                                                                                                      },
                                                                                                                                                                                                                  	{
                                                                                                                                                                                                                  	origen:3,
                                                                                                                                                                                                                  	destino:4,
                                                                                                                                                                                                                      Km:50,
                                                                                                                                                                                                                      Meteo:1.0
                                                                                                                                                                                                                      }
                                                                                                                                                                                                                      ]
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  Graphic {NAME:Text_0,Type:TEXT,X:219,Y:178,Text:"Text_0"}
                                                                                                                                                                                                                  Graphic {NAME:Text_1,Type:TEXT,X:246,Y:454,Text:"Text_1"}
                                                                                                                                                                                                                  Graphic {NAME:Text_2,Type:TEXT,X:429,Y:354,Text:"Text_2"}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  QUEUER {NAME:TRAMO_0,X:66,Y:350,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                  QUEUER {NAME:TRAMO_1,X:276,Y:554,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                  QUEUER {NAME:TRAMO_2,X:664,Y:360,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  Facility {NAME:Gas_0,X:215,Y:125,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}
                                                                                                                                                                                                                  Facility {NAME:Gas_1,X:245,Y:406,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}
                                                                                                                                                                                                                  Facility {NAME:Gas_2,X:434,Y:405,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  POSITION {NAME:POS_0,X:115,Y:92}
                                                                                                                                                                                                                  POSITION {NAME:POS_1,X:146,Y:449}
                                                                                                                                                                                                                  POSITION {NAME:POS_2,X:432,Y:506}
                                                                                                                                                                                                                  POSITION {NAME:POS_3,X:756,Y:33}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  Function {behavior: bTramoTiempo, type:GAUSS, p0:50, p1:0, Name:gName1, a:1, b:40, Sigma1:3.0, Sigma2:3.0}
                                                                                                                                                                                                                  Function {behavior: bTramoTiempo, type:GAUSS, p0:50, p1:1, Name:gName2, a:1, b:60, Sigma1:5.0, Sigma2:5.0}
                                                                                                                                                                                                                  Function {behavior: bTramoTiempo, type:GAUSS, p0:100, p1:0, Name:gName3, a:1, b:80, Sigma1:5.0, Sigma2:5.0}
                                                                                                                                                                                                                  Function {behavior: bTramoTiempo, type:GAUSS, p0:100, p1:1, Name:gName4, a:1, b:120, Sigma1:10.0, Sigma2:10.0}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  Function {behavior: bTramoGas, type:POISSON, p0:100, Name:fPoisson1, LAMBDA:1/8}
                                                                                                                                                                                                                  Function {behavior: bTramoGas, type:POISSON, p0:50, Name:fPoisson2, LAMBDA:1/20}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  START 2000
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  ;*****************************************************
                                                                                                                                                                                                                  PROCEDURE TRAMO_BEHAVIOR
                                                                                                                                                                                                                  assign nQueuer, "TRAMO_P$PARAM_A"
                                                                                                                                                                                                                  assign nText, "Text_P$PARAM_A"
                                                                                                                                                                                                                  assign nGas, "Gas_P$PARAM_A"
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  assign Km,X$(tramos.P$PARAM_A.Km)
                                                                                                                                                                                                                  assign Meteo,X$(tramos.P$PARAM_A.Meteo)
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  assign forceGas,BF$(bTramoGas,P$Km) 
                                                                                                                                                                                                                  if (P$forceGas>=1)
                                                                                                                                                                                                                  	move {name:P$nText,text:"GAS P$Km / P$Meteo"}
                                                                                                                                                                                                                  	ADVANCE 10 {TO:P$nGas}
                                                                                                                                                                                                                      seize P$nGas
                                                                                                                                                                                                                      ADVANCE 30,0
                                                                                                                                                                                                                      release P$nGas
                                                                                                                                                                                                                      ADVANCE 10 {TO:"POS_P$PARAM_A"}
                                                                                                                                                                                                                  	move {name:P$nText,text:" "}
                                                                                                                                                                                                                  endif
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  queue P$nQueuer
                                                                                                                                                                                                                  ADVANCE BF$(bTramoTiempo,P$Km,P$Meteo) {TO:"POS_P$PARAM_B"}
                                                                                                                                                                                                                  depart P$nQueuer
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  ENDPROCEDURE 1
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  ;*****************************************************
                                                                                                                                                                                                                  GENERATE 2,0,0,0 {NAME:GEN1,X:55,Y:41,enumber:0}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  ADVANCE 10,2 {TO:POS_0}
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  CALL TRAMO_BEHAVIOR,0,1
                                                                                                                                                                                                                  CALL TRAMO_BEHAVIOR,1,2
                                                                                                                                                                                                                  CALL TRAMO_BEHAVIOR,2,3
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  TERMINATE 1
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                                  
                                                                                                                                                                                                              • + Agradecimientos

                                                                                                                                                                                                                Agradecimientos

                                                                                                                                                                                                                Este proyecto no habría sido posible sin un sinfin de colaboradores. Mi más sincero agradecimiento a:

                                                                                                                                                                                                                • Geoffrey Gordon (GPSS Clásico): El genio detrás del General Purpose Simulation System. Gracias por sentar las bases de la simulación de eventos discretos y por enseñarnos que el mundo se puede entender a través de bloques, transacciones y lógica pura. Esta evolución se ha realizado con todo respeto y admiración.
                                                                                                                                                                                                                • Math.js: El motor dentro del motor. Sin su capacidad de cálculo, no habría ni solver, ni traductor matemático, ni derivadas, ni SNA... Impresiona ver tanta muestra gratuita de ingenio. Es el alma matemática de todo este proyecto.
                                                                                                                                                                                                                • Bootstrap, CodeMirror, three.js: Por formar la interfaz; aportando la estructura visual y una experiencia intuitiva y legible.
                                                                                                                                                                                                                • A las IA: Por actuar como esos "becarios" de capacidad inagotable y precisión asombrosa. Han sido el copiloto perfecto en el todo el desarrollo, demostrando que la inteligencia artificial es un multiplicador de fuerzas impresionante.
                                                                                                                                                                                                                • Los que nos aguantan: Por estar siempre a nuestro lado cuando nos encerremos delante de la pantalla o les contamos teorías que no entienden como si les interesase.

                                                                                                                                                                                                                Antonio Sánchez y resto del equipo. Enero de 2026

                                                                                                                                                                                                                 
                                                                                                                                                                                                                Utilizamos cookies analíticas para darte la mejor experiencia en nuestra web.
                                                                                                                                                                                                                Asumimos que estás de acuerdo con esto, pero puedes rechazarlas si quieres o informarte de ellas en Política de cookies.