[
    {
        "id": "98",
        "nombre": "The pilot: GPSS-Plus*",
        "texto": "<h3>GPSS-Plus: Motor de Simulaci&oacute;n por Eventos Discretos y Sistemas Continuos<\/h3>\r\n<p data-start=\"329\" data-end=\"616\">GPSS-Plus es un motor de simulaci&oacute;n por eventos discretos y sistemas continuos orientado a modelar y analizar el comportamiento de sistemas reales a lo largo del tiempo. Su prop&oacute;sito no es programar aplicaciones, sino <strong data-start=\"526\" data-end=\"564\">describir c&oacute;mo funciona un sistema<\/strong> y observar su evoluci&oacute;n bajo distintas condiciones.<\/p>\r\n<p data-start=\"618\" data-end=\"842\">En esencia, opera como un laboratorio digital donde fluye un conjunto de entidades (clientes, productos, veh&iacute;culos, se&ntilde;ales o paquetes de datos) a trav&eacute;s de una red de procesos y recursos con capacidades y demoras definidas.<\/p>\r\n<p data-start=\"844\" data-end=\"1239\">El objetivo es transformar una descripci&oacute;n real del sistema en un modelo ejecutable: desde una l&iacute;nea de producci&oacute;n industrial o una cadena log&iacute;stica, hasta el tr&aacute;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&ntilde;ar protocolos de contingencia dentro de un entorno de simulaci&oacute;n generalista.<\/p>\r\n<p data-start=\"844\" data-end=\"1239\">Existen diferentes enfoques para abordar el modelado de sistemas tanto por su interfaz como por su objetivo.<\/p>\r\n<p data-start=\"844\" data-end=\"1239\"><u>Basados en&nbsp;Drag &amp; Drop&nbsp;discretos y\/o continuos:<\/u><\/p>\r\n<ul>\r\n    <li>DES: Se construye conectando bloques gr&aacute;ficos.<\/li>\r\n    <li>Modelado continuo: Sistemas de ecuaciones diferenciales.<\/li>\r\n    <li>H&iacute;bridos gr&aacute;ficos + scripting: Combinan DES con c&oacute;digo externo.<\/li>\r\n<\/ul>\r\n<p data-start=\"844\" data-end=\"1239\"><u>Discretos basados en lenguajes propios o generalistas:<\/u><\/p>\r\n<ul>\r\n    <li>Con DSL propio: &Iacute;ntegramente en un lenguaje textual como GPSS cl&aacute;sico.<\/li>\r\n    <li>Sobre lenguajes generalistas: Mediante librer&iacute;as de simulaci&oacute;n.<\/li>\r\n<\/ul>\r\n<p data-start=\"844\" data-end=\"1239\">Gracias a la herencia de los conceptos clave de GPSS cl&aacute;sico, GPSS-Plus se sit&uacute;a en ambos mundos, utilizando un lenguaje propio para definir el modelo, y se apoya en la potencia de un lenguaje generalista cuando el dominio lo requiere.<\/p>\r\n<p data-start=\"844\" data-end=\"1239\">El objetivo &uacute;ltimo es que todo pueda comprenderse:<\/p>\r\n<pre>\r\nGENERATE 5\r\nADVANCE 10 {From:&quot;Madrid&quot;, To:&quot;Paris&quot;}\r\nTERMINATE<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p data-start=\"765\" data-end=\"1123\">Las principales innovaciones introducidas son:<\/p>\r\n<h3 data-start=\"1200\" data-end=\"1247\">1. La sintaxis: compatibilidad extendida<\/h3>\r\n<p data-start=\"1249\" data-end=\"1421\">El cambio m&aacute;s significativo est&aacute; en el manejo de los par&aacute;metros de los bloques y comandos. En GPSS cl&aacute;sico, los par&aacute;metros A, B, C... ofrec&iacute;an una sintaxis fija y limitada.<\/p>\r\n<p data-start=\"1423\" data-end=\"1439\">Ejemplo cl&aacute;sico:<\/p>\r\n<pre>\r\nADVANCE 30,10 ; Espera entre 30 y 40 tiempos<\/pre>\r\n<p>En GPSS-Plus, se mantiene esa estructura, pero se extiende con un formato JSON-like, que permite especificar m&aacute;s detalles sin romper la simplicidad:<\/p>\r\n<pre>\r\nADVANCE 30,10 {From:&quot;Madrid&quot;, To:&quot;Paris&quot;}<\/pre>\r\n<p>Esto hace posible integrar par&aacute;metros gr&aacute;ficos, de comportamiento, o l&oacute;gicos, sin inventar nuevas variantes sint&aacute;cticas para cada caso.<\/p>\r\n<h3 data-start=\"1844\" data-end=\"1875\">2. Visualizaci&oacute;n gr&aacute;fica<\/h3>\r\n<p data-start=\"1877\" data-end=\"2152\">GPSS-Plus representa visualmente las entidades y los bloques mediante elementos gr&aacute;ficos simples: &ldquo;bolitas&rdquo; que se desplazan por un canvas siguiendo exactamente el flujo del modelo. Esta representaci&oacute;n permite observar el sistema en tiempo real, comprender su din&aacute;mica de forma inmediata y facilitar la detecci&oacute;n de cuellos de botella o comportamientos inesperados durante la ejecuci&oacute;n.<br>\r\n<br>\r\nEsta visualizaci&oacute;n, inexistente en GPSS cl&aacute;sico, introduce una nueva forma de interactuar con el modelo, donde la observaci&oacute;n directa se convierte en una herramienta clave de validaci&oacute;n y comprensi&oacute;n.<\/p>\r\n<h3 data-start=\"2608\" data-end=\"2647\">3. Entidades virtuales (VE)<\/h3>\r\n<p data-start=\"374\" data-end=\"630\">Las <b>entidades virtuales<\/b> son la ayuda para describir lo invisible del modelo. Para quien venga de otros paradigmas, recogen lo que antes se describ&iacute;a fuera en otro lenguaje. Para quien se inicia, simplemente son otra entidad m&aacute;s.<\/p>\r\n<p data-start=\"374\" data-end=\"630\">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: <u>Como entidades<\/u> que se mueven, abstractas, visibles o un simple volumen.<\/p>\r\n<p data-start=\"374\" data-end=\"630\">Las entidades virtuales no se visualizan, pero <strong data-start=\"1236\" data-end=\"1263\">son entidades completas<\/strong> que ejecutan l&oacute;gica interna o eventos del sistema. Nacen para describir ese comportamiento con <code>ON_ENTER<\/code>, un <code>TIMER&nbsp;<\/code>o <code data-start=\"926\" data-end=\"935\">TIMEOUT<\/code>, y viven en el propio modelo.<\/p>\r\n<p data-start=\"1000\" data-end=\"1055\">Seg&uacute;n su ciclo de vida, existen tres tipos principales:<\/p>\r\n<ul data-start=\"1056\" data-end=\"1222\">\r\n    <li data-start=\"1056\" data-end=\"1103\">\r\n    <p data-start=\"1058\" data-end=\"1103\"><strong data-start=\"1058\" data-end=\"1070\">Reactores<\/strong>, que atienden eventos concretos y desaparecen.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1104\" data-end=\"1168\">\r\n    <p data-start=\"1106\" data-end=\"1168\"><strong data-start=\"1106\" data-end=\"1117\">Agentes<\/strong>, que no mueren y gestionan el universo del modelo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1169\" data-end=\"1222\">\r\n    <p data-start=\"1171\" data-end=\"1222\"><strong data-start=\"1171\" data-end=\"1186\">Componentes<\/strong>, entidades vivas que existen como parte de otra entidad y mueren con ella.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1224\" data-end=\"1510\">Con este enfoque, el modelo se convierte en un <strong data-start=\"1271\" data-end=\"1313\">ecosistema de ejecuciones concurrentes<\/strong>. Una entidad puede esperar un autob&uacute;s mientras atiende una llamada; un agente puede controlar el tr&aacute;fico; el sol puede salir y ponerse sin que ninguna entidad principal tenga que ocuparse de ello.<\/p>\r\n<p data-start=\"1224\" data-end=\"1510\">Se observa que muchos conceptos habituales de la Programaci&oacute;n Procedural o la Orientada a Objetos se reinterpretan: el comportamiento no se encapsula en objetos jer&aacute;rquicos, sino que se modela expl&iacute;citamente como entidades que coexisten y act&uacute;an en paralelo dentro del sistema.&nbsp;<br>\r\n<br>\r\nNo 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 &ldquo;this.respirar()&rdquo; que siempre respira igual, sino una persona que camina y, al mismo tiempo, respira un aire diferente.<br>\r\n&nbsp;<\/p>\r\n<h3 data-start=\"570\" data-end=\"603\">4. Auditor&iacute;a del modelo y V&amp;V.<\/h3>\r\n<p>Al describirse &iacute;ntegramente como un modelo expl&iacute;cito, GPSS-Plus permite que su comportamiento pueda ser analizado, interpretado y contrastado de forma autom&aacute;tica. Una inteligencia artificial o auditor externo puede reconstruir qu&eacute; hace el sistema, detectar incoherencias internas y contrastar la intenci&oacute;n del modelador con la ejecuci&oacute;n real del modelo.<br>\r\n<br>\r\nEsta capacidad introduce una evoluci&oacute;n natural de los procesos cl&aacute;sicos de verificaci&oacute;n y validaci&oacute;n (V&amp;V), que dejan de basarse &uacute;nicamente en revisi&oacute;n manual y an&aacute;lisis estad&iacute;stico. Se incorpora as&iacute; la coherencia entre historia (H), modelo (M) y ejecuci&oacute;n (E) sentando las bases de un nuevo enfoque de validaci&oacute;n.<br>\r\n&nbsp;<\/p>\r\n<h3 data-start=\"222\" data-end=\"252\">5. Variables estructuradas<\/h3>\r\n<p data-start=\"254\" data-end=\"516\">En GPSS-Plus, las variables (<code data-start=\"283\" data-end=\"294\">SAVEVALUE<\/code>, <code data-start=\"296\" data-end=\"304\">ASSIGN<\/code>) pueden contener <strong data-start=\"334\" data-end=\"372\">n&uacute;meros, strings, arrays u objetos<\/strong>.<br data-start=\"373\" data-end=\"376\">\r\nEsto permite trabajar con estructuras de datos complejas directamente en el modelo, sin recurrir a lenguajes externos ni perder legibilidad. Por ejemplo:<\/p>\r\n<pre>\r\nASSIGN DATOS, {tipo:&quot;Estudiante&quot;, Nombre:&quot;Antonio&quot;, Edad: 22, Calificaciones: [8,5.8,6,8]}<\/pre>\r\n<h3 data-start=\"570\" data-end=\"603\">6. Sistema h&iacute;brido discreto - continuo<\/h3>\r\n<p>El modelado continuo en GPSS-Plus se apoya en una idea simple: dividir el tiempo en peque&ntilde;os fotogramas. Al ejecutar estos pasos de forma sucesiva dentro de la cola de eventos, el comportamiento continuo emerge de manera natural.<\/p>\r\n<p><code data-start=\"137\" data-end=\"145\">INTEGRATE<\/code>, <code>DYNAMIC<\/code>, <code>SOLVE <\/code>son las herramientas b&aacute;sicas que permiten definir estructuras de sistema continuo integrada en el mismo coraz&oacute;n de lo discreto. RK4, matrices Jacobianas y Newton Raphson permiten simular fluidos, voltajes o velocidades con el mismo DSL.&nbsp;<\/p>\r\n<h3>7. Sandbox, gemelos digitales y edge software<\/h3>\r\n<p>Los modelos de simulaci&oacute;n pueden perseguir tres objetivos principales:<br>\r\n&nbsp;<br>\r\n- <b>Sandbox<\/b>, orientado a la simulaci&oacute;n acotada y a la obtenci&oacute;n de resultados, con o sin precarga de datos hist&oacute;ricos.<br>\r\n- <b>Gemelo digital<\/b>, donde el modelo acompa&ntilde;a a un sistema real para su verificaci&oacute;n, control u operaci&oacute;n predictiva.<br>\r\n- <b>Edge software<\/b>, en el que el modelo constituye el propio sistema, ejecut&aacute;ndose de forma permanente como l&oacute;gica operativa.<br>\r\n<br>\r\nEn los dos &uacute;ltimos casos, el modelo debe interactuar con el entorno externo. GPSS-Plus permite este acoplamiento mediante el <b>BRIDGER<\/b>, que act&uacute;a como un canal bidireccional gen&eacute;rico entre el modelo y sistemas f&iacute;sicos reales.<br>\r\n<br>\r\nDesde 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&eacute; o almacenar un dato se expresa mediante <code>BRIDGE_WRITE<\/code>, y sustituir un <code>GENERATE<\/code> que simula la entrada de un paquete por una suscripci&oacute;n a un sensor real de movimiento es pr&aacute;cticamente inmediato. La transici&oacute;n entre sandbox a gemelo digital o edge software resulta as&iacute; transparente.<\/p>\r\n<p>Gracias a <b>la Rehidrataci&oacute;n<\/b>, 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 &uacute;ltimo dato almacenado incluso tras la modificaci&oacute;n del modelo.<\/p>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"436\" data-end=\"457\">Otras extensiones destacadas:<\/h3>\r\n<p data-start=\"459\" data-end=\"593\">Adem&aacute;s de estas innovaciones, GPSS-Plus incorpora m&uacute;ltiples extensiones que ampl&iacute;an su expresividad, modularidad y potencia:<\/p>\r\n<ul data-start=\"595\" data-end=\"1702\">\r\n    <li data-start=\"595\" data-end=\"774\">\r\n    <p data-start=\"597\" data-end=\"774\"><strong data-start=\"597\" data-end=\"616\">Nuevos recursos<\/strong>:&nbsp;bloques nuevos como <code>STOCK<\/code>, <code>RESTROOM,<\/code>&nbsp;<code>CONDITIONS<\/code>, m&aacute;quinas de estados finitos que ampl&iacute;an la paleta cl&aacute;sica (<code>STORAGE<\/code>, <code>FACILITY<\/code>...).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<ul data-start=\"595\" data-end=\"1702\">\r\n    <li data-start=\"1091\" data-end=\"1244\">\r\n    <p><strong data-start=\"597\" data-end=\"616\">Creaci&oacute;n din&aacute;mica de recursos<\/strong>: Lo que en motores&nbsp;Drag&amp;Drop exigir&iacute;a arrastrar cientos de bloques manualmente, en GPSS-Plus se logra con un simple bucle <code>FOREACH <\/code>y un <code>NEWFACILITY<\/code>. Esta es la diferencia clave entre un lenguaje textual y las interfaces gr&aacute;ficas cl&aacute;sicas.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<ul data-start=\"595\" data-end=\"1702\">\r\n    <li data-start=\"595\" data-end=\"774\"><strong data-start=\"1093\" data-end=\"1114\">Estad&iacute;sticas<\/strong>:&nbsp;permite recolectar estad&iacute;sticas autom&aacute;ticas sobre cualquier recurso o secuencia, y visualizarlas gr&aacute;ficamente.<\/li>\r\n<\/ul>\r\n<ul data-start=\"595\" data-end=\"1702\">\r\n    <li data-start=\"1091\" data-end=\"1244\">\r\n    <p><strong data-start=\"1093\" data-end=\"1114\">Behavior functions<\/strong>:&nbsp;a partir de datos reales o simulados, crea interpolaciones autom&aacute;ticas que encapsulan comportamientos complejos (por ejemplo, distintos tramos de una carretera o m&aacute;quina).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1091\" data-end=\"1244\">\r\n    <p><strong data-start=\"1093\" data-end=\"1114\">Funciones nativas<\/strong>: herramientas como <code data-start=\"1134\" data-end=\"1142\">CONCAT<\/code>, <code data-start=\"1144\" data-end=\"1151\">MERGE<\/code>&nbsp;, <code>PUSH&nbsp;<\/code>extienden el lenguaje sin depender de c&oacute;digo externo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1091\" data-end=\"1244\">\r\n    <p><strong data-start=\"1093\" data-end=\"1114\">Gr&aacute;ficas autom&aacute;ticas<\/strong>:&nbsp;basta con tabular datos para generar gr&aacute;ficos dentro del entorno de simulaci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1245\" data-end=\"1388\">\r\n    <p data-start=\"1247\" data-end=\"1388\"><strong data-start=\"1247\" data-end=\"1268\">Contextos (<code data-start=\"1260\" data-end=\"1265\">CX$<\/code>)<\/strong>: cada modelo puede definir contextos independientes, ideales para crear librer&iacute;as, m&oacute;dulos o componentes reutilizables.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1389\" data-end=\"1545\">\r\n    <p data-start=\"1391\" data-end=\"1545\"><strong data-start=\"1391\" data-end=\"1411\">Depuraci&oacute;n total<\/strong>: el sistema permite el seguimiento detallado de la cola de eventos, de cada entidad, recurso o variable, facilitando el an&aacute;lisis y la correcci&oacute;n de modelos.<\/p>\r\n    <p data-start=\"1391\" data-end=\"1545\">&nbsp;<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
        "descripcion": null,
        "parametros": null,
        "parametros_json": "",
        "ejemplo": null,
        "hijos": []
    },
    {
        "id": "132",
        "nombre": "Season 1: Hola mundo",
        "texto": "<p>Para entender GPSS &oacute; GPSS-Plus y cualquier motor de simulaci&oacute;n, primero debemos entender qu&eacute; significa un sistema de eventos discretos (DES).<\/p>\r\n<p>Un Sistema de Eventos Discretos (DES) modela procesos complejos bas&aacute;ndose en la idea de que la actividad del sistema es una secuencia de eventos discretos.<\/p>\r\n<p><br>\r\n&iquest;Y qu&eacute; es un evento discreto? Un <b>evento discreto<\/b> es una acci&oacute;n o cambio que ocurre en un momento determinado y finaliza en otro:<\/p>\r\n<ul>\r\n    <li>Evento 1: Coger un coche<\/li>\r\n    <li>Evento 2: Conducir 2 horas<\/li>\r\n    <li>Evento 3: Aparcar.<\/li>\r\n<\/ul>\r\n<p>El sistema completo se representa como la combinaci&oacute;n de estos eventos puntuales que, al ejecutarse en el tiempo, simulan un proceso de la vida real.<\/p>\r\n<p><b>Motores de simulaci&oacute;n<\/b> como <b>GPSS<\/b> y <b>GPSS-Plus<\/b> son las herramientas clave para este an&aacute;lisis. Su funci&oacute;n es modelar y analizar c&oacute;mo los <b>recursos<\/b> (m&aacute;quinas, estaciones de trabajo, ventanillas, o veh&iacute;culos) son gestionados en conjunci&oacute;n con las <b>entidades<\/b> (clientes, productos) y sus respectivas <b>colas de espera<\/b>.<\/p>\r\n<p>Este enfoque genera informes y estad&iacute;sticas que, entre muchas:<\/p>\r\n<ol start=\"1\">\r\n    <li>\r\n    <p><b>Identifican cuellos de botella<\/b> y minimizan tiempos de espera.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p><b>Optimizan<\/b> el uso de los recursos de manera eficiente.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p><b>Estudian y protocolizan<\/b> acciones y contingencias ante cualquier eventualidad o fallo en el sistema.<\/p>\r\n    <\/li>\r\n<\/ol>",
        "descripcion": null,
        "parametros": null,
        "parametros_json": "",
        "ejemplo": null,
        "hijos": [
            {
                "id": "133",
                "nombre": "Un ejemplo simple",
                "texto": "<p>Simularemos un sistema b&aacute;sico donde las entidades (clientes) llegan a un recurso (ventanilla), esperan si est&aacute; ocupada, son atendidas y luego se retiran. En este modelo:<\/p>\r\n<ul>\r\n    <li><strong>Llegadas:<\/strong> Los clientes llegan cada 60 a 70 tiempos.<\/li>\r\n    <li><strong>Atenci&oacute;n:<\/strong> El tiempo de servicio en la ventanilla es de 60 a 90&nbsp;tiempos.<\/li>\r\n    <li><strong>Colas:<\/strong> Si la ventanilla est&aacute; ocupada, los clientes esperan.<\/li>\r\n    <li><strong>Salida:<\/strong> Los clientes tardan 10&nbsp;tiempos&nbsp;en colocarse en la cola y otros 10 en retirarse despu&eacute;s del servicio.<\/li>\r\n<\/ul>\r\n<p>Para los que conozcan&nbsp;<b>GPSS cl&aacute;sico<\/b> tendr&iacute;amos:<\/p>\r\n<pre>\r\nGENERATE 60,10   ; Entidades generadas cada 60 a 70 unidades de tiempo.  \r\nADVANCE 10       ; Tarda 10 tiempos en colocarse en la cola de la ventanilla.  \r\nSEIZE VENTANILLA ; La entidad llega a la cola de la ventanilla.  \r\nADVANCE 60,30    ; Tiempo de atenci&oacute;n: 60 a 90 tiempos.  \r\nRELEASE VENTANILLA ; Abandona la ventanilla.  \r\nADVANCE 10       ; Tarda 10 segundos en irse.  \r\nTERMINATE 1      ; La entidad finaliza. \r\nSTART 100        ; Ejecuta esta acci&oacute;n hasta que 100 entidades hayan sido atendidas.<\/pre>\r\n<p>En <b>GPSS-Plus<\/b>, es muy similar, pero con representaci&oacute;n gr&aacute;fica. Para ello, configuramos recursos y par&aacute;metros adicionales:<\/p>\r\n<pre>\r\nFACILITY {NAME:VENTANILLA, X:300, Y:300} ; Definimos la ventanilla en el espacio. \r\nPOSITION {NAME:SALIDA, X:500, Y:300}    ; Definimos la salida en el espacio.  \r\n\r\nGENERATE 60,10 {NAME:GEN1, X:100, Y:300} ; Posicionamos el generador de entidades. \r\nADVANCE 10 {TO:VENTANILLA}               ; Avanza hasta la ventanilla. \r\nSEIZE VENTANILLA                        ; Asignaci&oacute;n del recurso &quot;VENTANILLA&quot;. \r\nADVANCE 60,30                           ; Tiempo de atenci&oacute;n: 60 a 90 tiempos. \r\nRELEASE VENTANILLA                      ; Liberaci&oacute;n del recurso. \r\nADVANCE 10 {TO:SALIDA}                  ; Avanza hasta la salida. \r\nTERMINATE 1                             ; La entidad finaliza. \r\nSTART 100                               ; Ejecuta esta acci&oacute;n hasta que 100 entidades hayan sido atendidas.<\/pre>\r\n<p><b>Paso final:<\/b> Pulsa Play y observa el resultado. La primera entidad saldr&aacute; del &quot;Generate&quot; entre los momentos 60 y 70.<\/p>\r\n<p>Como se puede observar, el programa no ha cambiado demasiado para a&ntilde;adir la parte gr&aacute;fica. B&aacute;sicamente, hemos definido la situaci&oacute;n espacial de los elementos.<\/p>",
                "descripcion": "",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "137",
                "nombre": "Sintaxis: Los comandos y los bloques",
                "texto": "<p>En una simulaci&oacute;n, hay dos elementos principales en juego:<\/p>\r\n<p>1. Las <b>ENTIDADES<\/b> o transacciones son los elementos que que cambian su estado con la simulaci&oacute;n, pueden representar personas, cajas o cualquier elemento. En GPSS-Plus se representan con bolitas de colores. Estas entidades interact&uacute;an con el entorno. Por ejemplo, entran en juego a trav&eacute;s del bloque <code><b>GENERATE<\/b><\/code>,&nbsp;avanzan por un circuito con <code data-start=\"716\" data-end=\"725\">ADVANCE<\/code>&nbsp;y mueren en un <code><b>TERMINATE<\/b><\/code>.<\/p>\r\n<p>2. Los <b>RECURSOS<\/b>, que conforman el entorno y son utilizados por las entidades, como una ventanilla de atenci&oacute;n o una caja m&aacute;s grande que la que represente una entidad.<\/p>\r\n<p>Por ejemplo, una persona (entidad) se acerca a una ventanilla (recurso). Esta ventanilla tendr&aacute; un <b>COMANDO<\/b> que la define, y una serie de <b>BLOQUES<\/b> que describen qu&eacute; hace la persona en relaci&oacute;n con la ventanilla.<\/p>\r\n<p>Un ejemplo de definici&oacute;n de un recurso como ventanilla es el COMANDO:<\/p>\r\n<pre>\r\nFACILITY {name:Ventanilla1, x:100, y:100}<\/pre>\r\n<p><b><\/b>Un ejemplo de instrucci&oacute;n para que una entidad ocupe esa ventanilla es el BLOQUE:<\/p>\r\n<pre>\r\nSEIZE Ventanilla1<\/pre>\r\n<p><b><\/b>En resumen, nos basamos en dos elementos principales: <b>bloques<\/b> y <b>comandos<\/b>.<\/p>\r\n<p><b>1. Bloques&nbsp;<\/b><b>ligados a las entidades<\/b><\/p>\r\n<p>Los bloques son instrucciones que las <b>ENTIDADES ejecutan directamente<\/b> durante la simulaci&oacute;n. Cada bloque define una acci&oacute;n espec&iacute;fica que afecta al flujo o estado de esa entidad.<\/p>\r\n<p><b>Sintaxis general:<br>\r\n<\/b><br>\r\nBLOQUE [PAR&Aacute;METROS A,B,C..] {OPCIONES JSON-LIKE}<\/p>\r\n<p>Ejemplos de bloque:<\/p>\r\n<pre>\r\nASSIGN nombreVariable,10\r\nTERMINATE 1<\/pre>\r\n<p>Los par&aacute;metros se denominan por letras (A, B, C, ...). Dependiendo del bloque, tambi&eacute;n pueden incluir par&aacute;metros en formato JSON-like, que usualmente definen aspectos gr&aacute;ficos o avanzados.<br>\r\nEjemplo:<\/p>\r\n<pre>\r\nMOD {COLOR:#FF0000} ; pone de color rojo una entidad.<\/pre>\r\n<p>Otro ejemplo:<\/p>\r\n<pre>\r\nADVANCE 10,5 {TO:Ventanilla1} ; la entidad avanza en tiempo hacia &quot;Ventanilla1&quot;<\/pre>\r\n<p><b>2. Comandos&nbsp;<\/b><b>ligados a los recursos y entorno del motor<\/b><\/p>\r\n<p>Los comandos configuran el entorno de la simulaci&oacute;n. A diferencia de los bloques, no son ejecutados directamente por las entidades. En cambio, definen recursos, posiciones gr&aacute;ficas y reglas del sistema.<\/p>\r\n<p><b>Sintaxis general:<\/b><\/p>\r\n<p>COMANDO {NAME:theName, OPCIONES JSON-LIKE}<\/p>\r\n<p>Ejemplo para definir un almac&eacute;n:<\/p>\r\n<pre>\r\nSTORAGE {NAME:ALMACEN1, CAPACITY:10, X:270, Y:200}\r\n<\/pre>\r\n<p>Este formato permite agregar informaci&oacute;n adicional como la posici&oacute;n (X, Y).<\/p>",
                "descripcion": "",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "213",
                "nombre": "Variables: Savevalues y Assigns",
                "texto": "<p>En GPSS-Plus, las <b>variables<\/b> permiten almacenar informaci&oacute;n que puede ser utilizada por las entidades o para configurar elementos del entorno. Estas variables se dividen principalmente en dos tipos:<\/p>\r\n<p><b>1. SAVEVALUE<\/b> (variables globales):Las variables globales son accesibles por cualquier entidad y se mantienen durante toda la simulaci&oacute;n. Son &uacute;tiles para almacenar datos compartidos, como contadores, acumuladores o estados globales.<\/p>\r\n<p><b>Sintaxis:<\/b><br>\r\n<b>SAVEVALUE variable, valor<\/b><\/p>\r\n<p>Ejemplo:<\/p>\r\n<pre>\r\nSAVEVALUE TiempoEspera, 8\r\n<\/pre>\r\n<p>Este comando establece la variable global &quot;TiempoEspera&quot; con un valor de 8.<\/p>\r\n<p>Una vez definido, el valor puede actualizarse en cualquier momento:<\/p>\r\n<pre>\r\nSAVEVALUE TiempoEspera, 10\r\n<\/pre>\r\n<p>El valor de &quot;TiempoEspera&quot; ahora es 10.<\/p>\r\n<p>Para recuperar el valor de una variable global se utilizan los <b>SNA<\/b>&nbsp;que tienen un formato particular:<br>\r\n<br>\r\n<b>X$nombreSavevalue &oacute;&nbsp;<\/b><b>X$(nombreSavevalue)<\/b><\/p>\r\n<pre>\r\n; X$TiempoEspera &oacute; X$(TiempoEspera)\r\n\r\nIF (X$TiempoEspera&gt;10)\r\n...<\/pre>\r\n<p>Por ejemplo, incrementar el valor de un savevalue se puede hacer mediante:<\/p>\r\n<pre>\r\nSAVEVALUE TiempoEspera, X$TiempoEspera + 1<\/pre>\r\n<p><b><\/b><b>O a trav&eacute;s del m&eacute;todo:<\/b><\/p>\r\n<pre><b> <\/b>SAVEVALUE.inc TiempoEspera<\/pre>\r\n<p>Particularmente, y debido a que estas variables son usadas por m&aacute;s de una entidad, tiene un COMANDO asociado para su creaci&oacute;n e inicializaci&oacute;n llamado&nbsp;<code><b>INITIAL<\/b><\/code><b>.<\/b><\/p>\r\n<p><b><\/b>Su sintaxix es simple:<\/p>\r\n<pre>\r\nINITIAL TiempoEspera,10<\/pre>\r\n<p>Recuerda que <code>INITIAL <\/code>es un comando. Se usa al comienzo del programa para inicializar.<\/p>\r\n<p><b>2. ASSIGN<\/b> (variables locales de la entidad):<br>\r\nLas variables locales pertenecen a una entidad espec&iacute;fica. Cada entidad puede tener su propio valor para una misma variable, lo que las hace &uacute;tiles para gestionar datos exclusivos de cada entidad.<\/p>\r\n<p><b>Sintaxis:<\/b><br>\r\n<b>ASSIGN variable, valor<\/b><\/p>\r\n<p>Ejemplo:<\/p>\r\n<pre>\r\nASSIGN Identificador, 1\r\n<\/pre>\r\n<p>Este bloque asigna el valor 1 a la variable &quot;Identificador&quot; de la entidad actual que invoca el ASSIGN.<\/p>\r\n<p>Para recuperar el valor de una variable local con su <b>SNA <\/b>de formato:<br>\r\n<br>\r\n<b>P$nombreAssign &oacute;&nbsp;<\/b><b>P$(nombreAssign)<\/b><\/p>\r\n<pre>\r\n; P$Identificador &oacute; P$(Identificador)\r\n<b>if(<\/b>P$Identificador&gt;5<b>) ...<\/b><\/pre>\r\n<p><b>P$<\/b><b>Identificador&nbsp;<\/b>es la forma que permite obtener el valor de una variable local de la entidad.<\/p>\r\n<p><b>Diferencias clave entre SAVEVALUE y ASSIGN:<\/b><\/p>\r\n<ul>\r\n    <li><b>Alcance:<\/b> Las variables SAVEVALUE son globales, mientras que las ASSIGN son locales a la entidad.<\/li>\r\n    <li><b>Persistencia:<\/b> Las SAVEVALUE mantienen su valor en todo momento, incluso si ninguna entidad las usa. Las ASSIGN solo existen mientras la entidad est&aacute; activa.<\/li>\r\n    <li><b>Uso:<\/b> Utiliza SAVEVALUE para datos compartidos, y ASSIGN para datos espec&iacute;ficos de una entidad.<\/li>\r\n<\/ul>\r\n<p><b>Ejemplo combinado:<\/b><\/p>\r\n<p>En este ejemplo, combinamos SAVEVALUE y ASSIGN para calcular el tiempo promedio de espera en una ventanilla. Si el tiempo est&aacute; definido por un ADVANCE 15,10 significa que estar&aacute;n entre 15 y 25 tiempos. Eso deber&iacute;a ir aproximando el promedio a 20.<\/p>\r\n<pre>\r\nINITIAL TiempoTotal, 0      \r\nINITIAL NumEntidades, 0     \r\nFacility {NAME:Ventanilla,X:354,Y:204}  \r\nGraphic {NAME:Text1,Type:TEXT,X:358,Y:321,Text:&quot;Promedio&quot;}  \r\nPosition {NAME:Salida,X:577,Y:201}   \r\nSTART 50   \r\n;---------------------------------\r\n\r\nGenerate 20,0 {NAME:GEN1,X:100,Y:207}   \r\nADVANCE 10 {to:Ventanilla}           \r\nSEIZE Ventanilla     \r\nASSIGN TiempoInicio, AC1$  \r\nADVANCE 15,10       \r\nASSIGN TiempoFinal, AC1$      \r\nRELEASE Ventanilla       \r\nADVANCE 15 {to:Salida} \r\nASSIGN TiempoEntidad, (P$TiempoFinal - P$TiempoInicio)  \r\nSAVEVALUE NumEntidades, X$NumEntidades + 1  \r\nSAVEVALUE TiempoTotal, X$TiempoTotal + P$TiempoEntidad  \r\nSAVEVALUE promedio, round(X$TiempoTotal \/ X$NumEntidades,3)   \r\nMOVE {NAME:Text1,text:&quot;Promedio: X$promedio&quot;}  \r\nTERMINATE 1    <\/pre>\r\n<p>En este c&oacute;digo:<br>\r\n- Cada entidad calcula su tiempo de espera en la ventanilla.<br>\r\n- Los tiempos se acumulan en la variable global &quot;TiempoTotal&quot;.<br>\r\n- El n&uacute;mero de entidades atendidas se cuenta en &quot;NumEntidades&quot;.<br>\r\n- Al final de la simulaci&oacute;n, puedes calcular el tiempo promedio de espera:<br>\r\n<b>TiempoPromedio = X$TiempoTotal \/ X$NumEntidades<\/b><\/p>\r\n<p>De esta manera, <code>SAVEVALUE <\/code>y <code>ASSIGN <\/code>trabajan juntos para ofrecer un manejo eficiente de datos en la simulaci&oacute;n.<\/p>\r\n<p>M&aacute;s adelante veremos como ambos BLOQUES admiten mucho m&aacute;s que variables numerales.<\/p>\r\n<p>&nbsp;<\/p>",
                "descripcion": "",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "134",
                "nombre": "Estructura: IF, CALL, PROCEDURE",
                "texto": "<p>En este ejemplo, simulamos un sistema donde cada entidad elige aleatoriamente una de <strong data-start=\"524\" data-end=\"544\">tres ventanillas<\/strong> para ser atendida. Cada ventanilla tiene su propia cola, y el flujo se distribuye de forma desigual en funci&oacute;n del tiempo de servicio.<\/p>\r\n<p>La l&oacute;gica de decisi&oacute;n se resuelve exclusivamente mediante procedimientos estructurados.<\/p>\r\n<pre>\r\nFACILITY {NAME:VENTANILLA1,X:320,Y:450}\r\nFACILITY {NAME:VENTANILLA2,X:320,Y:300}\r\nFACILITY {NAME:VENTANILLA3,X:320,Y:150}\r\n\r\nPOSITION {NAME:POS1,X:160,Y:300}\r\nPOSITION {NAME:POS2,X:497,Y:300}\r\n\r\nSTART 30 ; Se ejecutar&aacute; hasta que se completen 30 entidades\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:60,Y:300}\r\nADVANCE 20,0 {TO:POS1}\r\nASSIGN ALEATORIO,RANDOM\r\nIF (P$ALEATORIO&lt;0.3)\r\n    CALL CAMINO1\r\n    ELSE \r\n       IF (P$ALEATORIO&lt;0.6)\r\n          CALL CAMINO2\r\n          ELSE\r\n          CALL CAMINO3\r\n       ENDIF\r\nENDIF\r\nADVANCE 20 {TO:POS2}\r\nTERMINATE 1\r\n\r\n;---------------------------------------------\r\n\r\nPROCEDURE CAMINO1\r\nADVANCE 20  {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE 25,10\r\nRELEASE VENTANILLA1\r\nENDPROCEDURE \r\n\r\nPROCEDURE CAMINO2\r\nADVANCE 20 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE 10,2\r\nRELEASE VENTANILLA2\r\nENDPROCEDURE \r\n\r\nPROCEDURE CAMINO3\r\nADVANCE 20 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE 10,2\r\nRELEASE VENTANILLA3\r\nENDPROCEDURE <\/pre>\r\n<p>Con respecto al anterior s&oacute;lo tenemos unos pocos BLOQUES nuevos:<\/p>\r\n<pre>\r\nASSIGN ALEATORIO,RANDOM\r\n<\/pre>\r\n<p>Este bloque crea una variable local en la entidad (llamada <code data-start=\"1273\" data-end=\"1284\">ALEATORIO<\/code>) y le asigna un n&uacute;mero aleatorio entre 0 y 1, usando la funci&oacute;n <code data-start=\"1349\" data-end=\"1357\">RANDOM<\/code>.<br>\r\n<br>\r\nEstas variables locales tambi&eacute;n se llaman <em data-start=\"1524\" data-end=\"1536\">par&aacute;metros<\/em> y se accede a ellas con la notaci&oacute;n SNA&nbsp;<code data-start=\"1573\" data-end=\"1583\">P$Nombre<\/code><br>\r\n<br>\r\n<code>P$ALEATORIO<\/code><\/p>\r\n<ul>\r\n    <li>&quot;P&quot; porque es un par&aacute;metro,<\/li>\r\n    <li>&quot;$&quot; es el separador estandar<\/li>\r\n    <li>&quot;ALEATORIO&quot; es su nombre<\/li>\r\n<\/ul>\r\n<pre>\r\nIF (P$ALEATORIO&lt;0.3)<\/pre>\r\n<p>Este es un bloque de estructura de control &quot;IF&quot;.<br>\r\n<br>\r\nComo se puede ver, es un &quot;if&quot; igual al de cualquier otro lenguaje. Comparamos una variable con un n&uacute;mero.<\/p>\r\n<p>Y este otro bloque de estructura de control no requiere tampoco muchas presentaciones.<br>\r\nLlama a un &quot;PROCEDURE&quot;.<\/p>\r\n<pre>\r\nCALL CAMINO1\r\n<\/pre>\r\n<p><br>\r\nPor &uacute;ltimo<\/p>\r\n<pre>\r\nPROCEDURE CAMINO1\r\n...\r\nENDPROCEDURE 100 ; producir&iacute;a un assign en la entidad invocadora llamado CAMINO1 de valor 100<\/pre>\r\n<p>Que tampoco requieren mucha presentaci&oacute;n. Encapsulan un conjunto de acciones (<code>PROCEDURE<\/code>).<\/p>\r\n<p><code>ENDPROCEDURE <\/code>Puede tener de un valor en el par&aacute;metro A que ser&aacute; retornado del mismo modo que si hubieramos generado una instrucci&oacute;n <code>ASSIGN<\/code>. Recuperable como P$NombredelProcedure (P.E. P$CAMINO1). En este caso, no se utiliza.<\/p>\r\n<p>&nbsp;<\/p>",
                "descripcion": "<p>En este ejemplo, simulamos un sistema donde cada entidad elige aleatoriamente una de <strong data-start=\"524\" data-end=\"544\">tres ventanillas<\/strong> para ser atendida. Cada ventanilla tiene su propia cola, y el flujo se distribuye de forma desigual en funci&oacute;n del tiempo de servicio.<\/p>\r\n<p>La l&oacute;gica de decisi&oacute;n se resuelve exclusivamente mediante procedimientos estructurados, sin uso de saltos ni etiquetas.<\/p>\r\n<pre>\r\nFACILITY {NAME:VENTANILLA1,X:320,Y:450}\r\nFACILITY {NAME:VENTANILLA2,X:320,Y:300}\r\nFACILITY {NAME:VENTANILLA3,X:320,Y:150}\r\n\r\nPOSITION {NAME:POS1,X:160,Y:300}\r\nPOSITION {NAME:POS2,X:497,Y:300}\r\n\r\nSTART 30 ; Se ejecutar&aacute; hasta que se completen 30 entidades\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:60,Y:300}\r\nADVANCE 20,0 {TO:POS1}\r\nASSIGN ALEATORIO,RANDOM\r\nIF (P$ALEATORIO&lt;0.3)\r\n    CALL CAMINO1\r\n    ELSE \r\n       IF (P$ALEATORIO&lt;0.6)\r\n          CALL CAMINO2\r\n          ELSE\r\n          CALL CAMINO3\r\n       ENDIF\r\nENDIF\r\nADVANCE 20 {TO:POS2}\r\nTERMINATE 1\r\n\r\n;---------------------------------------------\r\n\r\nPROCEDURE CAMINO1\r\nADVANCE 20  {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE 25,10\r\nRELEASE VENTANILLA1\r\nENDPROCEDURE \r\n\r\nPROCEDURE CAMINO2\r\nADVANCE 20 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE 10,2\r\nRELEASE VENTANILLA2\r\nENDPROCEDURE \r\n\r\nPROCEDURE CAMINO3\r\nADVANCE 20 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE 10,2\r\nRELEASE VENTANILLA3\r\nENDPROCEDURE <\/pre>\r\n<p>Con respecto al anterior s&oacute;lo tenemos unos pocos BLOQUES nuevos:<\/p>\r\n<pre>\r\nASSIGN ALEATORIO,RANDOM\r\n<\/pre>\r\n<p>Este bloque almacena <b>en una memoria propia de cada entidad llamamos en este caso<\/b>&nbsp;&quot;ALEATORIO&quot; el valor calculado por &quot;RANDOM&quot;, que es un n&uacute;mero aleatorio entre 0 y 1.<br>\r\nA estos &quot;Assigns&quot; se les denomina &quot;par&aacute;metros&quot; y son accesibles de mediante la notaci&oacute;n <b>SNA<\/b>:<br>\r\n<br>\r\n<code>P$ALEATORIO<\/code><\/p>\r\n<ul>\r\n    <li>&quot;P&quot; porque es un par&aacute;metro,<\/li>\r\n    <li>&quot;$&quot; es el separador estandar<\/li>\r\n    <li>&quot;ALEATORIO&quot; es su nombre<\/li>\r\n<\/ul>\r\n<pre>\r\nIF (P$ALEATORIO&lt;0.3)<\/pre>\r\n<p>Este es un bloque de estructura de control &quot;IF&quot;.<br>\r\n<br>\r\nComo se puede ver, es un &quot;if&quot; igual al de cualquier otro lenguaje. Comparamos una variable con un n&uacute;mero.<\/p>\r\n<pre>\r\nCALL CAMINO1\r\n<\/pre>\r\n<p>Y este otro bloque de estructura de control no requiere tampoco muchas presentaciones.<br>\r\nLlama a un &quot;PROCEDURE&quot;.<br>\r\n<br>\r\nPor &uacute;ltimo<\/p>\r\n<pre>\r\nPROCEDURE CAMINO1\r\n...\r\nENDPROCEDURE 100 ; producir&iacute;a un assign en la entidad invocadora llamado CAMINO1 de valor 100<\/pre>\r\n<p>Que tampoco requieren mucha presentaci&oacute;n. Encapsulan un conjunto de acciones (<code>PROCEDURE<\/code>).<\/p>\r\n<p><code>ENDPROCEDURE <\/code>Puede teter de un valor en el par&aacute;metro A que ser&aacute; retornado del mismo modo que si hubieramos generado una instrucci&oacute;n <code>ASSIGN<\/code>. Recuperable como P$NombredelProcedure (P.E. P$CAMINO1). En este caso, no se utiliza.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "248",
                "nombre": "Recursos: Storage",
                "texto": "\/* Almacenes: STORAGE\r\nGestión de capacidad y uso de un almacén.\r\n*\/\r\n\r\nINITIAL capacidad,40 ; define un savevalue llamado capacidad de valor 40\r\n\r\nSTORAGE {NAME:almacen1, X:300, Y:200, capacity:X$capacidad}\r\n\r\nPOSITION {NAME:SALIDA,  X:500, Y:200}\r\n\r\n; Texto que muestra el contenido actual\r\nGRAPHIC {NAME:texto1, Type:TEXT, X:320, Y:240, Text:\"Cajas actuales: 0\"}\r\n\r\nSTART 100\r\n\r\n; Camiones llegando cada 10 unidades de tiempo\r\nGENERATE 10,0 {NAME:Camion, X:100, Y:200}\r\n\r\nADVANCE 15 {TO:almacen1}\r\nENTER almacen1,(random*15)+1 ; Ocupa entre 1 y 16 unidades del storage\r\nsavevalue uso,X$capacidad - R$(almacen1,LEFT)\r\nMOVE {name:texto1,text:\"Cajas actuales X$uso : R$(almacen1,OCCUPIED)\"}\r\nADVANCE 40,10\r\nLEAVE almacen1 ; Libera las unidades ocupadas\r\nsavevalue uso,X$capacidad - R$(almacen1,LEFT)\r\nMOVE {name:texto1,text:\"Cajas actuales X$uso : R$(almacen1,OCCUPIED)\"}\r\nADVANCE 15 {TO:SALIDA}\r\nTERMINATE 1\r\n",
                "descripcion": "<p>Uno de los recursos  cl&aacute;sicos es el <code>STORAGE<\/code>.<\/p>\r\n<p><code>FACILITY<\/code> representa cosas como una ventanilla, una m&aacute;quina o un operario: solo puede atender a una entidad a la vez y seg&uacute;n su capacidad (con CAPACITY:3 la FACILITY atender&iacute;a 3 entidades). STORAGE, en cambio, representa algo como un almac&eacute;n, un tanque, o una red que puede usarse parcialmente.<\/p>\r\n<p>&iquest;Qu&eacute; es un <code>STORAGE<\/code>? Un <code>STORAGE <\/code>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&oacute;n puede dejar 10 cajas en un almac&eacute;n que admite hasta 40.<\/p>\r\n<p>&iquest;C&oacute;mo funciona? Se define con una capacidad total, por ejemplo 40 unidades.<\/p>\r\n<pre>\r\nSTORAGE {NAME:almacen1, X:300, Y:200, capacity:40}<\/pre>\r\n<p>Las entidades usan el bloque <code>ENTER <\/code>para ocupar cierta cantidad, y <code>LEAVE <\/code>para devolverla.<\/p>\r\n<pre>\r\nENTER almacen1,5\r\nLEAVE almacen1 <\/pre>\r\n<p>Si una entidad quiere ocupar m&aacute;s de lo que hay disponible, esperar&aacute; autom&aacute;ticamente en una cola interna&nbsp; hasta que el <code>STORAGE<\/code> tenga suficiente capacidad libre. No necesitas programar nada adicional para gestionar esa espera.<br>\r\n<br>\r\nEn el ejemplo: Almac&eacute;n de cajas definido (<code>STORAGE<\/code>) con 40 unidades de capacidad. Los camiones llegan cada 10 unidades de tiempo y depositan entre 1 y 16 cajas. Despu&eacute;s de un tiempo, se retiran liberando el espacio ocupado.  Ver&aacute;s en el texto de pantalla cu&aacute;ntas cajas hay almacenadas en cada momento.<br>\r\n<br>\r\nAdem&aacute;s de realizarse el c&aacute;lculo a trav&eacute;s de sumas y restas, se puede hacer directamente consultando el SNA correspondiente a la ocupaci&oacute;n del <code>STORAGE<\/code>:<\/p>\r\n<pre>\r\nR$(almacen1,OCCUPIED)  ; Unidades actualmente ocupadas\r\nR$(almacen1,LEFT)      ; Unidades disponibles<\/pre>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "307",
                "nombre": "La cola de eventos: el motor de la simulación",
                "texto": "<p><b>La cola de eventos:<\/b><\/p>\r\n<p data-start=\"178\" data-end=\"457\">En el coraz&oacute;n de cualquier simulaci&oacute;n de eventos discretos se encuentra la <strong data-start=\"240\" data-end=\"259\">cola de eventos<\/strong>, una estructura que define <strong data-start=\"287\" data-end=\"304\">c&oacute;mo y cu&aacute;ndo<\/strong> se ejecutan las acciones de las entidades en el modelo. Este enfoque de <strong data-start=\"377\" data-end=\"398\">eventos discretos<\/strong> es lo que lo distingue de otros lenguajes de programaci&oacute;n.<\/p>\r\n<p data-start=\"459\" data-end=\"716\">Hay que pensar que es como si cada entidad tuviese su propia programaci&oacute;n, como se har&iacute;a en C o Python, pero <strong data-start=\"568\" data-end=\"620\">saltando entre entidades seg&uacute;n el orden temporal<\/strong> que marca esta cola. Si simulamos 10 entidades, hay 10 programas corriendo &ldquo;a la vez&rdquo; (o casi).<\/p>\r\n<p><strong>&iquest;Qu&eacute; es la cola de eventos?<\/strong><\/p>\r\n<p data-start=\"756\" data-end=\"974\">Es una <strong data-start=\"763\" data-end=\"805\">lista ordenada de acciones programadas<\/strong>, cada una asociada a un instante de tiempo simulado (AC1). La cola garantiza que las acciones se ejecuten en el orden correcto <strong data-start=\"933\" data-end=\"973\">seg&uacute;n el tiempo en que deben ocurrir<\/strong>.<\/p>\r\n<p data-start=\"976\" data-end=\"1037\">Lo m&aacute;s importante: <strong data-start=\"995\" data-end=\"1023\">est&aacute; ordenada por tiempo<\/strong>. Por ejemplo:<\/p>\r\n<pre>\r\nTiempo 15 - Atender a la entidad 14 en el paso 88\r\nTiempo 45 - Atender a la entidad 22 en el paso 16\r\nTiempo 77 - Atender a la entidad 16 en el paso 25<\/pre>\r\n<p><strong>Ejemplo: Evoluci&oacute;n de la cola de eventos<\/strong><\/p>\r\n<p data-start=\"1253\" data-end=\"1464\">Supongamos el caso cl&aacute;sico de un banco donde los clientes entran por la puerta y son atendidos en una ventanilla. Las entidades son generadas con <code data-start=\"1399\" data-end=\"1409\">GENERATE<\/code> y pasan un tiempo siendo atendidas mediante <code data-start=\"1454\" data-end=\"1463\">ADVANCE<\/code>.<\/p>\r\n<p data-start=\"1466\" data-end=\"1535\">Imaginemos que estamos en el instante <code data-start=\"1504\" data-end=\"1511\">T = 5<\/code>, con la siguiente cola:<\/p>\r\n<pre>\r\nT 5: Entidad 5: Esperando en la cola de la ventanilla\r\nT 7: Generate: Generar elemento, cliente que va a entrar por la puerta\r\nT 8: Entidad 9: Dirigi&eacute;ndose a la cola de la ventanilla\r\nT 16: Entidad 3: Dirigi&eacute;ndose a la cola de la ventanilla\r\n<\/pre>\r\n<p>Ahora mismo, tenemos una entidad, la 5, que es la siguiente en la cola. Extraeremos ese elemento de la cola para procesarlo. &iexcl;Ahora sabemos que el tiempo es 5! No porque el tiempo haya pasado, sino porque nuestra siguiente tarea est&aacute; programada para T=5.<br>\r\n<br>\r\nVamos a pensar que, por ejemplo, la Entidad 5 se encuentra en el &quot;paso&quot;  (posici&oacute;n de su programaci&oacute;n) que indica un ADVANCE 10 para ser  atendida.<br>\r\nAnte esto, la soluci&oacute;n es colocarse en la cola en T=15 (5+10) y cesar en su ejecuci&oacute;n mientras es atendida.<br>\r\nAhora la cola de eventos tendr&aacute; esta situaci&oacute;n:<\/p>\r\n<pre>\r\nT 7: Generate: Generar elemento\r\nT 8: Entidad 9: Dirigiendose a la cola de la ventanilla\r\nT 15: Entidad 5: Ir a la salida\r\nT 16: Entidad 3: Dirigiendose a la cola de la ventanilla\r\n<\/pre>\r\n<p>Ya estamos en el evento siguiente. Toca actualizar el tiempo del sistema  de nuevo: &quot;AC1&quot; que ahora toma el valor 7. Como dec&iacute;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.<br>\r\n<br>\r\nExtraemos el elemento de la cola y nos encontramos con un GENERATE 20,5.  Por lo tanto, debemos calcular qu&eacute; tiempo ser&aacute; ese definido por 20,5;  algo entre 20 y 25. Supongamos el resultado de 22. Es decir, entrar&aacute;  otra persona dentro de 22 tiempos. La cola pasa ahora a tener este otro  aspecto despu&eacute;s de moverse el GENERATE <b>y realizar 2 tareas<\/b>:<br>\r\n<br>\r\n1.- En propio GENERATE avanza 20 (par&aacute;metro A) (7+20=27).<br>\r\n2.- Crear una nueva entidad (la n&uacute;mero 10) para que pueda llevar a cabo sus tareas en 22 momentos despu&eacute;s (7+22=29).<\/p>\r\n<pre>\r\nT 8: Entidad 9: Dirigiendose a la cola de la ventanilla\r\nT 15: Entidad 5: Ir a la salida\r\nT 16: Entidad 3: Dirigiendose a la cola de la ventanilla\r\nT 27: Generate: Generar elemento\r\nT 29: Entidad 10: Dirigiendose a la cola de la ventanilla<\/pre>\r\n<p>Ahora ser&iacute;a el momento de volver a mover el tiempo del sistema y atender el siguiente elemento: la Entidad 9.&nbsp;<\/p>\r\n<p>As&iacute; seguir&aacute; viva la cola de eventos hasta que se cumpla alguna de las opciones que finalicen el programa.<br>\r\nEsta cola de eventos en GPSS-Plus tiene, adem&aacute;s, otras caracter&iacute;sticas  para poder realizar las tareas en el mundo gr&aacute;fico y a&ntilde;adir los eventos  tipo &quot;ON_*&quot;.<br>\r\n<br>\r\n<b>En esta cola GPSS-Plus, tenemos 5 tipos de elementos:<\/b><\/p>\r\n<ul>\r\n    <li>GENERATE &ndash; Introducen nuevas entidades en el sistema.<\/li>\r\n    <li>Entidades &ndash; Las que se mueven por el modelo y consumen recursos.<\/li>\r\n    <li>Tiempos muertos &ndash; Pausas visibles que permiten simular paso a paso o ajustar la velocidad.<\/li>\r\n    <li>Entidades virtuales &ndash; No visibles, ejecutan eventos como ON_ENTER, ON_TIMER, etc.<\/li>\r\n    <li>TIMER del sistema &ndash; Como un generate, pero genera entidades virtuales a intervalos fijos.<\/li>\r\n<\/ul>\r\n<p><b><\/b><\/p>\r\n<p><b>Las colas de los recursos:<\/b><\/p>\r\n<p>Otras colas existentes son las de los recursos que cada uno de ellos puede tener una o dos seg&uacute;n el caso.<\/p>\r\n<p>Estas listas est&aacute;n ordenadas por orden de llegada y su gesti&oacute;n depende exclusivamente del propio recurso.<\/p>\r\n<p>Por ejemplo, una <code>facility <\/code>tiene dos listas, la de entidades ocupantes y la de entidades en cola.<\/p>\r\n<p>Cuando una entidad intenta hacer <code>SEIZE<\/code>, si el recurso est&aacute; ocupado, pasar&aacute; a la lista de espera. Si no hay entidades en el recurso, lo ocupar&aacute; ingresando en la lista de ocupantes. <br>\r\n<br>\r\nCuando realize <code>RELEASE<\/code>, abandonar&aacute; esta lista y forzar&aacute; al recurso a buscar alguna entidad en su cola pendiente de entrar. Si hay alguna, la incorporar&aacute; a la lista de ocupantes y planificar&aacute; su entrada en la cola de eventos en ese mismo momento, por lo que esa entidad tomar&aacute; el control del motor en cuanto la saliente cese su actividad.<\/p>\r\n<p><strong><\/strong><\/p>\r\n<p><strong>Conclusi&oacute;n<\/strong><\/p>\r\n<p>La cola de eventos no solo define el orden y el tiempo en que ocurren  las acciones, sino que tambi&eacute;n act&uacute;a como el n&uacute;cleo del sistema.  Garantiza que los bloques se ejecuten correctamente y que la simulaci&oacute;n  sea consistente y precisa. Comprender este concepto es esencial para  dise&ntilde;ar modelos eficientes y aprovechar al m&aacute;ximo las capacidades de  GPSS-Plus.<\/p>\r\n<p>En GPSS-Plus es sencillo ver esta cola, s&oacute;lo debes activar la ventana de men&uacute; y pulsar sobre el bot&oacute;n &quot;Event Queue&quot;.&nbsp;<\/p>",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "174",
                "nombre": "Conditions: Esperar una condición.",
                "texto": "\/* Sincronización: CONDITIONS \/ WAITUNTIL\r\nLas entidades esperan en grupo y avanzan juntas.\r\n*\/\r\n\r\nPOSITION {NAME:POS1,X:208,Y:222}\r\nPOSITION {NAME:POS2,X:452,Y:215}\r\nPOSITION {NAME:POS3,X:752,Y:215} \r\n\r\nCONDITIONS {NAME:Conditions1,X:208,Y:317,expression:(X$contador>=5)} ; Recurso retenedor\r\nINITIAL contador,0\r\n\r\nSTART 100 \r\n;*****************************************************\r\n\r\nGENERATE 10,2 {NAME:GEN1,X:86,Y:228,ERADIO:10,ECOLOR:#FF9900} \r\n\r\nADVANCE 16,0 {TO:POS1} \r\nsavevalue contador,X$contador + 1\r\nWAITUNTIL Conditions1\r\nWAITCHECK Conditions1\r\nsavevalue contador,0\r\nADVANCE 20,0 {TO:POS2} ; Avanzan hacia la segunda posición TODOS JUNTOS\r\nADVANCE 20,40 {TO:POS3} ; Avanzan hacia la tercera posición con un tiempo aleatorio entre 20 y 40 pero esta vez SEPARÁNDOSE\r\n\r\nENDGENERATE 1 ; Finaliza la vida de la entidad ES IDENTICO A TERMINATE 1\r\n",
                "descripcion": "<p data-start=\"226\" data-end=\"491\">Imagina un grupo de personas esperando a que se abra una puerta autom&aacute;tica. No est&aacute;n haciendo cola ni ocupando nada, simplemente est&aacute;n esperando que ocurra algo.<br>\r\n<br data-start=\"1450\" data-end=\"1453\">\r\nEso es lo que permite el recurso <code data-start=\"1486\" data-end=\"1495\">CONDITIONS<\/code>: detener entidades hasta que una condici&oacute;n l&oacute;gica se cumpla.<\/p>\r\n<p data-start=\"493\" data-end=\"657\">Algo parecido exist&iacute;a en GPSS cl&aacute;sico con el bloque <code data-start=\"527\" data-end=\"537\">ASSEMBLE<\/code> para este prop&oacute;sito. En GPSS-Plus, este comportamiento se sustituye por este recurso m&aacute;s general y flexible.<\/p>\r\n<p>El recurso&nbsp;<code data-start=\"1486\" data-end=\"1495\">CONDITIONS&nbsp;<\/code>permite <strong data-start=\"718\" data-end=\"739\">retener entidades<\/strong> hasta que se cumpla una condici&oacute;n. Esta condici&oacute;n se define como una expresi&oacute;n evaluada cada vez que una entidad intenta pasar.<\/p>\r\n<pre>\r\nCONDITIONS {NAME:Conditions1,X:208,Y:317,expression:(X$contador&gt;=5)}<\/pre>\r\n<p><code data-start=\"1486\" data-end=\"1495\">CONDITIONS<\/code>tiene m&aacute;s opciones que se ver&aacute;n m&aacute;s adelante.<br>\r\n<br>\r\nSu bloque asociado&nbsp;<code>WAITUNTIL <\/code>se encarga de validar para esta entidad la condici&oacute;n, en este caso, si el contador es mayor o igual a 5.<\/p>\r\n<p>Llegada la 5&ordf; entidad, la&nbsp;condici&oacute;n&nbsp;se cumplir&aacute; y seguir&aacute; adelante sin ser retenida. Ahora solo falta liberar al resto chequeando a todas las retenidas con otro bloque asociado:&nbsp;<code>WAITCHECK<\/code>.<\/p>\r\n<p>Resulta interesante observar que salen todas juntas del&nbsp;<code data-start=\"1486\" data-end=\"1495\">CONDITIONS&nbsp;<\/code>en el primer tramo. En el segundo, la aleatoriedad del ADVANCE 20,40 hace que se separen.<\/p>\r\n<p>Tambi&eacute;n empezamos a utilizar el BLOQUE <code>ENDGENERATE <\/code>m&aacute;s acorde con el m&eacute;todo estructurado que <code>TERMINATE<\/code>. Son equivalentes pero sint&aacute;cticamente es m&aacute;s adecuado estructurar el bloque con <code>GENERATE<\/code>\/<code>ENDGENERATE<\/code>.&nbsp;Permite identificar claramente qu&eacute; bloque fue generado, especialmente si hay m&uacute;ltiples <code data-start=\"2365\" data-end=\"2375\">GENERATE<\/code> en el programa.<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "216",
                "nombre": "Segundo paso de estructura",
                "texto": "\/* Bloques: SWITCH y LOCK\/UNLOCK\r\nEnrutamiento condicional y gestión de recursos bloqueables.\r\n*\/\r\n\r\nFACILITY {NAME:VENTANILLA1,X:320,Y:450,capacity:3}\r\nFACILITY {NAME:VENTANILLA2,X:320,Y:300,capacity:3}\r\nFACILITY {NAME:VENTANILLA3,X:320,Y:150}\r\nFACILITY {NAME:VENTANILLA4,X:320,Y:50}\r\n\r\nPOSITION {NAME:POS1,X:158,Y:301}\r\nPOSITION {NAME:POS2,X:497,Y:300}\r\nPOSITION {NAME:POS3,X:627,Y:300,TYPE:TERMINATE,TITLE:END}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:320,Y:104,Text:\"Unlock\"}\r\n\r\n\r\nSTART 30\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:57,Y:300}\r\nADVANCE 20,0 {TO:POS1,flow:1}\r\n\r\nASSIGN ALEATORIO,(RANDOM)\r\n\r\n\r\nSWITCH P$ALEATORIO\r\n    CASE <,0.4\r\n        CALL CAMINO1\r\n    ENDCASE\r\n\r\n    CASE <,0.8\r\n        CALL CAMINO2\r\n    ENDCASE\r\n\r\n    CASE <,0.92\r\n        CALL CAMINO3\r\n        MOVE {NAME:Text1,text:\"Lock\"}\r\n        LOCK VENTANILLA1\r\n        LOCK VENTANILLA2\r\n        LOCK VENTANILLA3\r\n        \r\n    ENDCASE\r\n\r\n    DEFAULT\r\n        CALL CAMINO4\r\n        MOVE {NAME:Text1,text:\"Unlock\"}\r\n        UNLOCK VENTANILLA1\r\n        UNLOCK VENTANILLA2\r\n        UNLOCK VENTANILLA3\r\n    ENDCASE\r\nENDSWITCH\r\n\r\nADVANCE 20,10 {TO:POS2,flow:1,MERGE:\"salida\"}\r\nADVANCE 20,0 {TO:POS3,flow:1}\r\n\r\nENDGENERATE 1\r\n;****************************************\r\n\r\nPROCEDURE CAMINO1\r\n    ADVANCE 20  {TO:VENTANILLA1,flow:1,DECISION:\"inicio\"}\r\n    SEIZE VENTANILLA1\r\n    ADVANCE 45,10\r\n    RELEASE VENTANILLA1\r\nENDPROCEDURE\r\n\r\nPROCEDURE CAMINO2\r\n    ADVANCE 20 {TO:VENTANILLA2,flow:1,DECISION:\"inicio\"}\r\n    SEIZE VENTANILLA2\r\n    ADVANCE 40,10\r\n    RELEASE VENTANILLA2\r\nENDPROCEDURE\r\n\r\nPROCEDURE CAMINO3\r\n    ADVANCE 20 {TO:VENTANILLA3,flow:1,DECISION:\"inicio\"}\r\n    SEIZE VENTANILLA3\r\n    ADVANCE 40,20\r\n    RELEASE VENTANILLA3\r\nENDPROCEDURE\r\n\r\nPROCEDURE CAMINO4\r\n    ADVANCE 20 {TO:VENTANILLA4,flow:1,DECISION:\"inicio\"}\r\n    SEIZE VENTANILLA4\r\n    ADVANCE 10,2\r\n    RELEASE VENTANILLA4\r\nENDPROCEDURE\r\n\r\n\r\n;***************************************************************",
                "descripcion": "<p data-start=\"1025\" data-end=\"1183\">Ahora que ya sabes usar procedimientos y decisiones condicionales simples (<code data-start=\"1100\" data-end=\"1104\">IF<\/code>), vamos a ver un caso donde una entidad elige entre <strong data-start=\"1157\" data-end=\"1182\">varias rutas posibles<\/strong>.<\/p>\r\n<p data-start=\"1185\" data-end=\"1265\">Para eso, usamos la estructura <code data-start=\"1216\" data-end=\"1224\">SWITCH<\/code>, muy parecida a un &ldquo;men&uacute; de decisiones&rdquo;.<\/p>\r\n<p><b>&iquest;Qu&eacute; hace este ejemplo?<\/b><\/p>\r\n<ul>\r\n    <li>Genera una entidad cada 10 unidades de tiempo.<\/li>\r\n    <li>Le asigna un n&uacute;mero aleatorio (<code data-start=\"1381\" data-end=\"1394\">P$ALEATORIO<\/code>).<\/li>\r\n    <li>Seg&uacute;n ese n&uacute;mero, elige una de cuatro rutas (<code data-start=\"1444\" data-end=\"1458\">CALL CAMINO1<\/code>, ..., <code data-start=\"1465\" data-end=\"1479\">CALL CAMINO4<\/code>).<\/li>\r\n    <li>En dos de esas rutas (CAMINO3 y CAMINO4), se activa un <strong data-start=\"1539\" data-end=\"1556\">bloque visual<\/strong> que muestra &quot;Lock&quot; o &quot;Unlock&quot;, y se <strong data-start=\"1593\" data-end=\"1628\">bloquean o desbloquean recursos<\/strong>.&nbsp;Si una entidad entra en el camino 3 se bloquear&aacute; el uso de los recursos 1, 2 y 3 aunque las que est&eacute;n dentro podr&aacute;n finalizar su tarea. Cuando una entidad tome el camino 4, volver&aacute;n a usar los recursos sin dilaci&oacute;n.&nbsp;El bloqueo no expulsa a las entidades que ya est&aacute;n usando los recursos, pero impide que nuevas los tomen hasta que sean desbloqueados.<\/li>\r\n<\/ul>\r\n<p>Adem&aacute;s definimos cierto aspecto puramente visual, &quot;el flow&quot;.<br>\r\nActiv&aacute;ndolo en los <code>ADVANCE<\/code> veremos una l&iacute;nea de ruta que comenzar&aacute; siendo vertical u horizontal seg&uacute;n el <code>LAYOUT<\/code>, podr&aacute; pasar por un cierto <code>VIA <\/code>y <code>VIA2<\/code>, adem&aacute;s de poder converger o divergir seg&uacute;n <code>MERGE <\/code>o <code>DECISION.<\/code><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "306",
                "nombre": "Un modelo completo",
                "texto": "QUEUER {NAME:EN_APARCAMIENTO,X:413,Y:497}\r\n\r\nFACILITY {NAME:UTILITARIOS, capacity:4,X:413,Y:390}\r\nSTORAGE {name: CAMIONES , capacity:5, X:412,Y:120}\r\n\r\nPOSITION {NAME:POS_ENTRADA,X:240,Y:270}\r\nPOSITION {NAME:POS_SALIDA,X:557,Y:316,type:decision,title:\"FIN?\"}\r\nPOSITION {NAME:POS_LOOP,X:225,Y:549}\r\nPOSITION {NAME:FIN,X:730,Y:324,type:terminate}\r\n\r\nSTART 20\r\n\r\n;************************************************************************\r\nGENERATE 20,10,0,10 {NAME:G_UTILITARIOS,X:80,Y:363,ESUBTITLE:\"P$ciclos\/3\"}\r\nassign TIPO_VEHICULO,1\r\nmod {RADIO:5,color:#000066}\r\ncall PROC.MAIN\r\nENDGENERATE 1 ; ALIAS DE TERMINATE\r\n;----------------------------\r\nGENERATE 20,10,0,10 {NAME:G_CAMIONES,X:80,Y:199,ESUBTITLE:\"P$ciclos\/3\"}\r\n\r\nASSIGN TIPO_VEHICULO,floor(RANDOM * 2 + 2)\r\nmod {RADIO:10,color:#006600}\r\ncall PROC.MAIN\r\nENDGENERATE 1  ; ALIAS DE TERMINATE\r\n;************************************************************************\r\n\r\nPROCEDURE PROC.MAIN\r\n\r\nASSIGN ciclos,1\r\n\r\nADVANCE0  {TO:POS_ENTRADA,flow:1,layout:H}\r\nASSIGN estado, \"loop\"\r\n\r\nWHILE (\"P$estado\"!=\"EXIT\")\r\n\r\n\tQUEUE EN_APARCAMIENTO\r\n\r\n\tCALL DECIDE.RUTA ; return assign:RUTA\r\n    CALL \"P$(RUTA)\"\r\n    ADVANCE 20,18 {TO:POS_SALIDA,flow:1,MERGE:\"salida\"}\r\n\r\n\tDEPART EN_APARCAMIENTO\r\n    \r\n    CALL DECIDE.LOOP_OR_END\r\n    ASSIGN estado,\"P$(LOOP_OR_END)\"\r\nENDWHILE\r\n\r\nADVANCE 20,0 {TO:FIN,flow:1}\r\n\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE DECIDE.RUTA\r\n    ASSIGN VALUE,\"\"\r\n    SWITCH P$TIPO_VEHICULO\r\n    CASE <=,1\r\n\t    ASSIGN VALUE,\"PROC.UTILITARIOS\"\r\n    ENDCASE\r\n    DEFAULT\r\n    \tASSIGN VALUE,\"PROC.CAMIONES\"\r\n    ENDCASE\r\n    ENDSWITCH\r\n\t\r\nENDPROCEDURE \"P$VALUE\"\r\n;---------------------------------------------\r\nPROCEDURE DECIDE.LOOP_OR_END\r\n    assign value,\"loop\"\r\n    assign ciclos,P$ciclos + 1\r\n\tif (P$ciclos > 3)\r\n    \tassign value,\"EXIT\"\r\n        mod {SUBTITLE:\"-EXIT-\"}\r\n    else\r\n        advance 30 {to:POS_ENTRADA,flow:1,via:POS_LOOP,layout:V}\r\n\tendif\r\nENDPROCEDURE \"P$value\"\r\n\r\n;---------------------------------------------\r\nPROCEDURE PROC.UTILITARIOS\r\n    ADVANCE 20  {TO:UTILITARIOS,flow:1,DECISION:\"recurso\"}\r\n    SEIZE UTILITARIOS\r\n    ADVANCE 55,40\r\n    RELEASE UTILITARIOS\r\nENDPROCEDURE \r\n\r\nPROCEDURE PROC.CAMIONES\r\n    ADVANCE 20 {TO:CAMIONES,flow:1,DECISION:\"recurso\" }\r\n    ENTER CAMIONES  ,P$TIPO_VEHICULO\r\n    ADVANCE 40,20\r\n    LEAVE CAMIONES \r\nENDPROCEDURE \r\n;----------------------------------\r\n",
                "descripcion": "<p>Ahora que ya conoces los fundamentos de GPSS-Plus, veamos c&oacute;mo se integran todos en un ejemplo unificado.<\/p>\r\n<p>Vamos a ver un c&oacute;digo bastante m&aacute;s largo de lo que hemos visto hasta ahora, casi 100 l&iacute;neas de c&oacute;digo.<\/p>\r\n<p>Este modelo representa un sistema con <strong data-start=\"1115\" data-end=\"1147\">veh&iacute;culos de distintos tipos<\/strong> que recorren una zona de trabajo durante varios ciclos. Cada uno decide su ruta, ejecuta tareas, y finaliza tras completar un n&uacute;mero de ciclos entrando en el aparcamiento de utilitarios o de camiones seg&uacute;n el caso ocupando la <code>facility <\/code>o el <code>storage <\/code>dependiendo del tama&ntilde;o del cami&oacute;n.<\/p>\r\n<p>Este c&oacute;digo tiene varias novedades:<\/p>\r\n<p>Lo primero que observamos es que hay a nivel general una estructura con dos <code>GENERATES<\/code> varios <code>PROCEDURES<\/code>.<\/p>\r\n<p>Los generates se separan visualmente para que cada uno tenga su propia actividad y marcan las entidades nacidas de ellos de forma diferenciada.<\/p>\r\n<pre>\r\nGENERATE ... NAME:G_UTILITARIOSG_UTILITARIOS ; genera los utilitarios\r\n   ....\r\n   assign ...\r\n   ....\r\n   CALL PROC.MAIN\r\nENDGENERATE 1<\/pre>\r\n<p>Y despu&eacute;s tenemos la serie de <code>PROCEDURES<\/code>:<\/p>\r\n<p>Procedures de proceso:<\/p>\r\n<pre>\r\nPROC.MAIN         ; Proceso principal\r\nPROC.UTILITARIOS\r\nPROC.CAMIONES\r\n<\/pre>\r\n<p>Procedures de decisi&oacute;n:<\/p>\r\n<pre>\r\nDECIDE.RUTA\r\nDECIDE.LOOP_OR_END\r\n<\/pre>\r\n<p><b>PROC.MAIN:<\/b><\/p>\r\n<p>Toda la l&oacute;gica principal estar&aacute; concentrada en el proceso PROC.MAIN que normalmente estar&aacute; formado por un bucle <code>while <\/code>que encerrar&aacute; las entidades mientras no cumplan la condici&oacute;n de finalizar todas sus tareas.<\/p>\r\n<pre>\r\nWHILE (&quot;P$estado&quot;!=&quot;EXIT&quot;)\r\n    ...\r\n&nbsp; &nbsp;&nbsp;...\r\n&nbsp; &nbsp; ASSIGN estado,????\r\nENDWHILE\r\n<\/pre>\r\n<p>Dentro de &eacute;ste se situar&aacute; la decisi&oacute;n sobre las rutas principales que pude tomar cada entidad que suele ser un <code>SWITCH <\/code>de opciones o varios <code>IF<\/code>s anidados. Todas estas decisiones conviene colocarlas en <code>PROCEDURES <\/code>con el nombre: <code>DECIDE.*<\/code>&nbsp;de forma que sea sencillo reconocerlos y saber de antemano que lo normal es que retornen, directamente el nombre del <code>PROCEDURE<\/code> a ser invocado por la entidad:<\/p>\r\n<pre>\r\nENDPROCEDURE &quot;P$VALUE&quot; ; Retornar&aacute; 'PROC.UTILITARIOS' &oacute; 'PROC.CAMIONES'<\/pre>\r\n<p>Es decir: CALL DECIDE.AAA devolver&aacute; en P$AAA (lo anterior al punto se obvia) el valor: 'PROC.BBB' que podr&aacute; usarse directamente como CALL&nbsp;P$AAA.<\/p>\r\n<pre>\r\nCALL DECIDE.RUTA           ; Ejecuta l&oacute;gica de decisi&oacute;n\r\nCALL &quot;P$(RUTA)&quot;            ; Llama al procedimiento retornado por la decisi&oacute;n<\/pre>\r\n<p>&nbsp;Muy parecido a la decisi&oacute;n de salir o continuar dando vueltas al ciclo.<\/p>\r\n<p>Otros elementos nuevos son:<\/p>\r\n<pre>\r\nADVANCE0  {TO:POS_ENTRADA,flow:1,layout:H}<\/pre>\r\n<p>Que en este caso es un <code>ADVANCE0 <\/code>y no <code>ADVANCE<\/code> puesto que no queremos m&aacute;s que dar un salto visual sin que la entidad pierda tiempo entrando y saliendo de la cola de eventos.<br>\r\n<br>\r\nY la &uacute;ltima novedad es el uso de otro recurso que tiene una utilidad de agrupamiento y estad&iacute;stica: <code>QUEUER<\/code><\/p>\r\n<p>&nbsp;Tiene capacidad ilimitada y con &eacute;l se peude saber de forma sencilla cu&aacute;ntas y cu&aacute;les son las entidades que est&aacute;n en una determinada zona.<\/p>\r\n<pre>\r\nQUEUE EN_APARCAMIENTO  ; entran\r\n...\r\n...\r\nDEPART EN_APARCAMIENTO ; salen<\/pre>\r\n<p>Resumidamente se puede observar en el ejemplo:<\/p>\r\n<ul>\r\n    <li>C&oacute;mo&nbsp;usar varios&nbsp;<strong data-start=\"1335\" data-end=\"1378\"><code data-start=\"1342\" data-end=\"1352\">GENERATE<\/code> <\/strong><strong data-start=\"1335\" data-end=\"1378\">con l&oacute;gica diferenciada<\/strong> (camiones y utilitarios).<\/li>\r\n    <li>C&oacute;mo las entidades pueden <strong data-start=\"1433\" data-end=\"1464\">decidir rutas din&aacute;micamente<\/strong> (<code data-start=\"1466\" data-end=\"1474\">SWITCH<\/code>, <code data-start=\"1476\" data-end=\"1482\">CALL<\/code>, <code data-start=\"1484\" data-end=\"1495\">PROCEDURE<\/code>).<\/li>\r\n    <li>C&oacute;mo se usa un <strong data-start=\"1515\" data-end=\"1544\">bucle interno por entidad<\/strong> (<code data-start=\"1546\" data-end=\"1553\">WHILE<\/code>) para simular iteraci&oacute;n.<\/li>\r\n    <li>C&oacute;mo visualizar el <strong data-start=\"1600\" data-end=\"1646\">flujo con posiciones, subt&iacute;tulos y estilos<\/strong>.<\/li>\r\n    <li>C&oacute;mo combinar distintos <strong data-start=\"1674\" data-end=\"1686\">recursos<\/strong> (<code data-start=\"1688\" data-end=\"1698\">FACILITY<\/code>, <code data-start=\"1700\" data-end=\"1709\">STORAGE<\/code>, <code data-start=\"1711\" data-end=\"1718\">QUEUE<\/code>).<\/li>\r\n    <li>C&oacute;mo trabajar con <strong data-start=\"1816\" data-end=\"1855\">nombres din&aacute;micos de procedimientos<\/strong> (<code data-start=\"1857\" data-end=\"1867\">P$(RUTA)<\/code>).<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "243",
        "nombre": "Season 2: Entidades virtuales (VE)",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "244",
                "nombre": "Entidades y entidades virtuales VE",
                "texto": "\/* Entidades Virtuales:\r\nProcesos invisibles que operan en segundo plano.\r\nLa primera entidad creará con un retraso de 30 momentos una VE.\r\nEsta VE creará su siguiente cada 3 momentos.\r\nSolo son visibles las entidades, no las VE.\r\n*\/\r\n\r\n\r\nPOSITION {NAME:POS1,X:321,Y:332}\r\nPOSITION {NAME:POS2,X:517,Y:326}\r\nGraphic {NAME:Text1,Type:TEXT,X:366,Y:454,Text:\"Contador: 0\"}\r\n\r\nINITIAL contador,0\r\n\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 20,0 {NAME:GEN1,x:101,y:332}\r\nADVANCE 10 {TO:POS1}\r\n; Lanzamos una entidad virtual que actualizará el contador\r\nif (D$N==1)\r\n\ttimeout actualizarContador,30\r\nendif\r\nADVANCE 10 {TO:POS2}\r\n\r\nENDGENERATE 1\r\n\r\n;*****************************************************\r\nPROCEDURE actualizarContador\r\n    SAVEVALUE contador, X$contador + 1\r\n    MOVE {name:Text1, text:\"Soy la Entidad Virtual D$N. Contador: X$contador\"}\r\n    TIMEOUT actualizarContador, 3\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n",
                "descripcion": "<p>Ya hemos visto sobradamente qu&eacute; es una entidad: Una bolita que se mueve por la pantalla.&nbsp;Sabemos que nace de un <code>GENERATE<\/code>, que recorre bloques, avanza por posiciones y puede entrar en recursos.<\/p>\r\n<p><b>&iquest;Y qu&eacute; es una entidad virtual?<\/b> Pues una entidad que no se mueve por la pantalla.<\/p>\r\n<p>Parece muy simple la diferencia pero es b&aacute;sicamente esa aunque conlleva muchas implicaciones.&nbsp;Imagina una bolita invisible que no ocupa espacio pero s&iacute; ejecuta instrucciones.<\/p>\r\n<p>Vamos a definirlas formalmente:<\/p>\r\n<ul>\r\n    <li>Las entidades virtuales no nacen de un GENERATE.  En su lugar nacen de los COMANDOS: <code>TIMER<\/code>, <code>PRE_RUN<\/code>, <code>TRIGGER<\/code> (<code>ON_ENTER<\/code>, <code>ON_QUEUE<\/code>, <code>ON_*<\/code>...) O del BLOQUE: <code>TIMEOUT  <\/code><\/li>\r\n    <li>Su n&uacute;mero identificativo (D$N) no es secuencial positivo (1,2,... n) sino justo del rev&eacute;s: (-1, -2,... -n)<\/li>\r\n    <li>Su <code>TERMINATE <\/code>no resta sucesos. De hecho, veremos que se usa <code>TERMINATE_VE<\/code> que solo termina con las virtuales.<\/li>\r\n    <li>No tienen representaci&oacute;n visual directa pero pueden interactuar como cualquiera con la parte gr&aacute;fica.<\/li>\r\n<\/ul>\r\n<p><b><\/b><b>&iquest;Y para qu&eacute; sirven?<\/b><\/p>\r\n<p>Pues tienen infinidad de utilidades.<\/p>\r\n<p>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.<\/p>\r\n<p>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&oacute;n de cosas de las que preocuparse entrando y saliendo de recursos. <br>\r\nEn su lugar, un <code>TIMER<\/code> que se lanzar&aacute; autom&aacute;ticamente cada N instantes puede hacer estas tareas. <br>\r\nSi queremos tener un cierto control de m&uacute;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.<\/p>\r\n<p>Las entidades virtuales trabajan entre bastidores. No se ven moverse, pero sin ellas, muchos procesos simplemente no ocurrir&iacute;an.<\/p>\r\n<p><b>En el ejemplo:<\/b><\/p>\r\n<p>Una entidad normal, la n&uacute;mero 1 <code>(D$N==1)<\/code>, crea una entidad virtual que nacer&aacute; dentro de 30 instantes para ejecutar un procedure:<\/p>\r\n<pre>\r\ntimeout actualizarContador,30<\/pre>\r\n<p>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. <br>\r\nSu &uacute;nica tarea: contar. <br>\r\nLa primera llama a la segunda, que llama a la tercera.... todo a trav&eacute;s de un BLOQUE <code>TIMEOUT<\/code> que s&oacute;lo requiere el nombre del procedimiento a ejecutar por la entidad virtual y el tiempo en el que ocurrir&aacute;.  <br>\r\nSe puede observar c&oacute;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&ntilde;o proceso aut&oacute;nomo.<\/p>\r\n<p>Este ejemplo demuestra c&oacute;mo una VE puede actuar como <strong data-start=\"1263\" data-end=\"1285\">proceso recurrente<\/strong> sin necesidad de ninguna intervenci&oacute;n humana ni entidad f&iacute;sica. A partir de ahora, ver&aacute;s que muchas de las <strong data-start=\"1393\" data-end=\"1443\">decisiones del sistema, alarmas o estad&iacute;sticas<\/strong> estar&aacute;n gestionadas por este tipo de entidades invisibles.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "200",
                "nombre": "Creación de las VE",
                "texto": "\/* Creación de las VE *\/\r\n\r\n\r\n\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 53}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nPOSITION {NAME:POS1,X:311,Y:497}\r\nPOSITION {NAME:POS2,X:571,Y:147,type:terminate,title:END}\r\n\r\nFacility {NAME:Facility1,X:573,Y:483,ON_RELEASE:on_RELEASE_Facility1}\r\n\r\n\r\n\r\nGraphic {NAME:Text_Pre_run,Type:TEXT,X:253,Y:196}\r\nGraphic {NAME:Text_TIMER1,Type:TEXT,X:253,Y:158}\r\nGraphic {NAME:Text_Timeout1,Type:TEXT,X:254,Y:252}\r\nGraphic {NAME:Text_on_release,Type:TEXT,X:575,Y:544}\r\n\r\n\r\nSTART 40\r\n\r\nGENERATE 30,0 {NAME:GEN1,X:110,Y:510, ECOLOR:#FF3333, ERADIO:8} \r\n\tif (\"P$PARAM_A\"==\"PRE_RUN\")\r\n    \tMOD {COLOR:magenta,subtitle:pre_run}\r\n    ENDIF\r\n\tif (\"P$PARAM_A\"==\"TIMER1\")\r\n    \tMOD {COLOR:blue,subtitle:timer1}\r\n    ENDIF\r\n    ADVANCE 30,2 {TO:POS1,flow:1}\r\n    ADVANCE 30,2 {TO:Facility1,flow:1}\r\n    TIMEOUT Timeout1,0,D$N ; le pasamos como parámetro el número de entidad.\r\n    seize Facility1\r\n    advance 30,30\r\n    release Facility1\r\n    ADVANCE 30,2 {TO:POS2,flow:1}\r\nENDGENERATE 1\r\n\r\n;******************************************\r\nPROCEDURE PRE_RUN\r\n\tMOVE {name:Text_Pre_run,Text:\"Soy [D$N] PRE_RUN\n Creo una nueva entidad.\"}\r\n    NEW GEN1,0,\"pre_run\" \r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n;---------------------------------\r\nPROCEDURE TIMER1\r\n\tMOVE {name:Text_TIMER1,Text:\"Soy [D$N] Timer1.\n Creo una nueva entidad.\"}\r\n    NEW GEN1,0,\"timer1\" ; Generate, tiempo, PARAM_A, PARAM_B...\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;---------------------------------\r\nPROCEDURE Timeout1\r\n\tMOVE {name:Text_Timeout1,Text:\"Soy  [D$N] Timeout1.\n La entidad [P$PARAM_A] va a entrar\"}\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\nPROCEDURE on_RELEASE_Facility1\r\n\tMOVE {name:Text_on_release,Text:\"Soy [D$N] on_RELEASE.\n La entidad [P$ENTITYNUMBER] abandona la Facility1\"}\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n\r\n;******************************************\r\n\r\n",
                "descripcion": "<p>Ya hemos visto qu&eacute; es una entidad virtual: una entidad que no se ve moverse por la pantalla pero que ejecuta instrucciones y participa del motor de simulaci&oacute;n.<br data-start=\"757\" data-end=\"760\">\r\nAhora veremos <strong data-start=\"774\" data-end=\"814\">c&oacute;mo nacen estas entidades virtuales<\/strong> y qui&eacute;n puede crearlas.<\/p>\r\n<h3 data-start=\"845\" data-end=\"868\">&iquest;Qui&eacute;n crea una VE?<\/h3>\r\n<p data-start=\"870\" data-end=\"997\">Las VE no nacen de un <code data-start=\"892\" data-end=\"902\">GENERATE<\/code>, como las entidades normales. En su lugar, pueden ser creadas por cuatro mecanismos distintos:<\/p>\r\n<ul data-start=\"999\" data-end=\"1348\">\r\n    <li data-start=\"999\" data-end=\"1146\">\r\n    <p data-start=\"1001\" data-end=\"1026\"><strong data-start=\"1003\" data-end=\"1017\">El sistema<\/strong>, usando:<\/p>\r\n    <ul data-start=\"1029\" data-end=\"1146\">\r\n        <li data-start=\"1029\" data-end=\"1086\">\r\n        <p data-start=\"1031\" data-end=\"1086\"><code data-start=\"1031\" data-end=\"1040\">PRE_RUN<\/code> &ndash; al comienzo de la simulaci&oacute;n, una sola vez.<\/p>\r\n        <\/li>\r\n        <li data-start=\"1089\" data-end=\"1146\">\r\n        <p data-start=\"1091\" data-end=\"1146\"><code data-start=\"1091\" data-end=\"1098\">TIMER<\/code> &ndash; en intervalos fijos definidos por <code data-start=\"1135\" data-end=\"1145\">INTERVAL<\/code>.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n    <li data-start=\"1148\" data-end=\"1252\">\r\n    <p data-start=\"1150\" data-end=\"1176\"><strong data-start=\"1152\" data-end=\"1167\">Una entidad<\/strong>, usando:<\/p>\r\n    <ul data-start=\"1179\" data-end=\"1252\">\r\n        <li data-start=\"1179\" data-end=\"1252\">\r\n        <p data-start=\"1181\" data-end=\"1252\"><code data-start=\"1181\" data-end=\"1190\">TIMEOUT<\/code> &ndash; programa la ejecuci&oacute;n de una VE para dentro de X instantes.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n    <li data-start=\"1254\" data-end=\"1348\">\r\n    <p data-start=\"1256\" data-end=\"1301\">&nbsp;<strong data-start=\"1258\" data-end=\"1272\">Un recurso<\/strong>, usando eventos <code data-start=\"1289\" data-end=\"1295\">ON_*<\/code> como:<\/p>\r\n    <ul data-start=\"1304\" data-end=\"1348\">\r\n        <li data-start=\"1304\" data-end=\"1348\">\r\n        <p data-start=\"1306\" data-end=\"1348\"><code data-start=\"1306\" data-end=\"1316\">ON_SEIZE<\/code>, <code data-start=\"1318\" data-end=\"1330\">ON_RELEASE<\/code>, <code data-start=\"1332\" data-end=\"1342\">ON_LEAVE<\/code>, etc.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ul>\r\n<p><b><\/b><\/p>\r\n<p><b>&iquest;Qu&eacute; tienen en com&uacute;n?<\/b><\/p>\r\n<p data-start=\"1382\" data-end=\"1635\">Todas las VEs ejecutan un <code data-start=\"1408\" data-end=\"1419\">PROCEDURE<\/code>, que puede venir indicado como <code data-start=\"1451\" data-end=\"1460\">TRIGGER<\/code> o como nombre de procedimiento.<br data-start=\"1492\" data-end=\"1495\">\r\nEse <code data-start=\"1499\" data-end=\"1510\">PROCEDURE<\/code> es el &quot;c&oacute;digo&quot; que ejecuta la VE. <strong data-start=\"1545\" data-end=\"1598\">Debe terminar con un <code data-start=\"1568\" data-end=\"1579\">TERMINATE<\/code> o <code data-start=\"1582\" data-end=\"1596\">TERMINATE_VE<\/code><\/strong>, igual que cualquier entidad normal.<\/p>\r\n<p><b>Sobre los par&aacute;metros<\/b><\/p>\r\n<p data-start=\"1668\" data-end=\"1873\">Cuando usamos <code data-start=\"1682\" data-end=\"1687\">NEW<\/code>, <code data-start=\"1689\" data-end=\"1698\">TIMEOUT<\/code> o <code data-start=\"1701\" data-end=\"1707\">CALL<\/code>, podemos pasar par&aacute;metros adicionales.<br data-start=\"1746\" data-end=\"1749\">\r\nEstos estar&aacute;n disponibles dentro del procedimiento como <code data-start=\"1805\" data-end=\"1816\">P$PARAM_A<\/code>, <code data-start=\"1818\" data-end=\"1829\">P$PARAM_B<\/code>, etc., igual que ya hemos visto con <code data-start=\"1866\" data-end=\"1872\">CALL<\/code>.<\/p>\r\n<p data-start=\"1875\" data-end=\"1936\">Esto permite que una VE &quot;sepa&quot; qui&eacute;n la ha llamado o por qu&eacute;.<\/p>\r\n<p><b>En el&nbsp;ejemplo<\/b>:<\/p>\r\n<ul data-start=\"1976\" data-end=\"2208\">\r\n    <li data-start=\"1976\" data-end=\"2031\">\r\n    <p data-start=\"1978\" data-end=\"2031\"><code data-start=\"1978\" data-end=\"1987\">PRE_RUN<\/code> crea una entidad al inicio (color magenta).<\/p>\r\n    <\/li>\r\n    <li data-start=\"2032\" data-end=\"2082\">\r\n    <p data-start=\"2034\" data-end=\"2082\"><code data-start=\"2034\" data-end=\"2042\">TIMER1<\/code> crea una cada 53 unidades (color azul).<\/p>\r\n    <\/li>\r\n    <li data-start=\"2083\" data-end=\"2155\">\r\n    <p data-start=\"2085\" data-end=\"2155\"><code data-start=\"2085\" data-end=\"2094\">TIMEOUT<\/code> se lanza justo antes de entrar al recurso y deja un mensaje.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2156\" data-end=\"2208\">\r\n    <p data-start=\"2158\" data-end=\"2208\"><code data-start=\"2158\" data-end=\"2170\">ON_RELEASE<\/code> se dispara al liberar la <code data-start=\"2196\" data-end=\"2207\">FACILITY1<\/code>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2210\" data-end=\"2322\">Cada vez que una de estas VEs se crea, aparece un texto en pantalla con su identificador y lo que est&aacute; haciendo.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "251",
                "nombre": "Las VE permanentes, los agentes",
                "texto": "\/* Agentes: Entidades Virtuales Permanentes\r\nMonitorización de recursos con un bucle infinito.\r\n*\/\r\n\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nPOSITION {NAME:POS1,X:311,Y:497}\r\nPOSITION {NAME:POS2,X:571,Y:147,type:terminate,title:END}\r\n\r\nFacility {NAME:Facility1,X:573,Y:483}\r\n\r\nGraphic {NAME:Text_agente,Type:TEXT,X:229,Y:305}\r\n\r\n\r\nSTART 40\r\n\r\nGENERATE 30,0 {NAME:GEN1,X:110,Y:510, ECOLOR:#FF3333, ERADIO:8} \r\n    ADVANCE 30,2 {TO:POS1,flow:1}\r\n    ADVANCE 30,2 {TO:Facility1,flow:1}\r\n    seize Facility1\r\n    advance 30,30\r\n    release Facility1\r\n    ADVANCE 30,2 {TO:POS2,flow:1}\r\nENDGENERATE 1\r\n\r\n;******************************************\r\nPROCEDURE PRE_RUN\r\n    TIMEOUT AGENTE.INIT,0\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n;---------------------------------\r\nPROCEDURE AGENTE.INIT\r\n\tsavevalue nAgente,D$N\r\n\tWHILE (1==1)\r\n \t   MOVE {name:Text_agente,Text:\"Soy el agente [D$N].\nTiempo AC1$\n Entidades en Facility1: R$(Facility1,IN)\nEntidades en cola: R$(Facility1,QUEUE)\"}\r\n  \t  ADVANCE 10\r\n    ENDWHILE\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n",
                "descripcion": "<p>Un tipo especial de entidad virtual es aquella que <strong data-start=\"369\" data-end=\"381\">no muere<\/strong> al final de su procedimiento.<br data-start=\"411\" data-end=\"414\">\r\nEstas <strong data-start=\"420\" data-end=\"476\">VE que permanecen activas durante toda la simulaci&oacute;n<\/strong> (o parte de ella) las llamamos <strong data-start=\"508\" data-end=\"519\">agentes<\/strong>.<\/p>\r\n<ul>\r\n    <li>Son como procesos en segundo plano que nunca terminan.<\/li>\r\n    <li>Se lanzan normalmente desde&nbsp;<code data-start=\"612\" data-end=\"621\">PRE_RUN<\/code> mediante un <code data-start=\"634\" data-end=\"643\">TIMEOUT<\/code>.<\/li>\r\n    <li>Guardan su <strong data-start=\"658\" data-end=\"685\">n&uacute;mero de entidad (D$N)<\/strong> en un <code data-start=\"692\" data-end=\"703\">SAVEVALUE<\/code>, para poder interactuar o consultar su estado.<\/li>\r\n<\/ul>\r\n<p>M&aacute;s adelante veremos&nbsp;que estos <strong data-start=\"800\" data-end=\"811\">agentes<\/strong> se encargan de <strong data-start=\"827\" data-end=\"848\">tareas de control<\/strong>, <strong data-start=\"850\" data-end=\"868\">monitorizaci&oacute;n<\/strong>&nbsp;o <strong data-start=\"872\" data-end=\"888\">coordinaci&oacute;n<\/strong> entre recursos.<\/p>\r\n<p>En resumen, un agente es una VE que no hace TERMINATE y permanece viva en bucle.<br>\r\nEso le permite estar atento, esperando, o vigilando el entorno.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "370",
                "nombre": "Entidades componente",
                "texto": "<p data-start=\"221\" data-end=\"599\">Adem&aacute;s de las entidades normales y de los agentes, el modelo admite un tercer tipo de entidad: las <strong data-start=\"320\" data-end=\"344\">entidades componente<\/strong>.<br data-start=\"345\" data-end=\"348\">\r\nUna entidad componente es una entidad que existe para ejecutar una funci&oacute;n <em data-start=\"423\" data-end=\"434\">en nombre<\/em> de otra entidad. No es un objeto f&iacute;sico ni un recurso externo, sino una parte activa de su comportamiento: algo que la entidad &ldquo;hace&rdquo; de manera continua o paralela.<\/p>\r\n<p data-start=\"601\" data-end=\"836\">Este concepto permite modelar situaciones muy naturales:<br data-start=\"657\" data-end=\"660\">\r\nun buzo que respira y consume ox&iacute;geno mientras avanza, un veh&iacute;culo cuyas ruedas se desgastan mientras circula, o una persona que lee una noticia y cambia de decisi&oacute;n mientras espera en la cola del autob&uacute;s.<\/p>\r\n<p data-start=\"838\" data-end=\"1090\">M&aacute;s adelante veremos c&oacute;mo se crean y c&oacute;mo trabajan junto a la entidad principal, pero por ahora basta con entender que permiten que una entidad no sea un &uacute;nico hilo secuencial, sino un peque&ntilde;o sistema formado por varias entidades colaborando entre s&iacute;.<\/p>\r\n<p>&nbsp;<\/p>",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "278",
        "nombre": "Season 3: DSL (Domain-Specific Language)",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "284",
                "nombre": "Filosofía del DSL",
                "texto": "\/* Season 3: DSL\r\nEjemplo de modelo simple con el DSL de GPSS-Plus.\r\n*\/\r\n\r\nFACILITY {NAME:VENTANILLA, X:300, Y:300} ; Definimos la ventanilla en el espacio. \r\nPOSITION {NAME:SALIDA, X:500, Y:300}    ; Definimos la salida en el espacio.  \r\n\r\nSTART 100                               ; Ejecuta esta acción hasta que 100 entidades hayan sido atendidas\r\n;------------------------------------------------------------------------------------\r\nGENERATE 60,10 {NAME:GEN1, X:100, Y:300} ; Posicionamos el generador de entidades. \r\n    ADVANCE 10 {TO:VENTANILLA}               ; Avanza hasta la ventanilla. \r\n    SEIZE VENTANILLA                        ; Asignación del recurso \"VENTANILLA\". \r\n    ADVANCE 60,30                           ; Tiempo de atención: 60 a 90 segundos. \r\n    RELEASE VENTANILLA                      ; Liberación del recurso. \r\n    ADVANCE 10 {TO:SALIDA}                  ; Avanza hasta la salida. \r\nTERMINATE 1                             ; La entidad finaliza. \r\n",
                "descripcion": "<h3 data-start=\"1647\" data-end=\"1681\">Filosof&iacute;a del DSL de GPSS-Plus<\/h3>\r\n<p data-start=\"1683\" data-end=\"1885\">El DSL de GPSS-Plus no est&aacute; pensado como un lenguaje de programaci&oacute;n general, ni como una notaci&oacute;n alternativa para dibujar bloques. Est&aacute; dise&ntilde;ado como <strong data-start=\"1835\" data-end=\"1884\">el lenguaje operativo del motor de simulaci&oacute;n<\/strong>.<\/p>\r\n<p data-start=\"1887\" data-end=\"2024\">Cuando se trabaja con el DSL, el usuario no &ldquo;programa&rdquo;: <strong data-start=\"1943\" data-end=\"2023\">define comportamientos, recursos y eventos que el motor ejecuta en el tiempo<\/strong>: <b>MODELA<\/b>.<\/p>\r\n<p data-start=\"2026\" data-end=\"2143\">Para entenderlo correctamente, conviene pensar el DSL como un conjunto de herramientas organizadas en cuatro niveles.<\/p>\r\n<hr data-start=\"2145\" data-end=\"2148\">\r\n<h3 data-start=\"2150\" data-end=\"2208\">1. Entidades visibles o f&iacute;sicas: comportamiento de las &ldquo;bolitas&rdquo;<\/h3>\r\n<p data-start=\"2210\" data-end=\"2310\">El primer nivel del DSL est&aacute; orientado a definir <strong data-start=\"2259\" data-end=\"2309\">qu&eacute; hacen las entidades que recorren el modelo<\/strong>.<\/p>\r\n<p data-start=\"2312\" data-end=\"2328\">Estas entidades:<\/p>\r\n<ul data-start=\"2329\" data-end=\"2500\">\r\n    <li data-start=\"2329\" data-end=\"2348\">\r\n    <p data-start=\"2331\" data-end=\"2348\">nacen (GENERATE),<\/p>\r\n    <\/li>\r\n    <li data-start=\"2349\" data-end=\"2382\">\r\n    <p data-start=\"2351\" data-end=\"2382\">avanzan en el tiempo (ADVANCE),<\/p>\r\n    <\/li>\r\n    <li data-start=\"2383\" data-end=\"2432\">\r\n    <p data-start=\"2385\" data-end=\"2432\">interact&uacute;an con recursos (SEIZE, ENTER, REST&hellip;),<\/p>\r\n    <\/li>\r\n    <li data-start=\"2433\" data-end=\"2465\">\r\n    <p data-start=\"2435\" data-end=\"2465\">toman decisiones (IF, SWITCH),<\/p>\r\n    <\/li>\r\n    <li data-start=\"2466\" data-end=\"2500\">\r\n    <p data-start=\"2468\" data-end=\"2500\">y finalmente mueren (TERMINATE).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2502\" data-end=\"2637\">Todo su comportamiento se describe <strong data-start=\"2537\" data-end=\"2552\">paso a paso<\/strong>, pero ese paso a paso no es secuencial real: es ejecuci&oacute;n sobre una cola de eventos.<\/p>\r\n<p data-start=\"2639\" data-end=\"2663\">El DSL proporciona aqu&iacute;:<\/p>\r\n<ul data-start=\"2664\" data-end=\"2770\">\r\n    <li data-start=\"2664\" data-end=\"2683\">\r\n    <p data-start=\"2666\" data-end=\"2683\">bloques de flujo,<\/p>\r\n    <\/li>\r\n    <li data-start=\"2684\" data-end=\"2703\">\r\n    <p data-start=\"2686\" data-end=\"2703\">control temporal,<\/p>\r\n    <\/li>\r\n    <li data-start=\"2704\" data-end=\"2741\">\r\n    <p data-start=\"2706\" data-end=\"2741\">estructuras condicionales y bucles,<\/p>\r\n    <\/li>\r\n    <li data-start=\"2742\" data-end=\"2770\">\r\n    <p data-start=\"2744\" data-end=\"2770\">llamadas a procedimientos.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2772\" data-end=\"2856\">Este es el nivel m&aacute;s visible del modelo y el m&aacute;s cercano a la intuici&oacute;n del usuario.<\/p>\r\n<hr data-start=\"2858\" data-end=\"2861\">\r\n<h3 data-start=\"2863\" data-end=\"2922\">2. Entidades virtuales (VE): comportamiento paralelo<\/h3>\r\n<p data-start=\"2924\" data-end=\"2972\">No todo comportamiento pertenece a una &ldquo;bolita&rdquo;.<\/p>\r\n<p data-start=\"2974\" data-end=\"3047\">GPSS-Plus introduce <strong data-start=\"2994\" data-end=\"3022\">entidades virtuales (VE)<\/strong> para modelar l&oacute;gica que:<\/p>\r\n<ul data-start=\"3048\" data-end=\"3156\">\r\n    <li data-start=\"3048\" data-end=\"3082\">\r\n    <p data-start=\"3050\" data-end=\"3082\">no tiene representaci&oacute;n gr&aacute;fica,<\/p>\r\n    <\/li>\r\n    <li data-start=\"3083\" data-end=\"3108\">\r\n    <p data-start=\"3085\" data-end=\"3108\">no recorre el circuito,<\/p>\r\n    <\/li>\r\n    <li data-start=\"3109\" data-end=\"3156\">\r\n    <p data-start=\"3111\" data-end=\"3156\">pero <strong data-start=\"3116\" data-end=\"3155\">vive dentro del motor de simulaci&oacute;n<\/strong>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3158\" data-end=\"3174\">Las VE permiten:<\/p>\r\n<ul data-start=\"3175\" data-end=\"3366\">\r\n    <li data-start=\"3175\" data-end=\"3229\">\r\n    <p data-start=\"3177\" data-end=\"3229\">reaccionar a eventos (ON_SEIZE, ON_RELEASE, SIGNAL),<\/p>\r\n    <\/li>\r\n    <li data-start=\"3230\" data-end=\"3268\">\r\n    <p data-start=\"3232\" data-end=\"3268\">ejecutar l&oacute;gica peri&oacute;dica (TIMEOUT),<\/p>\r\n    <\/li>\r\n    <li data-start=\"3269\" data-end=\"3321\">\r\n    <p data-start=\"3271\" data-end=\"3321\">actuar como agentes, controladores, incluso como comonentes,<\/p>\r\n    <\/li>\r\n    <li data-start=\"3322\" data-end=\"3366\">\r\n    <p data-start=\"3324\" data-end=\"3366\">coordinar otras entidades sin bloquearlas.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3368\" data-end=\"3527\">Desde el punto de vista del DSL, <strong data-start=\"3401\" data-end=\"3435\">una VE es una entidad completa<\/strong>, con ciclo de vida propio, pero orientada al control del sistema o entidades.<\/p>\r\n<hr data-start=\"3529\" data-end=\"3532\">\r\n<h3 data-start=\"3534\" data-end=\"3598\">3. Recursos: el entorno con el que interact&uacute;an las entidades<\/h3>\r\n<p data-start=\"3600\" data-end=\"3657\">El tercer nivel del DSL define el <strong data-start=\"3634\" data-end=\"3656\">entorno del modelo<\/strong>.<\/p>\r\n<p data-start=\"3659\" data-end=\"3756\">Los recursos representan aquello que las entidades usan, ocupan, esperan o consultan. Pueden ser:<\/p>\r\n<ul data-start=\"3758\" data-end=\"3896\">\r\n    <li data-start=\"3758\" data-end=\"3809\">\r\n    <p data-start=\"3760\" data-end=\"3809\"><strong data-start=\"3760\" data-end=\"3771\">f&iacute;sicos<\/strong>: FACILITY, STORAGE, RESTROOM, STOCK<\/p>\r\n    <\/li>\r\n    <li data-start=\"3810\" data-end=\"3850\">\r\n    <p data-start=\"3812\" data-end=\"3850\"><strong data-start=\"3812\" data-end=\"3823\">l&oacute;gicos<\/strong>: QUEUER, CONDITIONS, FSM<\/p>\r\n    <\/li>\r\n    <li data-start=\"3851\" data-end=\"3896\">\r\n    <p data-start=\"3853\" data-end=\"3896\"><strong data-start=\"3853\" data-end=\"3865\">externos<\/strong>: BRIDGER, FILE, TABLE, PLOTTER<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3898\" data-end=\"3944\">Cada recurso sigue el mismo patr&oacute;n conceptual:<\/p>\r\n<ol data-start=\"3945\" data-end=\"4047\">\r\n    <li data-start=\"3945\" data-end=\"3978\">\r\n    <p data-start=\"3948\" data-end=\"3978\">se define mediante un <b>COMANDO<\/b>,<\/p>\r\n    <\/li>\r\n    <li data-start=\"3979\" data-end=\"4018\">\r\n    <p data-start=\"3982\" data-end=\"4018\">se usa mediante <b>BLOQUES <\/b>emparejados,<\/p>\r\n    <\/li>\r\n    <li data-start=\"4019\" data-end=\"4047\">\r\n    <p data-start=\"4022\" data-end=\"4047\">se observa mediante <b>SNAs<\/b>.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"4049\" data-end=\"4152\">El DSL no oculta el estado de los recursos: <strong data-start=\"4093\" data-end=\"4151\">todo puede ser le&iacute;do, consultado y usado en decisiones<\/strong>.<\/p>\r\n<hr data-start=\"4154\" data-end=\"4157\">\r\n<h3 data-start=\"4159\" data-end=\"4201\">4. El motor: tiempo, eventos y sistema<\/h3>\r\n<p data-start=\"4203\" data-end=\"4321\">Por debajo de entidades y recursos existe un cuarto nivel, menos visible pero fundamental: <strong data-start=\"4294\" data-end=\"4320\">el motor de simulaci&oacute;n<\/strong>.<\/p>\r\n<p data-start=\"4323\" data-end=\"4376\">El DSL permite interactuar con &eacute;l de forma expl&iacute;cita:<\/p>\r\n<ul data-start=\"4377\" data-end=\"4609\">\r\n    <li data-start=\"4377\" data-end=\"4410\">\r\n    <p data-start=\"4379\" data-end=\"4410\">controlando el tiempo simulado,<\/p>\r\n    <\/li>\r\n    <li data-start=\"4411\" data-end=\"4451\">\r\n    <p data-start=\"4413\" data-end=\"4451\">programando eventos futuros (TIMEOUT),<\/p>\r\n    <\/li>\r\n    <li data-start=\"4452\" data-end=\"4504\">\r\n    <p data-start=\"4454\" data-end=\"4504\">configurando el sistema (SYSTEM),&nbsp;<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<ul data-start=\"4377\" data-end=\"4609\">\r\n    <li data-start=\"4541\" data-end=\"4609\">\r\n    <p data-start=\"4543\" data-end=\"4609\">accediendo a la cola de eventos y al estado interno mediante SNAs.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"4611\" data-end=\"4638\">Esto permite modelos donde:<\/p>\r\n<ul data-start=\"4639\" data-end=\"4731\">\r\n    <li data-start=\"4639\" data-end=\"4678\">\r\n    <p data-start=\"4641\" data-end=\"4678\">el comportamiento no es solo &ldquo;flujo&rdquo;,<\/p>\r\n    <\/li>\r\n    <li data-start=\"4679\" data-end=\"4731\">\r\n    <p data-start=\"4681\" data-end=\"4731\">sino <strong data-start=\"4686\" data-end=\"4730\">reacci&oacute;n, sincronizaci&oacute;n y planificaci&oacute;n<\/strong><\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "283",
                "nombre": "Objetos y arrays en ASSIGN y SAVEVALUE",
                "texto": "\/* Variables Estructuradas: Arrays y Objetos\r\nDeclaración, acceso y modificación de datos complejos.\r\n*\/\r\n\r\nPOSITION {NAME:POS1,X:300,Y:100}\r\n\r\ninitial posY,400\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text9,Type:TEXT,X:324,Y:X$posY - 160}\r\n\r\nSTART 1 \r\n\r\n;*****************************************************************\r\n\r\nGENERATE 1,0,0,1 {NAME:GEN1,X:100,Y:100}\r\n\r\n; --- Declaraciones de todos los tipos ---\r\nASSIGN miNumero, 10 * 2\r\nASSIGN miTexto, \"Resultado\"\r\nASSIGN miArray, [10, 20, 30]\r\nASSIGN miObjeto, {clave1: 222, clave2: 333}\r\nASSIGN miArrayDeObjetos, [10, {clave1: 555, clave2: (111 * 6)}, 30]\r\n\r\n; --- Accesos por rutas ---\r\nASSIGN suma, P$(miArray.0) + P$(miArray.1)\r\n\r\n; --- Modificaciones por ruta ---\r\nASSIGN miArray.2, 777\r\nASSIGN miObjeto.clave1, {profundidad: \"valor interno\"}\r\nASSIGN miObjeto.clave2, V$(miArray.2)\r\n\r\n; --- Copias por valor bruto (estructuras completas) ---\r\nASSIGN copiaObjeto, V$(miObjeto)\r\nASSIGN elementoCopiado, V$(miArrayDeObjetos.1)\r\n\r\n; --- Evaluación de longitud ---\r\nASSIGN tamArray, P$(miArrayDeObjetos.LENGTH)\r\n\r\n; --- Visualización ---\r\nmove {name:Text1, text: \"miNumero = P$miNumero\"}\r\nmove {name:Text2, text: \"miTexto = P$miTexto\"}\r\nmove {name:Text3, text: \"miArray.2 = P$(miArray.2)\"}\r\nmove {name:Text4, text: \"miObjeto.clave1.profundidad = P$(miObjeto.clave1.profundidad)\"}\r\nmove {name:Text5, text: \"miObjeto.clave2 (copiado) = P$(miObjeto.clave2)\"}\r\nmove {name:Text6, text: \"miArrayDeObjetos.1.clave2 = P$(miArrayDeObjetos.1.clave2)\"}\r\nmove {name:Text7, text: \"copiaObjeto.clave2 = P$(copiaObjeto.clave2)\"}\r\nmove {name:Text8, text: \"elementoCopiado.clave1 = P$(elementoCopiado.clave1)\"}\r\nmove {name:Text9, text: \"Longitud array = P$tamArray\"}\r\n\r\nADVANCE 100,0 {TO:POS1}\r\nENDGENERATE 1\r\n",
                "descripcion": "<p>En cuanto al lenjuage, la evoluci&oacute;n de GPSS cl&aacute;sico a GPSS-Plus tiene dos grandes puntos de inflexi&oacute;n:<\/p>\r\n<ul>\r\n    <li>La introducci&oacute;n de los <b>STACKS<\/b>, que permiten bucles, llamadas anidadas y estructuras de control complejas.<\/li>\r\n    <li>La ampliaci&oacute;n del sistema de variables <code>SAVEVALUE<\/code> y <code>ASSIGN<\/code>, que ahora no solo almacenan n&uacute;meros, sino tambi&eacute;n <b>cadenas<\/b>, <b>arrays<\/b> y <b>objetos<\/b>&nbsp;y permiten ejecutar funciones sobre ellos.<\/li>\r\n<\/ul>\r\n<p>Este segundo avance convierte en obsoletos elementos antiguos como <code>MATRIX<\/code> y permite que GPSS-Plus incorpore funciones nativas de cualquier lenguaje moderno como <code>push<\/code>, <code>concat<\/code>, <code>merge<\/code> o <code>split<\/code>, todo dentro de la propia l&oacute;gica del simulador y sin recurrir a programaci&oacute;n externa.  Lo que antes se resolv&iacute;a con herramientas r&iacute;gidas, ahora puede hacerse de forma expresiva, estructurada y mantenible. GPSS-Plus deja de ser un lenguaje de tarjetas con n&uacute;meros y pasa a ser un <b>DSL de prop&oacute;sito completo<\/b>, orientado a flujos de eventos y manipulaci&oacute;n estructurada de datos.<\/p>\r\n<p>Ya sabemos que GPSS-Plus permite declarar variables con dos niveles de &aacute;mbito:<\/p>\r\n<ul>\r\n    <li><b>ASSIGN<\/b>: variable local a la entidad.<\/li>\r\n    <li><b>SAVEVALUE<\/b>: variable global, compartida entre entidades.<\/li>\r\n<\/ul>\r\n<p>Su funcionamiento es id&eacute;ntico salvo por su visibilidad. En ambos casos, las variables pueden contener diferentes tipos de datos:<\/p>\r\n<ul>\r\n    <li><b>N&uacute;meros<\/b>: su escritura es la habitual: <b>123.4<\/b><\/li>\r\n    <li><b>Cadenas<\/b>: siempre entre comillas dobles: <b>&quot;Un texto&quot;<\/b><\/li>\r\n    <li><b>Arrays<\/b>: su notaci&oacute;n es entre corchetes y sus elementos separados por comas: <b>[10,20,&quot;un texto&quot;,&quot;otro texto&quot;,30]<\/b><\/li>\r\n    <li><b>Objetos<\/b>: su declaraci&oacute;n es entre llaves y dentro de ellos con pares clave:valor: <b>{clave1:&quot;Un texto&quot;,clave2:123.4,clave3:[10,20,30]}<\/b><\/li>\r\n<\/ul>\r\n<p>N&oacute;tese que no existe booleano, que se usar&aacute; 0 para falso y 1 para verdadero.  Ejemplos:<\/p>\r\n<pre>\r\nASSIGN miNumero, 10 \r\nASSIGN miTexto, &quot;hola&quot; \r\nASSIGN miLista, [10, 20, 30] \r\nASSIGN miObjeto, {clave1: 20, clave2: &quot;hola&quot;} \r\nASSIGN mixto, [10, {clave: &quot;dato&quot;}, 30]<b><\/b>\r\n<\/pre>\r\n<p><b>Acceso mediante rutas en sus SNA<br>\r\n<\/b> Los SNA asociados a estas variables son <code>P$<\/code> (para <code>ASSIGN<\/code>) y <code>X$<\/code> (para <code>SAVEVALUE<\/code>).  Pero dado que las variables ahora pueden ser algo m&aacute;s que n&uacute;meros, los SNA ser&aacute;n m&aacute;s vers&aacute;tiles para poder acceder a cualquier parte de la variable. Su acceso ser&aacute; guiado por la ruta de claves separadas por puntos tanto para arrays como para objetos:<\/p>\r\n<pre>\r\nASSIGN notas, [8,4,5]\r\nASSIGN media, (P$(notas.0) +P$(notas.1) +P$(notas.2))\/3 <\/pre>\r\n<pre>\r\nASSIGN notas, {notaExamen1:8,notaExamen2:4,notaExamen3:5}\r\nASSIGN media, (P$(notas.notaExamen1) +P$(notas.notaExamen2) +P$(notas.notaExamen3))\/3 <\/pre>\r\n<p>Y todo esto puede hacerse tan complejo como sea necesario introduciendo los SNA en cualquier parte de la notaci&oacute;n para construir la ruta adecuada:<\/p>\r\n<pre>\r\nASSIGN numero_0,0\r\nASSIGN numero_1,1\r\nASSIGN numero_2,2\r\n\r\nASSIGN notas, {notaExamen_0:8,notaExamen_1:4,notaExamen_2:5}\r\nASSIGN media, (P$(notas.notaExamen_P$numero_0) + P$(notas.notaExamen_P$numero_1) + P$(notas.notaExamen_P$numero_2)) \/ 3<\/pre>\r\n<p>O el acceso a un array dentro de un objeto o un objeto dentro de un array siguiendo el mismo proceder:<\/p>\r\n<pre>\r\nASSIGN grupo, [{nombre: &quot;Luis&quot;}, {nombre: &quot;Marta&quot;}]\r\nASSIGN nombrePrimero, P$(grupo.0.nombre)\r\n<\/pre>\r\n<p>Existe una &uacute;nica palabra reservada para estas rutas: <code>LENGTH<\/code><\/p>\r\n<pre>\r\nASSIGN longitud, P$(unArray.LENGTH) <\/pre>\r\n<p><b>Modificaci&oacute;n parcial mediante rutas<\/b>  <br>\r\nTambi&eacute;n puede modificarse una parte de una estructura accediendo a ella directamente:<\/p>\r\n<pre>\r\n ASSIGN unArray.0, {clave1: &quot;texto1&quot;} <\/pre>\r\n<p>convirtiendo el primer elemento del array en un objeto  <b><\/b><\/p>\r\n<p><b>SNA de acceso sin evaluaci&oacute;n:<code>V$<\/code><\/b> <br>\r\nMientras que <code>P$<\/code> y <code>X$<\/code> eval&uacute;an los contenidos para ser mostrados, <code>V$<\/code> devuelve el contenido bruto (n&uacute;mero, cadena, objeto o array) sin tratamiento alguno. Esto es necesario para transmitir un array o un objeto completo a trav&eacute;s de un par&aacute;metro o hacer copias perfectas.<\/p>\r\n<pre>\r\n;realizar una copia:\r\nASSIGN alumnos,[{nombre:&quot;Ana&quot;,edad:20},{nombre:&quot;Alberto&quot;,edad:22},{nombre:&quot;Antonio&quot;,edad:19}]\r\nASSIGN copiaAlumnos, V$(alumnos) \r\n\r\n;obtener un elemento concreto \r\nASSIGN elemento, V$(unArray.1) \r\n\r\n; o pasarlo como par&aacute;metro\r\nCALL irAClase, V$(unArray.1)\r\n<\/pre>\r\n<p>Es importante diferenciar <code>P$unTexto<\/code> de <code>V$unTexto<\/code>.<\/p>\r\n<pre>\r\nassign miNombre:&quot;Antonio&quot;\r\nmove {name:text1,text:&quot;Mi nombre es P$miNombre&quot;} ; se muestra 'Mi nombre es Antonio'\r\nmove {name:text1,text:&quot;Mi nombre es V$miNombre&quot;} ; se muestra 'Mi nombre es &quot;Antonio&quot;'<\/pre>\r\n<p>Esta diferencia viene, precisamente, de que <code>V$<\/code> es la obtenci&oacute;n del dato bruto, que incluye la declaraci&oacute;n del tipo de dato que es, en este caso, un string.<br>\r\n<code>V$<\/code> no debe usarse dentro de cadenas directamente. Se reserva para contextos donde se espera un valor estructurado.<\/p>\r\n<p>Como &uacute;ltima nota, la caracter&iacute;stica de nulo es se trata con el operador &quot;?&quot; de tal forma que se puede asignar un valor si antes no ha sido asignado con:<\/p>\r\n<pre>\r\nASSIGN max_value,P$max_value ? P$max_value : 100<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "281",
                "nombre": "SNA (String Numeric Accessor)",
                "texto": "\/* SNA: Atributos del Sistema y Recursos\r\nEntidad virtual monitoriza el estado de la cola de un recurso.\r\n*\/\r\n\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:Timer1, INTERVAL: 5}\r\n\r\nFACILITY {NAME:Ventanilla,X:470,Y:308}\r\nGraphic {NAME:Text1,Type:TEXT,X:471,Y:254}\r\nGraphic {NAME:Text2,Type:TEXT,X:471,Y:234}\r\n\r\nSTART 100\r\n\r\nGENERATE 10,0 {name:gen1,X:115,Y:307}\r\n\tADVANCE 20 {to:Ventanilla}\r\n    SEIZE Ventanilla\r\n    ADVANCE 20 \r\n    RELEASE Ventanilla\r\nENDGENERATE 1\r\n\r\n; Mostrar la cola actual cada cierto tiempo\r\n\r\nPROCEDURE Timer1\r\n\tassign numero,D$N\r\n\tmove {name:Text1,text:\"Soy la entidad virtual número: P$numero\"}\r\n\tmove {name:Text2,text:\"T: AC1$       Entidades en cola: R$(Ventanilla,QUEUE)\"}\r\n\tTERMINATE \r\nENDPROCEDURE 1",
                "descripcion": "<p>Ya sabemos que un <code>ASSIGN<\/code> crea una caracter&iacute;stica (variable) asociada a cada entidad virtual y que un <code>SAVEVALUE<\/code> es un valor global del sistema. <br>\r\nTambi&eacute;n hemos visto que se accede a ellos a trav&eacute;s de expresiones como <code>P$nombre<\/code> o <code>X$nombre<\/code>. <br>\r\nEstas expresiones se llaman <b>SNA<\/b>, <i>String\/Numeric Accessors<\/i>, y son un mecanismo fundamental del lenguaje para acceder din&aacute;micamente a cualquier valor del modelo.<\/p>\r\n<ul>\r\n    <li><code>P$nombre<\/code> &mdash; Devuelve el contenido como <b>texto<\/b>. Es &uacute;til para trazas, etiquetas gr&aacute;ficas, o concatenaciones.<\/li>\r\n    <li><code>V$nombre<\/code> &mdash; Devuelve el <b>valor bruto<\/b> (string, n&uacute;mero, objeto o array), tal como fue asignado.<\/li>\r\n    <li><code>X$nombre<\/code> &mdash; Devuelve el contenido de un <code>SAVEVALUE<\/code> (valor global).<\/li>\r\n<\/ul>\r\n<pre>\r\nassign&nbsp;unaVariable,{nombre:&quot;Antonio&quot;,edad:30}\r\ncall fun, V$(unaVariable) ;env&iacute;a el objeto como tal<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p>Adem&aacute;s, existen SNA espec&iacute;ficos para acceder a <b>datos del sistema<\/b>:<\/p>\r\n<ul>\r\n    <li><code>AC1$<\/code> &mdash; Tiempo actual del sistema.<\/li>\r\n    <li><code>TG1$<\/code> &mdash; N&uacute;mero de entidades pendientes de procesar.<\/li>\r\n<\/ul>\r\n<p>Y tambi&eacute;n a <b>propiedades de recursos<\/b>:<\/p>\r\n<ul>\r\n    <li><code>R$(recurso, propiedad)<\/code> &mdash; Devuelve el valor de la propiedad solicitada.<\/li>\r\n    <li><code>R$(almacen1, IN)<\/code> &mdash; Entradas al <code>STORAGE<\/code>.<\/li>\r\n    <li><code>R$(facility3, ENTRIES)<\/code> &mdash; N&uacute;mero de usos de la <code>FACILITY<\/code>.<\/li>\r\n    <li><code>R$(market1, QUEUE)<\/code> &mdash; Longitud actual de la cola.<\/li>\r\n<\/ul>\r\n<p>Los SNA no son instrucciones: son expresiones. <br>\r\nSe eval&uacute;an en cualquier parte donde se espere un valor. Esto los convierte en una herramienta esencial para tomar decisiones, construir mensajes, hacer c&aacute;lculos o acceder a estructuras complejas.<\/p>\r\n<p>En el ejemplo, se usan dos SNA muy representativos:<\/p>\r\n<ul>\r\n    <li><code>AC1$<\/code> el tiempo actual del sistema.<\/li>\r\n    <li><code>R$(VENTANILLA,QUEUE)<\/code>: estado actual de la cola del recurso.<\/li>\r\n    <li><code>D$N<\/code>: devuelve el n&uacute;mero identificador de la entidad virtual actual. En este caso, lo almacenamos en una variable <code>numero<\/code> mediante un <code>ASSIGN<\/code>, y luego accedemos a su valor con <code>P$numero<\/code>.<\/li>\r\n<\/ul>\r\n<p>Esto muestra c&oacute;mo los SNA pueden ser usados tanto para obtener datos del sistema como para manipular variables propias de cada entidad, y c&oacute;mo se integran f&aacute;cilmente con los comandos visuales como <code>move<\/code>.<\/p>\r\n<p>Por &uacute;ltimo tenemos el SNA <code>SYS$<\/code> que contiene variables b&aacute;sicas del sistema en un objeto como los datos de fecha y hora:<\/p>\r\n<pre>\r\nASSIGN sys, SYS$\r\n&nbsp; MOVE {name: REALTIME1, text:&quot;P$(sys.date.year)\/P$(sys.date.month)\/P$(sys.date.day)&quot;}\r\n&nbsp; MOVE {name: REALTIME2, text:&quot;P$(sys.date.hour):P$(sys.date.min):P$(sys.date.sec)&quot;}<\/pre>\r\n<p><b>Acceso directo a datos de entidades:<\/b><\/p>\r\n<p>El SNA <strong data-start=\"2235\" data-end=\"2241\">D$<\/strong> permite consultar propiedades internas de cualquier entidad del sistema.<br data-start=\"2314\" data-end=\"2317\">\r\nSu sintaxis general es:<\/p>\r\n<pre>\r\nD$(propiedad)              ; entidad actual\r\nD$(propiedad, numeroEntidad)   ; entidad espec&iacute;fica<\/pre>\r\n<p>Si se omite el segundo par&aacute;metro, se asume <code data-start=\"2486\" data-end=\"2491\">D$N<\/code> (la entidad actual).<\/p>\r\n<p data-start=\"2569\" data-end=\"2683\">Todas estas propiedades se aplican tanto a la entidad actual como a cualquier otra entidad si se indica su n&uacute;mero:<\/p>\r\n<div class=\"TyagGW_tableContainer\">\r\n<div tabindex=\"-1\" class=\"group TyagGW_tableWrapper flex w-fit flex-col-reverse\">\r\n<table data-start=\"2685\" data-end=\"3488\" class=\"w-fit min-w-(--thread-content-width)\">\r\n    <thead data-start=\"2685\" data-end=\"2712\">\r\n        <tr data-start=\"2685\" data-end=\"2712\">\r\n            <th data-start=\"2685\" data-end=\"2697\" data-col-size=\"sm\">Propiedad<\/th>\r\n            <th data-start=\"2697\" data-end=\"2712\" data-col-size=\"md\">Descripci&oacute;n<\/th>\r\n        <\/tr>\r\n    <\/thead>\r\n    <tbody data-start=\"2740\" data-end=\"3488\">\r\n        <tr data-start=\"2740\" data-end=\"2787\">\r\n            <td data-start=\"2740\" data-end=\"2756\" data-col-size=\"sm\"><strong data-start=\"2742\" data-end=\"2747\">N<\/strong>, <strong data-start=\"2749\" data-end=\"2755\">ID<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2756\" data-end=\"2787\">Identificador de la entidad<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2788\" data-end=\"2827\">\r\n            <td data-start=\"2788\" data-end=\"2805\" data-col-size=\"sm\"><strong data-start=\"2790\" data-end=\"2796\">M0<\/strong>, <strong data-start=\"2798\" data-end=\"2804\">M1<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2805\" data-end=\"2827\">Par&aacute;metros M0 y M1<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2828\" data-end=\"2868\">\r\n            <td data-start=\"2828\" data-end=\"2840\" data-col-size=\"sm\"><strong data-start=\"2830\" data-end=\"2839\">BLOCK<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2840\" data-end=\"2868\">&Iacute;ndice del bloque actual<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2869\" data-end=\"2908\">\r\n            <td data-start=\"2869\" data-end=\"2880\" data-col-size=\"sm\"><strong data-start=\"2871\" data-end=\"2879\">STEP<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2880\" data-end=\"2908\">N&uacute;mero de paso ejecutado<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2909\" data-end=\"2975\">\r\n            <td data-start=\"2909\" data-end=\"2928\" data-col-size=\"sm\"><strong data-start=\"2911\" data-end=\"2927\">RESOURCETIME<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2928\" data-end=\"2975\">Tiempo dentro de la FACILITY\/STORAGE actual<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2976\" data-end=\"3027\">\r\n            <td data-start=\"2976\" data-end=\"2995\" data-col-size=\"sm\"><strong data-start=\"2978\" data-end=\"2994\">RESOURCENAME<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"2995\" data-end=\"3027\">Nombre del recurso que ocupa<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"3028\" data-end=\"3090\">\r\n            <td data-start=\"3028\" data-end=\"3047\" data-col-size=\"sm\"><strong data-start=\"3030\" data-end=\"3046\">ADVANCESTART<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"3047\" data-end=\"3090\">Tiempo en que comenz&oacute; el ADVANCE actual<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"3091\" data-end=\"3143\">\r\n            <td data-start=\"3091\" data-end=\"3110\" data-col-size=\"sm\"><strong data-start=\"3093\" data-end=\"3109\">ADVANCELAPSE<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"3110\" data-end=\"3143\">Duraci&oacute;n restante del ADVANCE<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"3144\" data-end=\"3190\">\r\n            <td data-start=\"3144\" data-end=\"3166\" data-col-size=\"sm\"><strong data-start=\"3146\" data-end=\"3151\">X<\/strong>, <strong data-start=\"3153\" data-end=\"3158\">Y<\/strong>, <strong data-start=\"3160\" data-end=\"3165\">Z<\/strong><\/td>\r\n            <td data-start=\"3166\" data-end=\"3190\" data-col-size=\"md\">Coordenadas visuales<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"3191\" data-end=\"3230\">\r\n            <td data-start=\"3191\" data-end=\"3199\" data-col-size=\"sm\"><strong data-start=\"3193\" data-end=\"3198\">T<\/strong><\/td>\r\n            <td data-col-size=\"md\" data-start=\"3199\" data-end=\"3230\">Tiempo absoluto de creaci&oacute;n<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"3357\" data-end=\"3426\">\r\n            <td data-start=\"3357\" data-end=\"3366\" data-col-size=\"sm\"><strong data-start=\"3359\" data-end=\"3365\">CX<\/strong><\/td>\r\n            <td data-start=\"3366\" data-end=\"3426\" data-col-size=\"md\">Contexto de la entidad<\/td>\r\n        <\/tr>\r\n    <\/tbody>\r\n<\/table>\r\n<\/div>\r\n<\/div>\r\n<p>De forma especial se puede verificar la existencia de una entidad dentro del mismo SNA D$:<\/p>\r\n<pre>\r\nif (D$(EXIST,1000)==1)\r\n    ; la entidad 1000 est&aacute; activa\r\nendif<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "289",
                "nombre": "Funciones nativas",
                "texto": "POSITION {NAME:POS1,X:300,Y:100}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text9,Type:TEXT,X:324,Y:X$posY - 160}\r\nGraphic {NAME:Text10,Type:TEXT,X:324,Y:X$posY - 180}\r\nGraphic {NAME:Text11,Type:TEXT,X:324,Y:X$posY - 200}\r\nGraphic {NAME:Text12,Type:TEXT,X:324,Y:X$posY - 220}\r\nGraphic {NAME:Text13,Type:TEXT,X:324,Y:X$posY - 240}\r\nGraphic {NAME:Text14,Type:TEXT,X:324,Y:X$posY - 260}\r\n\r\nSTART 1\r\n\r\nGENERATE 1,0,0,1 {NAME:GEN1,X:100,Y:100}\r\n\r\n; Variables base\r\nASSIGN saludo, \"Hola\"\r\nASSIGN complemento, \"mundo\"\r\nASSIGN mensajeOriginal, \"   Hola Mundo GPSS+   \"\r\nASSIGN nombres, [\"Ana\", \"Luis\", \"Eva\"]\r\nASSIGN otrosNombres, [\"Sonia\", \"David\"]\r\nASSIGN textoConSeparador, \"uno,dos,tres,cuatro,cinco\"\r\n\r\n\r\n\r\n; Métodos de arrays\r\nassign.push nombres, \"Ricardo\" ; [\"Ana\", \"Luis\", \"Eva\" \"Ricardo\"]\r\nassign.unshift nombres, \"Laura\" ; [\"laura\", \"Ana\", \"Luis\", \"Eva\" \"Ricardo\"]\r\nassign.extend nombres, V$otrosNombres ; [\"laura\", \"Ana\", \"Luis\", \"Eva\", \"Ricardo\",\"Sonia\", \"David\"]\r\nassign.slice nombres, {START:1,END:6} ; [\"Ana\", \"Luis\", \"Eva\", \"Ricardo\",\"Sonia\"]\r\n\r\n; String methods\r\nassign.join mensajeFinal, {DATA:[\"P$saludo\", \" \", \"P$complemento\", \"!\"]} ; \"Hola Mundo!\"\r\nassign.join nombresTexto, {DATA:V$nombres,SEP:\" - \"} ; \"Ana - Luis - Eva\"\r\nassign.split palabras, {DATA:V$textoConSeparador, SEP: \",\"}; [\"uno\",\"dos\",\"tres\",\"cuatro\",\"cinco\"]\r\nassign.trim mensajeOriginal ; \"Hola Mundo GPSS+\"\r\nassign.LENGTH mensajeOriginal, largoMensaje ; 16\r\n\r\n; Objetos\r\nASSIGN datos, {nombre:\"Juan\", edad:25}\r\nASSIGN nuevosDatos, {ciudad:\"Madrid\", edad:30}\r\nassign.merge datos, V$nuevosDatos\r\nassign.delete datos, ciudad\r\n\r\n; Numérico\r\nASSIGN contador, 10\r\nassign.inc contador,-5\r\nassign.inc contador\r\n\r\n; Claves\r\nASSIGN alumnos, {ana:{edad:20}, luis:{edad:25}}\r\nassign.keys alumnos, clavesAlumnos\r\n\r\n; Rutas de acceso\r\nASSIGN miObjeto, {}\r\nASSIGN miObjeto.clave1, 123\r\n;ASSIGN miObjeto.clave2.otroNivel, \"error\" -> error por no existir\r\nASSIGN miArray, []\r\nASSIGN miArray.2, 99\r\nASSIGN miArray.3, []\r\nASSIGN.push miArray.3, 199\r\n\r\n\r\n; Mostrar resultados\r\nmove {name:Text1, text:\"Mensaje final: P$mensajeFinal\"}\r\nmove {name:Text2, text:\"Nombres (2): P$(nombres.2) LENGTH: VD$(nombres,LENGTH)\"}\r\nmove {name:Text3, text:\"Nombres texto: P$nombresTexto\"}\r\nmove {name:Text4, text:\"Palabras[1]: P$(palabras.1)\"}\r\nmove {name:Text5, text:\"Texto trimmed: P$mensajeOriginal\"}\r\nmove {name:Text6, text:\"Largo mensaje: P$largoMensaje\"}\r\nmove {name:Text7, text:\"datos.edad: P$(datos.edad)\"}\r\nmove {name:Text8, text:\"datos.ciudad: P$(datos.ciudad)\"} ; debería estar vacío\r\nmove {name:Text9, text:\"Contador final: P$contador\"}\r\nmove {name:Text10, text:\"clavesAlumnos[1]: P$(clavesAlumnos.1)\"}\r\nmove {name:Text11, text:\"Tipo de nombres: VD$(nombres,TYPEOF)\"}\r\nmove {name:Text12, text:\"Alumnos tiene luis?: VD$(alumnos,HASKEY,luis)\"}\r\nmove {name:Text13, text:\"miObjeto.clave1: P$(miObjeto.clave1)\"}\r\nmove {name:Text14, text:\"miArray.3.0: P$(miArray.3.0)\"}\r\n\r\n\r\n\r\nADVANCE 100,0 {TO:POS1}\r\nENDGENERATE 1",
                "descripcion": "<p>Las funciones nativas permiten operar directamente sobre el contenido de una variable, sea num&eacute;rica, de texto, array u objeto. Algunas funciones modifican su contenido.<\/p>\r\n<p>El formato es siempre el mismo:<\/p>\r\n<p>ASSIGN.&lt;FUNCION&gt; nombreVariableDestino, par&aacute;metro [,n&uacute;mero entidad destino]<br>\r\nSAVEVALUE.&lt;FUNCION&gt; nombreVariableDestino, par&aacute;metro<\/p>\r\n<p>Si la variable no existe, se crea.<\/p>\r\n<p>Es muy importante decir que los objetos JSON &quot;array&quot; y &quot;object&quot; solo se pueden crear bajo estos dos bloques y el comando INITIAL. Cualquier otro COMANDO o BLOQUE que requiera par&aacute;metros como objeto o array recibir&aacute; el objeto ya creado bajo el SNA V$(variable).<\/p>\r\n<pre>\r\nCALL fun, V$(estructura)\r\nCALL fun, P$(variable)\r\n<strike>CALL fun, {nombre_&quot;Antonio&quot;} <\/strike> ; no se interpretan los objetos fuera de ASSIGN, SAVEVALUE &oacute; INITIAL\r\n<strike>CALL fun, [1,2,V$(otraLista)] <\/strike> ; no se interpretan los objetos fuera de ASSIGN, SAVEVALUE &oacute; INITIAL<\/pre>\r\n<h3>Funciones:<\/h3>\r\n<p><b>1. Variables num&eacute;ricas<\/b><\/p>\r\n<ul>\r\n    <li><code>.INC valor<\/code> &ndash; incrementa el valor actual, uno por defecto. <i>Modifica su contenido<\/i> <br>\r\n    <b>Ejemplos:<\/b>\r\n    <pre>\r\nASSIGN.INC contador\r\nASSIGN.INC contador,10\r\nASSIGN.INC miObjeto.puntuacion <\/pre>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>2. Variables string<\/b><\/p>\r\n<ul>\r\n    <li><code>.TRIM<\/code> &ndash; elimina espacios al principio y al final <i>Modifica su contenido<\/i>\r\n    <pre>\r\nASSIGN.TRIM miCadena <\/pre>\r\n    <\/li>\r\n    <li><code>.LENGTH<\/code> &ndash; devuelve la longitud del string <i>Devuelve el resultado en otro assign<\/i>\r\n    <pre>\r\n ASSIGN.LENGTH miCadena, longitud <\/pre>\r\n    <\/li>\r\n    <li><code>.JOIN<\/code> &ndash; une los elementos en un string <i>Devuelven un nuevo valor<br>\r\n    <\/i>\r\n    <pre>\r\nASSIGN.JOIN cadenaFinal, {DATA:[&quot;uno&quot;,&quot;dos&quot;,&quot;tres&quot;], SEP:&quot; - &quot;} \r\nASSIGN.JOIN cadenaFinal, {DATA:V$miArray, SEP:&quot; - &quot;} <\/pre>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>3. Variables array<\/b><\/p>\r\n<ul>\r\n    <li><code>.PUSH valor<\/code> &ndash; a&ntilde;ade al final  <code><br>\r\n    .UNSHIFT valor<\/code> &ndash; a&ntilde;ade al inicio  <code><br>\r\n    .EXTEND array<\/code> &ndash; a&ntilde;ade los elementos de otro array\r\n    <pre>\r\nASSIGN.PUSH miArray, 45 ; [45]\r\nASSIGN.PUSH miArray, {edad:45} ; [45,{edad:45}]\r\nASSIGN.UNSHIFT miArray, &quot;inicio&quot; ; [&quot;inicio&quot;,45,{edad:45}]\r\nASSIGN.EXTEND miArray, V$otroArray ; [&quot;inicio&quot;,45,{edad:45},&quot;otroArray_a&quot;,&quot;otroArray_b&quot;]<\/pre>\r\n    <\/li>\r\n    <li><code>.SPLIT<\/code> &ndash; divide un string por separador y lo convierte en array&nbsp;\r\n    <pre>\r\nASSIGN.SPLIT resultado, {DATA:&quot;uno,dos,tres&quot;, SEP:&quot;,&quot;} <\/pre>\r\n    <\/li>\r\n    <li><code>.SLICE<\/code>&ndash; tona una secci&oacute;n de un array entre START y END (no incluido)&nbsp;\r\n    <pre>\r\nASSIGN myArray [0,1,2,3,4]\r\nASSIGN.SLICE myArray, {START:1,END:3} ; [1,2]<\/pre>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>4. Variables objeto<\/b><\/p>\r\n<ul>\r\n    <li><code>.MERGE objeto<\/code> &ndash; fusiona claves desde otro objeto  <code><br>\r\n    .DELETE ruta<\/code> &ndash; elimina una propiedad del objeto <i>Modifican su contenido<\/i>\r\n    <pre>\r\nASSIGN.MERGE datos, V$nuevosDatos \r\nASSIGN.DELETE datos, direccion.calle <\/pre>\r\n    <\/li>\r\n    <li><code>.KEYS<\/code> &ndash; obtiene una lista con los nombres de clave <i>Devuelve el resultado en otra variable<\/i>\r\n    <pre>\r\nASSIGN.KEYS datos, claves <\/pre>\r\n    <\/li>\r\n<\/ul>\r\n<p><b><\/b><\/p>\r\n<h3>Acceso por rutas<\/h3>\r\n<p data-start=\"265\" data-end=\"485\">Adem&aacute;s de asignar un valor completo a una variable, el DSL permite <strong data-start=\"332\" data-end=\"374\">asignar &uacute;nicamente a una parte interna<\/strong> de un objeto o un array mediante <em data-start=\"408\" data-end=\"415\">rutas<\/em>.<br data-start=\"416\" data-end=\"419\">\r\nUna ruta es una secuencia de claves o &iacute;ndices separada por puntos:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre><b><\/b>ASSIGN variable.clave , 3\r\nASSIGN variable.clave.subclave , 3\r\nASSIGN variable.3 , 3\r\nASSIGN variable.3.otroNivel , 3\r\n\r\n<\/pre>\r\n<p><b>Rutas en objetos<\/b><\/p>\r\n<p data-start=\"590\" data-end=\"732\">Las rutas en objetos <strong data-start=\"611\" data-end=\"659\">no crean niveles intermedios autom&aacute;ticamente<\/strong>.<br data-start=\"660\" data-end=\"663\">\r\nSi una ruta no existe, se genera un error y la simulaci&oacute;n se detiene:<\/p>\r\n<pre>\r\nASSIGN miObjeto, {}\r\nASSIGN miObjeto.clave1, 123        ; OK\r\nASSIGN miObjeto.clave2.otroNivel, 999  ; ERROR &rarr; clave2 no existe<\/pre>\r\n<p><b><\/b>Los objetos deben existir previamente en cada nivel de la ruta:<\/p>\r\n<pre>\r\nASSIGN miObjeto, {clave2:{}}       ; Ahora s&iacute; existe clave2\r\nASSIGN miObjeto.clave2.otroNivel, 999<\/pre>\r\n<p><b>Rutas en arrays<\/b><\/p>\r\n<p data-start=\"1073\" data-end=\"1163\">Los arrays <strong data-start=\"1084\" data-end=\"1139\">s&iacute; permiten acceder directamente a cualquier &iacute;ndice<\/strong>, incluso si est&aacute; vac&iacute;o:<\/p>\r\n<pre>\r\nASSIGN miArray, []\r\nASSIGN miArray.2, 99     ; Crea MIARRAY como [ , , 99 ]<\/pre>\r\n<p><b><\/b>Adem&aacute;s, es posible operar sobre una ruta interna con funciones nativas:<\/p>\r\n<pre>\r\nASSIGN miArray.3, []\r\nASSIGN.PUSH miArray.3, 199<\/pre>\r\n<h3><b>Los SNA asociados&nbsp;<code>VD$<\/code><\/b><\/h3>\r\n<p>Algunos datos son de acceso directo a trav&eacute;s del SNA asociado para, por ejemplo, validar la existencia de un elemento extraido de <code>.POP<\/code> antes de continuar<\/p>\r\n<ul>\r\n    <li><code>VD$(ruta)<\/code> &ndash; Devuelve el valor bruto sin procesar. Similar a <code>V$<\/code> pero dentro de una expresi&oacute;n.<\/li>\r\n    <li><code>VD$(ruta,LENGTH)<\/code> &ndash; Longitud del contenido:\r\n    <ul>\r\n        <li>Cadenas o arrays: n&uacute;mero de elementos.<\/li>\r\n        <li>Objetos: n&uacute;mero de claves.<\/li>\r\n        <li>N&uacute;meros: n&uacute;mero de d&iacute;gitos.<\/li>\r\n    <\/ul>\r\n    <\/li>\r\n    <li><code>VD$(ruta,TYPEOF)<\/code> &ndash; Tipo de dato en forma de texto: <code>&quot;STRING&quot;<\/code>, <code>&quot;NUMBER&quot;<\/code>, <code>&quot;ARRAY&quot;<\/code>, <code>&quot;OBJECT&quot;<\/code>, etc.<\/li>\r\n    <li><code>VD$(ruta,ISEMPTY)<\/code> &ndash; Devuelve <code>&quot;1&quot;<\/code> si el valor est&aacute; vac&iacute;o:\r\n    <ul>\r\n        <li>&quot;&quot; (cadena vac&iacute;a), array vac&iacute;o, objeto sin claves, n&uacute;mero 0.<\/li>\r\n        <li>Devuelve <code>&quot;0&quot;<\/code> en cualquier otro caso.<\/li>\r\n    <\/ul>\r\n    <\/li>\r\n    <li><code>VD$(ruta,EXIST)<\/code> &ndash; Comprueba si la ruta existe completamente sin errores.\r\n    <ul>\r\n        <li>Devuelve <code>&quot;1&quot;<\/code> si es v&aacute;lida, <code>&quot;0&quot;<\/code> si no lo es.<\/li>\r\n    <\/ul>\r\n    <\/li>\r\n    <li><code>VD$(ruta,HASKEY,clave)<\/code> &ndash; Comprueba si un objeto tiene una determinada clave. Devuelve <code>&quot;1&quot;<\/code> o <code>&quot;0&quot;<\/code>.<\/li>\r\n    <li><code>VD$(ruta,INCLUDES,valor)<\/code> &ndash; Comprueba si un array o cadena incluye un valor dado. Devuelve <code>&quot;1&quot;<\/code> o <code>&quot;0&quot;<\/code>.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "288",
                "nombre": "Acceso a variables de otras entidades",
                "texto": "\/* Acceso cruzado entre entidades usando P$ y ASSIGN destino *\/\r\n\r\nPOSITION {NAME:POS_MOD,   X:286, Y:244}\r\nPOSITION {NAME:POS_END,   X:741, Y:233, type:terminate}\r\n\r\nGraphic {NAME:T1, type:TEXT, X:455, Y:300}\r\nGraphic {NAME:T2, type:TEXT, X:455, Y:188}\r\n\r\nSTART 60   ; habrá 6 entidades (3 de cada tipo)\r\n\r\n\/* --- ENTIDAD TIPO A --- *\/\r\nGENERATE 30,0,15 {NAME:GA, X:100, Y:70, ECOLOR:#ff3333, subtitle:\"A\"}\r\n    ASSIGN miValor, 100\r\n    mod {subtitle:\"Mi valor P$miValor\"}\r\n    \r\n\r\n    ADVANCE 20 {to:POS_MOD}\r\n\r\n    ; Si existe la entidad B (2, 4, 6...)\r\n    IF (D$(EXIST, D$N+1)==1)\r\n    \tASSIGN nEntidadDestino,D$N+1\r\n        ASSIGN miValor, P$(miValor)+10, P$nEntidadDestino   ; A incrementa el valor de B\r\n        MOVE {name:T1, text:\"Cambio el valor de P$nEntidadDestino a: [P$(miValor,P$nEntidadDestino)]\"}\r\n    ENDIF\r\n\r\n    ADVANCE 50 {to:POS_END}\r\nENDGENERATE 3\r\n\r\n\/* --- ENTIDAD TIPO B --- *\/\r\nGENERATE 30,0 {NAME:GB, X:103, Y:407, ECOLOR:#3366ff, subtitle:\"B\"}\r\n    ASSIGN miValor, 200\r\n    mod {subtitle:\"Mi valor P$miValor\"}\r\n\r\n    ADVANCE 20 {to:POS_MOD}\r\n\r\n    ; Si existe la entidad A (1, 3, 5...)\r\n    IF (D$(EXIST, D$N-1)==1)\r\n    \tASSIGN nEntidadDestino,D$N-1\r\n        ASSIGN miValor, P$(miValor)-5, P$nEntidadDestino   ; B reduce el valor de A\r\n        MOVE {name:T2, text:\"Cambio el valor de P$nEntidadDestino a: [P$(miValor,P$nEntidadDestino)]\"}\r\n    ENDIF\r\n\r\n    ADVANCE 50 {to:POS_END}\r\nENDGENERATE 3\r\n",
                "descripcion": "<h3>Acceso a variables de otras entidades<\/h3>\r\n<p>Adem&aacute;s de trabajar con variables propias o globales,&nbsp;GPSS-Plus permite leer y escribir variables <code><strong data-start=\"312\" data-end=\"322\">ASSIGN<\/strong> <\/code>de cualquier entidad viva del sistema.&nbsp;<\/p>\r\n<p><b>Lectura a trav&eacute;s del SNA:<\/b><\/p>\r\n<p>El SNA habitual <code data-start=\"413\" data-end=\"417\">P$<\/code> admite un segundo par&aacute;metro que indica el n&uacute;mero de entidad objetivo:<\/p>\r\n<pre>\r\n P$(nombreVariable, n&uacute;meroEntidad) <\/pre>\r\n<p>Esto permite consultar valores internos de otra entidad sin necesidad de copiarlos a un SAVEVALUE.<\/p>\r\n<pre>\r\n move {name:text5, text:&quot;La entidad 2 tiene: P$(unNumeroPrivado,2)&quot;} <\/pre>\r\n<p>Esto accede directamente a la variable <code>unNumeroPrivado<\/code>&nbsp;activa en la entidad n&uacute;mero 2.<\/p>\r\n<p><b>Escritura en otra entidad:<\/b><\/p>\r\n<p>Mediante un tercer par&aacute;metro en la instrucci&oacute;n&nbsp;<code>ASSIGN<\/code>, podemos guardar informaci&oacute;n en la entidad deseada:<\/p>\r\n<pre>\r\n ASSIGN unNumero, 123, 1\r\n<\/pre>\r\n<p><b>Comprobaci&oacute;n de la existencia de la entidad:<\/b><\/p>\r\n<p>Se puede consultar la existencia con el SNA espec&iacute;fico:<\/p>\r\n<pre>\r\nif (D$(EXIST,1000)==1)<\/pre>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "280",
                "nombre": "Estructuras de control",
                "texto": "\/* Estructuras de Control: FOREACH y REPEAT\r\nIteración sobre arrays, objetos y recursos.\r\n*\/\r\n\r\nPOSITION {NAME:POS1,X:651,Y:480}\r\ninitial posY,400\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160,color:#000000}\r\n\r\nFacility {NAME:Facility1,X:382,Y:483,capacity:3}\r\n\r\n\r\nSTART 100\r\n\r\nGENERATE 10,0 {name:GEN1,X:110,Y:477}\r\n\r\nassign edad,2\r\nassign nombre,\"Eva\"\r\n\r\n; IF (P$nombre==\"Eva\") -> Expresión incorrecta, faltan las comillas\r\nIF (\"P$nombre\"==\"Eva\")\r\n\tmove {name:Text1, text:\"Expresiones correctas\"}\r\nendif\r\n\r\nIF (P$edad > 18 || V$nombre==\"Eva\")\r\n\tmove {name:Text1, text:\"Expresiones correctas\"}\r\nendif\r\n\r\n\r\nASSIGN intentos, 0\r\nmove {name:Text1, text:\"Color actual: P$valor\"}\r\n\r\n; FOREACH: recorrer todos los colores\r\nassign gustos_luis,\"\"\r\nASSIGN persona, {nombre:\"Luis\"\r\n\t\t, gustos:[\"leer\", \"música\", \"viajar\", \"dormir\"]\r\n        , ciudad:\"Madrid\"\r\n        , pais:\"España\"\r\n        }\r\nFOREACH gusto, IN, V$(persona.gustos)\r\n    assign gustos_luis,\"P$gustos_luis P$gusto\"\r\nENDFOREACH\r\nmove {name:Text1, text:\"Gustos de Luis: P$gustos_luis\"}\r\n\r\nassign claves_valores,\"\"\r\nFOREACH clave, IN_OBJECT, V$(persona)\r\n    assign claves_valores,\"P$(claves_valores) \\n P$clave  P$(persona.P$clave)\"\r\nENDFOREACH\r\n\r\nmove {name:Text6, text:\"Claves y valores de persona:\\n P$(claves_valores)\"}\r\n\r\nassign entidades_cola,\"\"\r\nFOREACH entidad, IN_QUEUE, Facility1\r\n    assign entidades_cola,\"P$entidades_cola P$entidad\"\r\nENDFOREACH\r\nmove {name:Text3, text:\"entidades en cola: P$entidades_cola\"}\r\n\r\n\r\nassign entidades_dentro,\"\"\r\nFOREACH entidad, IN_RESOURCE, Facility1\r\n    assign entidades_dentro,\"P$entidades_dentro P$entidad\"\r\nENDFOREACH\r\nmove {name:Text4, text:\"entidades dentro: P$entidades_dentro\"}\r\n\r\nassign intento, 0\r\nassign maximo, 3\r\n\r\nassign intentos,\"\"\r\nREPEAT\r\n    assign.inc intento\r\n    assign intentos,\"P$intentos P$intento\"\r\nUNTIL (P$intento >= P$maximo)\r\nmove {name:Text5, text:\"intentos: P$intentos\"}\r\n\r\nadvance 10,0 {to:Facility1}\r\nSEIZE Facility1\r\nadvance 20,30\r\nRELEASE Facility1\r\n\r\nADVANCE 100,0 {TO:POS1}\r\nENDGENERATE 1\r\n",
                "descripcion": "<p>Las estructuras de control permiten a las entidades tomar decisiones, repetir bloques de c&oacute;digo o recorrer colecciones. GPSS-Plus incorpora estas estructuras con una sintaxis simple y adaptada al paradigma declarativo del lenguaje.<\/p>\r\n<p><b>1. <code>IF<\/code> \/ <code>ELSE<\/code> \/ <code>ENDIF<\/code><\/b><\/p>\r\n<p>Permite evaluar una condici&oacute;n y ejecutar bloques alternativos seg&uacute;n el resultado.<\/p>\r\n<pre>\r\nIF (P$edad &gt; 18 || &quot;P$nombre&quot;==&quot;Eva&quot;)\r\n&nbsp; &nbsp; move {name:text1,text:&quot;Mayor de edad o es Eva&quot;}\r\nELSE\r\n&nbsp; &nbsp; move {name:text1,text:&quot;Menor de edad o no es Eva&quot;}\r\nENDIF\r\n<\/pre>\r\n<ul>\r\n    <li>La condici&oacute;n debe estar entre par&eacute;ntesis.<\/li>\r\n    <li>Se pueden usar operadores l&oacute;gicos (<code data-start=\"961\" data-end=\"965\">&amp;&amp;<\/code>, <code data-start=\"967\" data-end=\"971\">||<\/code>) y comparaciones (<code data-start=\"990\" data-end=\"994\">==<\/code>, <code data-start=\"996\" data-end=\"1000\">!=<\/code>, <code data-start=\"1002\" data-end=\"1005\">&gt;<\/code>, <code data-start=\"1007\" data-end=\"1010\">&lt;<\/code>, <code data-start=\"1012\" data-end=\"1016\">&gt;=<\/code>, <code data-start=\"1018\" data-end=\"1022\">&lt;=<\/code>).<\/li>\r\n    <li>Todo se eval&uacute;a como una &uacute;nica expresi&oacute;n dentro de los par&eacute;ntesis.<\/li>\r\n<\/ul>\r\n<p><b>2. <code>FOREACH<\/code> \/ <code>ENDFOREACH<\/code><\/b><\/p>\r\n<pre>\r\nASSIGN listaAlumnos,[{nombre:&quot;Ana&quot;},{nombre:&quot;Luis&quot;}]\r\nFOREACH alumno IN V$listaAlumnos\r\n    move {name:text1,text:&quot;Alumno: P$alumno.nombre&quot;}\r\nENDFOREACH<\/pre>\r\n<ul>\r\n    <li>Permite iterar sobre los elementos de una colecci&oacute;n (array u objeto).<\/li>\r\n    <li>La variable que representa cada elemento debe ser un ASSIGN.<\/li>\r\n    <li>Se puede iterar sobre arrays ([1,2,3]) o arrays de objetos ([{nombre:&quot;Ana&quot;}, {nombre:&quot;Luis&quot;}]).<\/li>\r\n    <li>La variable usada en la iteraci&oacute;n (por ejemplo <code data-start=\"1395\" data-end=\"1402\">color<\/code>, <code data-start=\"1404\" data-end=\"1413\">entidad<\/code>, <code data-start=\"1415\" data-end=\"1422\">clave<\/code>) debe ser un <code data-start=\"1436\" data-end=\"1444\">ASSIGN<\/code>, creado autom&aacute;ticamente si no existe.<\/li>\r\n    <li>El tercer par&aacute;metro de <code>IN<\/code> y <code>IN_OBJECT<\/code> (el origen de los datos) <strong data-start=\"1530\" data-end=\"1564\">debe ser una referencia v&aacute;lida<\/strong>&nbsp;por su SNA&nbsp;<code data-start=\"1582\" data-end=\"1593\">V$(ruta)<\/code><\/li>\r\n<\/ul>\r\n<h4 data-start=\"526\" data-end=\"552\"><strong data-start=\"531\" data-end=\"552\">Modos disponibles<\/strong><\/h4>\r\n<ul>\r\n    <li><strong data-start=\"556\" data-end=\"562\">IN<\/strong>: Itera sobre un array.<\/li>\r\n    <li><strong data-start=\"718\" data-end=\"731\">IN_OBJECT<\/strong>: Itera sobre las claves de un objeto.<\/li>\r\n    <li><strong data-start=\"1118\" data-end=\"1133\">IN_RESOURCE<\/strong>: Itera sobre las entidades actualmente utilizando el recurso (<code data-start=\"967\" data-end=\"977\">FACILITY<\/code>, <code data-start=\"979\" data-end=\"988\">STORAGE<\/code>, etc.).<\/li>\r\n    <li><strong data-start=\"901\" data-end=\"913\">IN_QUEUE<\/strong>: Itera sobre las entidades en la cola de un recurso (<code data-start=\"967\" data-end=\"977\">FACILITY<\/code>, <code data-start=\"979\" data-end=\"988\">STORAGE<\/code>, etc.).<\/li>\r\n<\/ul>\r\n<pre>\r\nFOREACH color, IN, V$(persona.colores)\r\n&nbsp; &nbsp; assign resultado, &quot;P$color&quot;\r\nENDFOREACH\r\n\r\nFOREACH clave, IN_OBJECT, V$(persona)\r\n&nbsp; &nbsp; assign resultado, &quot;P$clave&quot;\r\nENDFOREACH\r\n\r\nFOREACH entidad, IN_RESOURCE, Ventanilla\r\n&nbsp; &nbsp; assign ids, &quot;P$entidad&quot;\r\nENDFOREACH\r\n\r\nFOREACH entidad, IN_QUEUE, Ventanilla\r\n&nbsp; &nbsp; assign ids, &quot;P$entidad&quot;\r\nENDFOREACH<\/pre>\r\n<p data-start=\"556\" data-end=\"598\"><b><br>\r\n3. <code>REPEAT<\/code> \/ <code>UNTIL<\/code><\/b><\/p>\r\n<pre>\r\nASSIGN intento, 0\r\n\r\nREPEAT\r\n    ASSIGN.INC intento\r\n    move {name:text1,text:&quot;Intento n&uacute;mero: P$intento&quot;}\r\nUNTIL (P$intento &gt;= 3)\r\n\r\n<\/pre>\r\n<ul>\r\n    <li>Repite un bloque hasta que una condici&oacute;n se cumpla.<\/li>\r\n    <li>Se ejecuta al menos una vez.<\/li>\r\n    <li>La condici&oacute;n entre par&eacute;ntesis se eval&uacute;a tras cada repetici&oacute;n.<\/li>\r\n    <li>El bloque entre REPEAT y UNTIL puede contener cualquier otra instrucci&oacute;n GPSS-Plus, incluso estructuras anidadas.<\/li>\r\n<\/ul>\r\n<p><b>4. <code>SWITCH<\/code> \/ <code>CASE<\/code>&nbsp;<\/b><b>\/ <code>ENDCASE<\/code><\/b><b>\/ <code>ENDSWITCH<\/code><\/b><\/p>\r\n<pre>\r\nSWITCH P$color\r\n\r\nCASE ==,&quot;rojo&quot;\r\n    move {name:text1,text:&quot;Color ROJO&quot;}\r\n\r\nCASE ==,&quot;azul&quot;\r\n    move {name:text1,text:&quot;Color AZUL&quot;}\r\n\r\nCASE ==,&quot;verde&quot;\r\n    move {name:text1,text:&quot;Color VERDE&quot;}\r\n\r\nENDCASE\r\n\r\nENDSWITCH<\/pre>\r\n<ul>\r\n    <li>Permite ejecutar bloques espec&iacute;ficos seg&uacute;n el valor de una expresi&oacute;n, similar a switch-case en otros lenguajes.<\/li>\r\n    <li>La expresi&oacute;n del SWITCH puede ser cualquier valor evaluable (n&uacute;mero, texto, etc.).<\/li>\r\n    <li>Cada CASE compara el valor con un operador (==, !=, &gt;, &lt;, etc.) y un segundo argumento.<\/li>\r\n    <li>Se ejecuta solo el primer CASE que cumpla la condici&oacute;n.<\/li>\r\n    <li>No hay necesidad de BREAK, ya que no contin&uacute;a evaluando los siguientes casos.<\/li>\r\n    <li>El bloque se cierra con ENDCASE.<\/li>\r\n<\/ul>\r\n<p><b>&nbsp;5. <code>WHILE <\/code>\/ <\/b><code><b>ENDWHILE<\/b><\/code><\/p>\r\n<pre>\r\nassign intentos, &quot;&quot;\r\nassign intento, 0\r\nassign maximo, 5\r\n\r\nWHILE (P$intento &lt; P$maximo)\r\n    assign.inc intento\r\n&nbsp; &nbsp; assign intentos, &quot;P$intentos P$intento&quot;\r\nENDWHILE\r\n\r\nmove {name:text1, text:&quot;Intentos: P$intentos&quot;}<\/pre>\r\n<p>&nbsp;<\/p>\r\n<ul>\r\n    <li>Ejecuta un bloque de c&oacute;digo <strong data-start=\"1230\" data-end=\"1242\">mientras<\/strong> se cumpla una condici&oacute;n. A diferencia de <code data-start=\"1284\" data-end=\"1292\">REPEAT<\/code>, la condici&oacute;n se eval&uacute;a <strong data-start=\"1317\" data-end=\"1344\">antes de cada iteraci&oacute;n<\/strong>.<\/li>\r\n    <li>La condici&oacute;n debe ir entre par&eacute;ntesis y puede usar expresiones con SNA (<code data-start=\"1448\" data-end=\"1452\">P$<\/code>, <code data-start=\"1454\" data-end=\"1459\">VD$<\/code>, <code data-start=\"1461\" data-end=\"1465\">D$<\/code>) y operadores l&oacute;gicos.<\/li>\r\n    <li>Si la condici&oacute;n es falsa al inicio, el bloque <strong data-start=\"1537\" data-end=\"1560\">no se ejecuta nunca<\/strong>.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "286",
                "nombre": "Subprocesos y llamadas",
                "texto": "<h3 data-start=\"626\" data-end=\"684\"><strong data-start=\"630\" data-end=\"684\">Subprocesos y llamadas en GPSS-Plus (introducci&oacute;n)<\/strong><\/h3>\r\n<p data-start=\"686\" data-end=\"983\">GPSS-Plus incorpora un sistema completo de subprocesos que no exist&iacute;a en GPSS cl&aacute;sico, gracias a la introducci&oacute;n de <strong data-start=\"802\" data-end=\"833\">pilas de ejecuci&oacute;n (stacks)<\/strong>.<br data-start=\"834\" data-end=\"837\">\r\nEsto permite que una entidad &mdash;o incluso el propio sistema&mdash; pueda invocar procedimientos independientes y retomar la ejecuci&oacute;n de forma controlada.<\/p>\r\n<p data-start=\"985\" data-end=\"1074\">Existen varios tipos de llamada, seg&uacute;n qui&eacute;n invoca y <strong data-start=\"1039\" data-end=\"1056\">qui&eacute;n ejecuta<\/strong> el procedimiento:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"1078\" data-end=\"1086\">CALL<\/strong> &mdash; Lo ejecuta <em data-start=\"1100\" data-end=\"1118\">la misma entidad<\/em> que lo invoca.<\/li>\r\n    <li><strong data-start=\"1138\" data-end=\"1148\">SIGNAL<\/strong> &mdash; Lo ejecuta <em data-start=\"1162\" data-end=\"1176\">otra entidad<\/em>, pero solo cuando la actual libera el turno.<\/li>\r\n    <li><strong data-start=\"1226\" data-end=\"1239\">SIGNALNOW<\/strong> &mdash; Lo ejecuta <em data-start=\"1253\" data-end=\"1282\">otra entidad inmediatamente<\/em>, interrumpiendo a la actual.<\/li>\r\n    <li><strong data-start=\"1316\" data-end=\"1327\">TIMEOUT<\/strong> &mdash; Lo ejecuta <em data-start=\"1341\" data-end=\"1367\">una entidad virtual (VE)<\/em> en un tiempo futuro.<\/li>\r\n    <li><strong data-start=\"1393\" data-end=\"1402\">ON_*<\/strong> &mdash; Lo ejecuta <em data-start=\"1416\" data-end=\"1424\">una VE<\/em> al producirse un evento en un recurso (SEIZE, RELEASE, ENTER...).<\/li>\r\n    <li><strong data-start=\"1495\" data-end=\"1504\">TIMER<\/strong> &mdash; Lo ejecuta <em data-start=\"1518\" data-end=\"1526\">una VE<\/em> peri&oacute;dicamente.<\/li>\r\n    <li><strong data-start=\"1547\" data-end=\"1558\">PRE_RUN<\/strong> &mdash; Lo ejecuta <em data-start=\"1572\" data-end=\"1580\">una VE<\/em> al inicio de la simulaci&oacute;n.<\/li>\r\n<\/ul>\r\n<p data-start=\"1610\" data-end=\"1698\">Todos ellos saltan a un bloque <strong data-start=\"1641\" data-end=\"1654\">PROCEDURE<\/strong>, que <strong data-start=\"1660\" data-end=\"1678\">debe finalizar<\/strong> con <code data-start=\"1683\" data-end=\"1697\">ENDPROCEDURE<\/code>.<\/p>\r\n<h3 data-start=\"1700\" data-end=\"1727\"><strong data-start=\"1704\" data-end=\"1727\">Nombres jer&aacute;rquicos<\/strong><\/h3>\r\n<p data-start=\"1728\" data-end=\"1777\">Los procedimientos pueden organizarse como rutas:<\/p>\r\n<pre>\r\nagente.abrir\r\ncliente.saludar\r\nrobot1.motor.arrancar<\/pre>\r\n<p data-start=\"1839\" data-end=\"1981\">El valor devuelto se almacenar&aacute; autom&aacute;ticamente en una variable cuyo nombre coincide con el &uacute;ltimo fragmento (&quot;abrir&quot;, &quot;saludar&quot;, &quot;arrancar&quot;).<\/p>\r\n<h3 data-start=\"1983\" data-end=\"2019\"><strong data-start=\"1987\" data-end=\"2019\">Par&aacute;metros y SNA disponibles<\/strong><\/h3>\r\n<p data-start=\"2020\" data-end=\"2087\">Dentro de un PROCEDURE pueden consultarse los par&aacute;metros recibidos:<\/p>\r\n<ul data-start=\"2089\" data-end=\"2193\">\r\n    <li data-start=\"2089\" data-end=\"2121\">\r\n    <p data-start=\"2091\" data-end=\"2121\"><code data-start=\"2091\" data-end=\"2102\">P$PARAM_A<\/code> &mdash; valor evaluado<\/p>\r\n    <\/li>\r\n    <li data-start=\"2122\" data-end=\"2193\">\r\n    <p data-start=\"2124\" data-end=\"2193\"><code data-start=\"2124\" data-end=\"2135\">V$PARAM_A<\/code> &mdash; valor bruto (array, objeto, n&uacute;mero, string sin evaluar)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"2195\" data-end=\"2229\"><strong data-start=\"2199\" data-end=\"2229\">Finalizaci&oacute;n del PROCEDURE<\/strong><\/h3>\r\n<p data-start=\"2231\" data-end=\"2267\">El procedimiento puede terminar con:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"2271\" data-end=\"2287\">RETURN valor<\/strong> &mdash; Vuelve al llamador.<\/li>\r\n    <li><strong data-start=\"2314\" data-end=\"2338\">RETURN_RESTORE valor<\/strong> &mdash; Para SIGNAL\/SIGNALNOW: la entidad destino retoma exactamente donde estaba.<\/li>\r\n    <li><strong data-start=\"2420\" data-end=\"2442\">RETURN_RETRY valor<\/strong> &mdash; Para SIGNAL\/SIGNALNOW: la entidad destino repite su paso actual (t&iacute;pico en reintentos de recursos).<\/li>\r\n<\/ul>\r\n<p data-start=\"1610\" data-end=\"1698\">&nbsp;<\/p>\r\n<p data-start=\"1610\" data-end=\"1698\">&nbsp;<\/p>",
                "descripcion": "<h3 data-start=\"626\" data-end=\"684\"><strong data-start=\"630\" data-end=\"684\">Subprocesos y llamadas en GPSS-Plus (introducci&oacute;n)<\/strong><\/h3>\r\n<p data-start=\"686\" data-end=\"983\">GPSS-Plus incorpora un sistema completo de subprocesos que no exist&iacute;a en GPSS cl&aacute;sico, gracias a la introducci&oacute;n de <strong data-start=\"802\" data-end=\"833\">pilas de ejecuci&oacute;n (stacks)<\/strong>.<br data-start=\"834\" data-end=\"837\">\r\nEsto permite que una entidad &mdash;o incluso el propio sistema&mdash; pueda invocar procedimientos independientes y retomar la ejecuci&oacute;n de forma controlada.<\/p>\r\n<p data-start=\"985\" data-end=\"1074\">Existen varios tipos de llamada, seg&uacute;n qui&eacute;n invoca y <strong data-start=\"1039\" data-end=\"1056\">qui&eacute;n ejecuta<\/strong> el procedimiento:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"1078\" data-end=\"1086\">CALL<\/strong> &mdash; Lo ejecuta <em data-start=\"1100\" data-end=\"1118\">la misma entidad<\/em> que lo invoca.<\/li>\r\n    <li><strong data-start=\"1138\" data-end=\"1148\">SIGNAL<\/strong> &mdash; Lo ejecuta <em data-start=\"1162\" data-end=\"1176\">otra entidad<\/em>, pero solo cuando la actual libera el turno.<\/li>\r\n    <li><strong data-start=\"1226\" data-end=\"1239\">SIGNALNOW<\/strong> &mdash; Lo ejecuta <em data-start=\"1253\" data-end=\"1282\">otra entidad inmediatamente<\/em>, interrumpiendo a la actual.<\/li>\r\n    <li><strong data-start=\"1316\" data-end=\"1327\">TIMEOUT<\/strong> &mdash; Lo ejecuta <em data-start=\"1341\" data-end=\"1367\">una entidad virtual (VE)<\/em> en un tiempo futuro.<\/li>\r\n    <li><strong data-start=\"1393\" data-end=\"1402\">ON_*<\/strong> &mdash; Lo ejecuta <em data-start=\"1416\" data-end=\"1424\">una VE<\/em> al producirse un evento en un recurso (SEIZE, RELEASE, ENTER...).<\/li>\r\n    <li><strong data-start=\"1495\" data-end=\"1504\">TIMER<\/strong> &mdash; Lo ejecuta <em data-start=\"1518\" data-end=\"1526\">una VE<\/em> peri&oacute;dicamente.<\/li>\r\n    <li><strong data-start=\"1547\" data-end=\"1558\">PRE_RUN<\/strong> &mdash; Lo ejecuta <em data-start=\"1572\" data-end=\"1580\">una VE<\/em> al inicio de la simulaci&oacute;n.<\/li>\r\n<\/ul>\r\n<p data-start=\"1610\" data-end=\"1698\">Todos ellos saltan a un bloque <strong data-start=\"1641\" data-end=\"1654\">PROCEDURE<\/strong>, que <strong data-start=\"1660\" data-end=\"1678\">debe finalizar<\/strong> con <code data-start=\"1683\" data-end=\"1697\">ENDPROCEDURE<\/code>.<\/p>\r\n<h3 data-start=\"1700\" data-end=\"1727\"><strong data-start=\"1704\" data-end=\"1727\">Nombres jer&aacute;rquicos<\/strong><\/h3>\r\n<p data-start=\"1728\" data-end=\"1777\">Los procedimientos pueden organizarse como rutas:<\/p>\r\n<pre>\r\nagente.abrir\r\ncliente.saludar\r\nrobot1.motor.arrancar<\/pre>\r\n<p data-start=\"1839\" data-end=\"1981\">El valor devuelto se almacenar&aacute; autom&aacute;ticamente en una variable cuyo nombre coincide con el &uacute;ltimo fragmento (&quot;abrir&quot;, &quot;saludar&quot;, &quot;arrancar&quot;).<\/p>\r\n<h3 data-start=\"1983\" data-end=\"2019\"><strong data-start=\"1987\" data-end=\"2019\">Par&aacute;metros y SNA disponibles<\/strong><\/h3>\r\n<p data-start=\"2020\" data-end=\"2087\">Dentro de un PROCEDURE pueden consultarse los par&aacute;metros recibidos:<\/p>\r\n<ul data-start=\"2089\" data-end=\"2193\">\r\n    <li data-start=\"2089\" data-end=\"2121\">\r\n    <p data-start=\"2091\" data-end=\"2121\"><code data-start=\"2091\" data-end=\"2102\">P$PARAM_A<\/code> &mdash; valor evaluado<\/p>\r\n    <\/li>\r\n    <li data-start=\"2122\" data-end=\"2193\">\r\n    <p data-start=\"2124\" data-end=\"2193\"><code data-start=\"2124\" data-end=\"2135\">V$PARAM_A<\/code> &mdash; valor bruto (array, objeto, n&uacute;mero, string sin evaluar)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"2195\" data-end=\"2229\"><strong data-start=\"2199\" data-end=\"2229\">Finalizaci&oacute;n del PROCEDURE<\/strong><\/h3>\r\n<p data-start=\"2231\" data-end=\"2267\">El procedimiento puede terminar con:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"2271\" data-end=\"2287\">RETURN valor<\/strong> &mdash; Vuelve al llamador.<\/li>\r\n    <li><strong data-start=\"2314\" data-end=\"2338\">RETURN_RESTORE valor<\/strong> &mdash; Para SIGNAL\/SIGNALNOW: la entidad destino retoma exactamente donde estaba.<\/li>\r\n    <li><strong data-start=\"2420\" data-end=\"2442\">RETURN_RETRY valor<\/strong> &mdash; Para SIGNAL\/SIGNALNOW: la entidad destino repite su paso actual (t&iacute;pico en reintentos de recursos).<\/li>\r\n<\/ul>\r\n<p data-start=\"1610\" data-end=\"1698\">&nbsp;<\/p>\r\n<p data-start=\"1610\" data-end=\"1698\">&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "367",
                        "nombre": "CALL",
                        "texto": "\/* CALL *\/\r\n\r\nPOSITION {NAME:POS1,   X:286, Y:244}\r\n\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}\r\n\r\n\r\n\r\nSTART 1\r\n\r\n\r\nGENERATE 10,0,0,1 {NAME:GA, X:100, Y:70}\r\n    ASSIGN datos, {nombre:\"Ana\", edad:30}\r\n    ASSIGN numero, 123\r\n    ASSIGN colores, [\"rojo\",\"verde\"]\r\n\r\n    CALL procesar, V$datos, V$numero, V$colores,30\r\n\r\n    move {name:Text1, text:\"Resultado del procedimiento: P$(procesar)\"}\r\n\r\n    ADVANCE 10 {to:POS1}\r\nENDGENERATE 1\r\n;---------------------------------------\r\nPROCEDURE procesar\r\n    move {name:Text2, text:\"Nombre: P$(PARAM_A.nombre)\"}\r\n    move {name:Text3, text:\"Edad: P$(PARAM_A.edad)\"}\r\n    move {name:Text4, text:\"Número: P$PARAM_B\"}\r\n    move {name:Text5, text:\"Primer color: P$(PARAM_C.0)\"}\r\n    move {name:Text6, text:\"Dato directo: P$(PARAM_D)\"}\r\n\r\n    RETURN \"OK\"\r\nENDPROCEDURE\r\n\r\n\r\n",
                        "descripcion": "<p data-start=\"183\" data-end=\"374\">El comando <strong data-start=\"194\" data-end=\"202\">CALL<\/strong> permite que <em data-start=\"215\" data-end=\"241\">la propia entidad activa<\/em> salte a un procedimiento y lo ejecute inmediatamente.<br data-start=\"295\" data-end=\"298\">\r\nAl finalizar, la entidad vuelve exactamente al paso siguiente del que llam&oacute;.<\/p>\r\n<p data-start=\"376\" data-end=\"445\">Es el comportamiento m&aacute;s simple y directo del sistema de subprocesos.<\/p>\r\n<p data-start=\"376\" data-end=\"445\"><b>Sintaxis:<\/b><\/p>\r\n<pre>\r\nCALL nombreProcedimiento, par&aacute;metroA, par&aacute;metroB, ...\r\n<\/pre>\r\n<ul>\r\n    <li>Los par&aacute;metros se reciben dentro del procedimiento como <code data-start=\"593\" data-end=\"604\">P$PARAM_A<\/code>, <code data-start=\"606\" data-end=\"617\">P$PARAM_B<\/code>, etc.<\/li>\r\n    <li>El valor devuelto por el procedimiento se guarda autom&aacute;ticamente en una variable llamada como el <strong data-start=\"723\" data-end=\"743\">&uacute;ltimo fragmento<\/strong> del nombre del PROCEDURE.<\/li>\r\n<\/ul>\r\n<p>Ejemplos de nombre &rarr; variable de resultado:<\/p>\r\n<pre>\r\nCALL calcular, 10   &rarr;  P$calcular\r\nCALL cliente.sumar, 20,30  &rarr;  P$sumar\r\nCALL robot.motor.arrancar &rarr; P$arrancar<\/pre>\r\n<p data-start=\"376\" data-end=\"445\">Finalizaci&oacute;n del PROCEDURE:<\/p>\r\n<p><strong data-start=\"1057\" data-end=\"1082\">ENDPROCEDURE valor<\/strong><\/p>\r\n<p data-start=\"1083\" data-end=\"1121\">Finaliza y devuelve el valor indicado:<\/p>\r\n<pre>\r\nENDPROCEDURE 3 ; P$calcular = 3<\/pre>\r\n<p data-start=\"376\" data-end=\"445\"><b>RETURN valor<\/b><\/p>\r\n<p data-start=\"376\" data-end=\"445\">Hace lo mismo, pero resulta m&aacute;s expl&iacute;cito y legible:<\/p>\r\n<pre>\r\nRETURN 3\r\nENDPROCEDURE<\/pre>\r\n<p data-start=\"376\" data-end=\"445\"><b><\/b>Ambas formas son equivalentes cuando se usa CALL.<\/p>\r\n<p data-start=\"376\" data-end=\"445\">&nbsp;<\/p>\r\n<p data-start=\"376\" data-end=\"445\"><b><\/b><\/p>\r\n<p><b><strong data-start=\"1335\" data-end=\"1362\">Acceso a los par&aacute;metros:<\/strong><\/b><\/p>\r\n<p data-start=\"1364\" data-end=\"1389\">Dentro del procedimiento:<\/p>\r\n<ul>\r\n    <li><code data-start=\"1393\" data-end=\"1404\">P$PARAM_A<\/code> devuelve el valor evaluado.<\/li>\r\n    <li><code data-start=\"1435\" data-end=\"1446\">V$PARAM_A<\/code> devuelve el valor bruto (arrays, objetos, strings sin evaluar).<\/li>\r\n<\/ul>\r\n<p data-start=\"1512\" data-end=\"1521\">Ejemplos:<\/p>\r\n<ul>\r\n    <li><code data-start=\"1525\" data-end=\"1545\">P$(PARAM_A.nombre)<\/code> si pasaste un objeto.<\/li>\r\n    <li><code data-start=\"1570\" data-end=\"1585\">P$(PARAM_B.2)<\/code> si pasaste un array.<\/li>\r\n<\/ul>\r\n<p data-start=\"376\" data-end=\"445\">&nbsp;<\/p>\r\n<p data-start=\"376\" data-end=\"445\"><b><\/b><\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "366",
                        "nombre": "SIGNAL y SIGNALNOW",
                        "texto": "\/* SIGNAL y SIGNALNOW en agentes permanentes *\/\r\n\r\nSYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}\r\n\r\nPOSITION {NAME:POS_1,   X:615, Y:398}\r\n\r\nGraphic {NAME:T_resultado1, type:TEXT, X:425, Y:281,text:\"resultado1\"}\r\nGraphic {NAME:T_resultado2, type:TEXT, X:425, Y:256,text:\"resultado2\"}\r\nGraphic {NAME:T2, type:TEXT, X:430, Y:501}\r\nGraphic {NAME:T3, type:TEXT, X:430, Y:531}\r\nGraphic {NAME:T4, type:TEXT, X:426, Y:310}\r\nGraphic {NAME:T5, type:TEXT, X:425, Y:188}\r\n\r\nRESTROOM {name:RestRoomAgenteDormido,x:423,y:118}\r\n\r\nSTART 200\r\n\r\n\/* ---- PRE RUN: Crear los agentes ---- *\/\r\nPROCEDURE PRE_RUN\r\n    TIMEOUT agenteVivo.loop, 0\r\n    TIMEOUT agenteDormido.loop, 0\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n\/* ---- GENERADOR DE ENTIDADES NORMALES ---- *\/\r\nGENERATE 15,0 {NAME:G1, X:153, Y:411}\r\n\r\n    ASSIGN miValor, D$N * 10\r\n    MOVE {name:T4, text:\"Entidad D$N comienza con miValor = P$miValor\"}\r\n\r\n    ADVANCE 10 {to:POS_1}\r\n\r\n    ; Llamada SIGNAL diferida\r\n    SIGNAL agenteVivo.procesar, X$nAgenteVivo, \"Hola\"\r\n\r\n    ; Llamada SIGNALNOW inmediata\r\n    SIGNALNOW agenteVivo.sumar, X$nAgenteVivo, P$miValor, 5\r\n\tMOVE {name:T_resultado1, text:\"Resultado del agente Vivo = P$sumar\"}\r\n\r\n\tSIGNALNOW agenteDormido.sumar, X$nAgenteDormido, P$miValor, 5\r\n\tMOVE {name:T_resultado2, text:\"Resultado del agente dormido = P$sumar\"}\r\n\r\n\r\nENDGENERATE 4\r\n\r\n\r\n\/* ---- AGENTE VIVO ---- *\/\r\nPROCEDURE agenteVivo.loop\r\n    SAVEVALUE nAgenteVivo, D$N\r\n    assign procesando,0\r\n    WHILE (1==1)\r\n        MOVE {name:T3, text:\"Agente Vivo [P$(nAgente)] activo en t = AC1$\"}\r\n        ADVANCE 2\r\n    ENDWHILE\r\n    terminate_ve\r\nENDPROCEDURE\r\n\r\n\/* ---- AGENTE DORMIDO ---- *\/\r\nPROCEDURE agenteDormido.loop\r\n    SAVEVALUE nAgenteDormido, D$N\r\n    assign procesando,0\r\n    MOVE {name:T5, text:\"Agente Dormido [P$(nAgente)] activo\"}\r\n    rest RestRoomAgenteDormido\r\n    terminate_ve\r\nENDPROCEDURE\r\n\r\n\r\n\/* ---- PROCEDIMIENTOS INVOCADOS POR ENTIDADES ---- *\/\r\n\r\nPROCEDURE agenteVivo.procesar\r\n\tif (P$procesando==1)\r\n    \treturn\r\n    endif\r\n    assign procesando,1\r\n    MOVE {name:T2, text:\"SIGNAL → agente INICIA proceso mensaje: P$PARAM_A\"}\r\n    ADVANCE 80\r\n    MOVE {name:T2, text:\"SIGNAL → agente FINALIZA proceso mensaje: P$PARAM_A\"}\r\n    assign procesando,0\r\n    RETURN ; viene de signal y no afecta el tipo a la entidad\r\nENDPROCEDURE\r\n\r\nPROCEDURE agenteVivo.sumar\r\n    ; PARAM_A = valor base\r\n    ; PARAM_B = incremento\r\n   ; RETURN P$PARAM_A + P$PARAM_B ; finaliza el proceso para admitir otro\r\n    RETURN_RESTORE P$PARAM_A + P$PARAM_B ; el proceso permanece en su tiempo\r\n   ; RETURN_RETRY P$PARAM_A + P$PARAM_B ; explota por no estar en un SEIZE o similar\r\nENDPROCEDURE\r\n\r\n;-------------------------------------------------------------------\r\n\r\nPROCEDURE agenteDormido.sumar\r\n    ; PARAM_A = valor base\r\n    ; PARAM_B = incremento\r\n   \r\n   ; RETURN P$PARAM_A + P$PARAM_B ; explota por morir el agente al salir del restroom\r\n    \r\n    RETURN_RESTORE P$PARAM_A + P$PARAM_B ; procesa quedandose el agente en su restroom\r\n    \r\n   ; WAKE RestRoomAgenteDormido,0,X$nAgenteDormido ; saca al agente de estar en REST\r\n   ; RETURN_RETRY P$PARAM_A + P$PARAM_B ; procesa reiniciando el agente su restroom\r\n   \r\nENDPROCEDURE\r\n\r\n",
                        "descripcion": "<h3>SIGNAL<\/h3>\r\n<p data-start=\"453\" data-end=\"576\"><code data-start=\"453\" data-end=\"461\">SIGNAL<\/code> invoca un <code>PROCEDURE <\/code>para que sea ejecutado por <strong data-start=\"509\" data-end=\"525\">otra entidad<\/strong>, pero <strong data-start=\"532\" data-end=\"549\">no interrumpe<\/strong> a la entidad que lo llama.<\/p>\r\n<ul data-start=\"578\" data-end=\"861\">\r\n    <li data-start=\"578\" data-end=\"643\">\r\n    <p data-start=\"580\" data-end=\"643\">La ejecuci&oacute;n ocurrir&aacute; <strong data-start=\"602\" data-end=\"642\">en el mismo instante de tiempo (AC1)<\/strong>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"644\" data-end=\"725\">\r\n    <p data-start=\"646\" data-end=\"725\">Entrar&aacute; <strong data-start=\"654\" data-end=\"724\">al final del conjunto de eventos programados para ese mismo tiempo<\/strong>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"726\" data-end=\"832\">\r\n    <p data-start=\"728\" data-end=\"832\">La entidad actual <strong data-start=\"746\" data-end=\"770\">contin&uacute;a normalmente<\/strong>, termina su ciclo y luego la entidad se&ntilde;alada tomar&aacute; el control en el momento programado.<\/p>\r\n    <\/li>\r\n    <li data-start=\"833\" data-end=\"861\">\r\n    <p data-start=\"835\" data-end=\"861\">Es una llamada <em data-start=\"850\" data-end=\"860\">diferida<\/em>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<blockquote data-start=\"863\" data-end=\"975\">\r\n<p data-start=\"865\" data-end=\"975\">En resumen: <strong data-start=\"877\" data-end=\"975\">SIGNAL programa la ejecuci&oacute;n del procedimiento, pero no interrumpe a la entidad que lo invoca.<\/strong><\/p>\r\n<\/blockquote>\r\n<h3>SIGNALNOW<\/h3>\r\n<p data-start=\"885\" data-end=\"1014\"><code data-start=\"885\" data-end=\"896\">SIGNALNOW<\/code> invoca un PROCEDURE para que sea ejecutado <strong data-start=\"940\" data-end=\"956\">de inmediato<\/strong> por otra entidad, <strong data-start=\"975\" data-end=\"993\">interrumpiendo<\/strong> a la entidad actual.<\/p>\r\n<p data-start=\"1016\" data-end=\"1029\">Puntos clave:<\/p>\r\n<ul>\r\n    <li>La entidad invocada toma el control <strong data-start=\"1069\" data-end=\"1087\">inmediatamente<\/strong>, dentro del mismo AC1.<\/li>\r\n    <li>La entidad llamante queda <strong data-start=\"1141\" data-end=\"1155\">suspendida<\/strong>, y retomar&aacute; exactamente donde estaba (o repetir&aacute; la operaci&oacute;n, seg&uacute;n el retorno).<\/li>\r\n    <li>La entidad se&ntilde;alada pasa a ser <strong data-start=\"1273\" data-end=\"1287\">la primera<\/strong> en ejecutarse en ese instante.<\/li>\r\n    <li>Es una <strong data-start=\"1330\" data-end=\"1352\">llamada preemptiva<\/strong>.<\/li>\r\n<\/ul>\r\n<p data-start=\"1355\" data-end=\"1403\">Normalmente, el PROCEDURE deber&iacute;a finalizar con:<\/p>\r\n<ul data-start=\"1405\" data-end=\"1608\">\r\n    <li data-start=\"1405\" data-end=\"1496\">\r\n    <p data-start=\"1407\" data-end=\"1496\"><strong data-start=\"1407\" data-end=\"1425\">RETURN_RESTORE<\/strong><br data-start=\"1425\" data-end=\"1428\">\r\n    Si la entidad interrumpida debe reanudar exactamente donde estaba.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1497\" data-end=\"1608\">\r\n    <p data-start=\"1499\" data-end=\"1608\"><strong data-start=\"1499\" data-end=\"1515\">RETURN_RETRY<\/strong><br data-start=\"1515\" data-end=\"1518\">\r\n    Si debe <strong data-start=\"1528\" data-end=\"1539\">repetir<\/strong> la operaci&oacute;n en la que estaba (t&iacute;pico para SEIZE, ENTER o recursos).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1610\" data-end=\"1731\"><strong data-start=\"1610\" data-end=\"1625\">En resumen:<\/strong> <code data-start=\"1626\" data-end=\"1637\">SIGNALNOW<\/code> provoca una interrupci&oacute;n inmediata; la entidad se&ntilde;alada ejecuta el procedimiento sin esperar.<\/p>\r\n<h3>En el ejemplo:<\/h3>\r\n<p>Funcionamiento de <code>SIGNAL <\/code>\/ <code>SIGNALNOW <\/code>con agentes activos y dormidos<\/p>\r\n<p data-start=\"306\" data-end=\"467\">Cuando un procedimiento es ejecutado mediante <strong data-start=\"352\" data-end=\"362\">SIGNAL<\/strong> o <strong data-start=\"365\" data-end=\"378\">SIGNALNOW<\/strong>, el comportamiento final depende del estado en el que se encuentre la entidad receptora.<\/p>\r\n<p data-start=\"469\" data-end=\"521\">En este ejemplo se muestran dos casos muy distintos:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"525\" data-end=\"539\">agenteVivo<\/strong> &rarr; est&aacute; en un bucle con <strong data-start=\"563\" data-end=\"574\">ADVANCE<\/strong>, siempre activo.<\/li>\r\n    <li><strong data-start=\"596\" data-end=\"613\">agenteDormido<\/strong> &rarr; est&aacute; detenido en una instrucci&oacute;n <strong data-start=\"649\" data-end=\"657\">REST<\/strong>, esperando indefinidamente.<\/li>\r\n<\/ul>\r\n<p data-start=\"687\" data-end=\"733\">El agente vivo tiene una tares de procesar un texto que tarda un tiempo considerable. Mientras no lo est&aacute; procesando, muestra constantemente el tiempo AC1, pero cuando entra en el proceso, no es capaz de hacerlo not&aacute;ndose que el tiempo no se actualiza.<\/p>\r\n<p data-start=\"687\" data-end=\"733\">Si cambiamos la salida del procedure a:<\/p>\r\n<pre>\r\nRETURN P$PARAM_A + P$PARAM_B<\/pre>\r\n<p data-start=\"687\" data-end=\"733\">Notaremos que las llamadas a sumar interrumpen la tarea.<\/p>\r\n<p data-start=\"687\" data-end=\"733\">&nbsp;<\/p>\r\n<p data-start=\"687\" data-end=\"733\">En resumen, esto permite ver las diferencias reales entre:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"737\" data-end=\"747\">RETURN<\/strong><\/li>\r\n    <li><strong data-start=\"750\" data-end=\"768\">RETURN_RESTORE<\/strong><\/li>\r\n    <li><strong data-start=\"771\" data-end=\"787\">RETURN_RETRY<\/strong><\/li>\r\n<\/ul>\r\n<p><b>PARA EL AGENTE VIVO&nbsp;<\/b><b>(en ADVANCE)<\/b><\/p>\r\n<ul>\r\n    <li><b>RETURN -&gt; ERROR<\/b><br>\r\n    El agente vuelve a su ejecuci&oacute;n normal.<br>\r\n    El problema es que el agente da por terminado su ADVANCE y finaliza el proceso AUNQUE a&uacute;n le falte tiempo de ejecuci&oacute;n.<\/li>\r\n    <li><b>RETURN_RESTORE -&gt; CORRECTO<\/b><br>\r\n    El agente vuelve correctamente a su ejecuci&oacute;n normal.<br>\r\n    No pierde su turno ni su posici&oacute;n temporal.<br>\r\n    El advance del proceso que estuviese ejecutando contin&uacute;a exactamente donde se interrumpi&oacute;.<\/li>\r\n    <li><b>RETURN_RETRY -&gt; ERROR FATAL     <\/b>Error fatal, la entidad vuelve a ejecutar el SIGNALNOW indefinidamente. Usar este retorno est&aacute; reservado a circunstancias en las que se sabe que la entidad est&aacute; en la cola de un recurso.<\/li>\r\n<\/ul>\r\n<p data-start=\"837\" data-end=\"936\">&nbsp;<\/p>\r\n<p><b>PARA EL AGENTE DORMIDO&nbsp;<\/b><b>(en RESTROOM)<\/b><\/p>\r\n<ul>\r\n    <li><b>RETURN -&gt; ERROR<\/b><br>\r\n    RETURN indica que el procedimiento ha terminado, por lo que el agente contin&uacute;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&aacute; agente receptor.<\/li>\r\n    <li><b>RETURN_RESTORE -&gt; CORRECTO<\/b><br>\r\n    El agente vuelve correctamente a su ejecuci&oacute;n normal dentro del RESTROOM.<\/li>\r\n    <li><b>RETURN_RETRY -&gt; CASI CORRECTO<br>\r\n    <\/b>El agente vuelve a ejecutar el REST quedando de nuevo dormido en el RESTROOM. No est&aacute; en la cola del restroom, por lo que volver&aacute; a entrar indicando que el restroom tiene una entidad m&aacute;s cuando es incorrecto. Para evitarlo, habr&iacute;a que tratarlo como si interrumpi&eacute;semos una entidad en un SEIZE sac&aacute;ndola primero de la lista de ocupantes con WAKE.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "365",
                        "nombre": "Procedimientos de VE: TIMER, TIMEOUT, ON_*, PRE_RUN",
                        "texto": "POSITION {NAME:Salida, X:624, Y:199}\r\n\r\nGraphic {NAME:T1, type:TEXT, X:493, Y:505}\r\nGraphic {NAME:T2, type:TEXT, X:493, Y:476}\r\nGraphic {NAME:T3, type:TEXT, X:492, Y:444}\r\n\r\nFacility {NAME:Ventana, ON_RELEASE: cuandoSale, X:249, Y:335 }\r\n\r\nSYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}\r\n\r\nSTART 50\r\n\r\n;----------------------------------------\r\nPROCEDURE PRE_RUN\r\n    move {name:T1, text:\"PRE_RUN ejecutado en t = AC1$\"}\r\n    TIMEOUT avisoInicial, 10\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;----------------------------------------\r\nGENERATE 15,0 {NAME:\"GEN1\", X:100, Y:100}\r\n\tADVANCE 10 {to:Ventana}\r\n    SEIZE Ventana\r\n    ADVANCE 5\r\n    RELEASE Ventana        ; disparará ON_RELEASE\r\n    ADVANCE 10 {to:Salida}\r\nENDGENERATE 1\r\n\r\n;----------------------------------------\r\nPROCEDURE avisoInicial\r\n    move {name:T2, text:\"TIMEOUT ejecutado en t = AC1$\"}\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nPROCEDURE cuandoSale\r\n    move {name:T3, text:\"ON_RELEASE ejecutado en t = AC1$\"}\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n",
                        "descripcion": "<p><b>TIMEOUT<\/b>&nbsp;\/ <b>ON_* \/ PRE_RUN \/ TIMER<\/b><\/p>\r\n<p data-start=\"3449\" data-end=\"3536\">Estos procedimientos son ejecutados por entidades virtuales (<strong data-start=\"3940\" data-end=\"3946\">VE<\/strong>) reci&eacute;n creadas.<br>\r\nLas VE deben finalizar como cualquier entidad con un <code>TERMINATE <\/code>o <code>TERMINATE_VE<\/code>. La diferencia entre ambos BLOQUES es que la primera finaliza  cualquier entidad y la segunda solo entidades virtuales. Usando el  bloque&nbsp;<code>TERMINATE_VE<\/code> , el&nbsp;<code>PROCEDURE&nbsp;<\/code>podr&aacute; ser invocado para ser ejecutado por ambos tipos de entidades.&nbsp;<br>\r\nEs importante decir que deben ser TERMINADAS obligatoriamente pues de llegar al ENDPROCEDURE no sabr&iacute;an donde retornar.<\/p>\r\n<pre>\r\nTIMEOUT abrirRecurso, 50, P$valor, V$Objeto\r\n\r\nPROCEDURE abrirRecurso\r\n&nbsp; move {name:text1, text:&quot;Ejecutado en t=AC1$&quot;}\r\n  TERMINATE_VE\r\nENDPROCEDURE<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "368",
                        "nombre": "SCAPE \/ LOAD \/ UNLOAD *",
                        "texto": "POSITION {NAME:Salida, X:178, Y:39}\r\nPOSITION {NAME:inicioCarretera, X:150, Y:313}\r\nPOSITION {NAME:finCarretera, X:514, Y:314}\r\n\r\nFacility {NAME:museo, capacity:50, X:534, Y:77 }\r\n\r\nRestroom {NAME:paradaAutobusCiudad, X:68, Y:321 }\r\nRestroom {NAME:paradaAutobusMuseo, X:588, Y:312 }\r\n\r\nSTART 500\r\n\r\n;----------------------------------------\r\nGENERATE 15,0 {NAME:\"GenUsuarios\", X:100, Y:100 }\r\n\r\n\tif (P$museoVisitado==1)\r\n   \t\tadvance 100,50 {from:inicioCarretera ,to:Salida}\r\n        terminate 1\r\n    endif\r\n    \r\n\tASSIGN TIPO,0 ; EN VEHICULO PARTICULAR\r\n    if (RANDOM >0.2)\r\n    ASSIGN TIPO,1 ; EN AUTOBUS\r\n    endif\r\n    advance 10 {to:inicioCarretera}\r\n    if (P$TIPO==1)\r\n    REST paradaAutobusCiudad\r\n    endif\r\n    advance 50,10 {to:finCarretera}\r\n    SCAPE GenMuseo\r\nENDGENERATE 1\r\n\r\n;----------------------------------------\r\nGENERATE 0,0,0,0 {NAME:\"GenMuseo\", X:600, Y:160 }\r\n\tadvance 20,30 {from:finCarretera,to:museo}\r\n    seize museo\r\n    advance 30,50\r\n    assign museoVisitado,1\r\n    mod {color:green}\r\n    release museo\r\n    \r\n    advance 10 {to:finCarretera}\r\n\r\n    if (P$TIPO==1)\r\n    REST paradaAutobusMuseo\r\n    endif\r\n    advance 50 {to:inicioCarretera}\r\n    SCAPE GenUsuarios\r\nENDGENERATE 1\r\n\r\n;----------------------------------------\r\n\r\n\r\n;----------------------------------------\r\nGENERATE 50,0,0,2 {NAME:\"GenAutobuses\", X:300, Y:460,ecolor:red,eradio:10,visible:0 }\r\n\tadvance 50 {to:inicioCarretera}\r\n    while (1==1)\r\n    \tload miBackpackIda, paradaAutobusCiudad\r\n        mod {subtitle:\"Pasajeros P$(miBackpackIda.LENGTH)\"}\r\n  \t  \tadvance 80,10 {to:finCarretera}\r\n   \t \tunload miBackpackIda, GenMuseo\r\n        advance 10\r\n        load miBackpackVuelta, paradaAutobusMuseo\r\n        mod {subtitle:\"Pasajeros P$(miBackpackVuelta.LENGTH)\"}\r\n  \t  \tadvance 80,10 {to:inicioCarretera}\r\n        unload miBackpackVuelta, GenUsuarios\r\n        advance 10\r\n    endwhile\r\nENDGENERATE 0\r\n\r\n\r\n",
                        "descripcion": "<p><b>SCAPE<\/b><\/p>\r\n<p>Es una llamada especial que tiene como destino un <code>GENERATE<\/code>.<\/p>\r\n<p data-start=\"3449\" data-end=\"3536\">En un motor basado en colas y listas finitas, el stack tambi&eacute;n debe considerarse una estructura acotada.<br data-start=\"2156\" data-end=\"2159\">\r\nLas transiciones estructurales de una entidad no pueden depender indefinidamente de apilar retornos.<br data-start=\"2261\" data-end=\"2264\">\r\nPor ello, GPSS-Plus incorpora <code data-start=\"2296\" data-end=\"2303\">SCAPE<\/code> como salto estructural sin retorno, capaz de reiniciar la ejecuci&oacute;n desde un <code data-start=\"2381\" data-end=\"2391\">GENERATE<\/code> y mantener el tama&ntilde;o del stack bajo control.<\/p>\r\n<p data-start=\"3449\" data-end=\"3536\"><code data-start=\"2268\" data-end=\"2275\">SCAPE<\/code> se usa cuando una entidad debe abandonar completamente su flujo actual y comenzar uno nuevo.<br data-start=\"2368\" data-end=\"2371\">\r\nEsto es &uacute;til cuando una condici&oacute;n requiere reiniciar, cambiar de comportamiento o bifurcar el flujo de manera irreversible.<\/p>\r\n<p data-start=\"3449\" data-end=\"3536\">El salto <code>SCAPE <\/code>lleva a los &uacute;nicos puntos seguros limpios de stack de direcciones: los <code>GENERATES<\/code>.<br>\r\nConservar&aacute; todos los valores de assign.<\/p>\r\n<pre>\r\nSCAPE nombreGenerate, 50, P$valor, V$Objeto\r\n<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p><b><strong data-start=\"3716\" data-end=\"3744\">BACKPACK (LOAD \/ UNLOAD)<\/strong><\/b><\/p>\r\n<p data-start=\"1624\" data-end=\"1727\">Una entidad <strong data-start=\"1653\" data-end=\"1702\">solo puede ser cargada (LOAD) si est&aacute; en REST&nbsp;<\/strong>porque en ese estado:<\/p>\r\n<ul data-start=\"1729\" data-end=\"1851\">\r\n    <li data-start=\"1729\" data-end=\"1746\">\r\n    <p data-start=\"1731\" data-end=\"1746\">Est&aacute; dormida.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1747\" data-end=\"1768\">\r\n    <p data-start=\"1749\" data-end=\"1768\">No consume ciclo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1769\" data-end=\"1823\">\r\n    <p data-start=\"1771\" data-end=\"1823\"><strong data-start=\"1771\" data-end=\"1798\">No puede despertar sola<\/strong>, solo mediante <code data-start=\"1814\" data-end=\"1820\">WAKE<\/code>&nbsp;por terceros.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1824\" data-end=\"1851\">\r\n    <p data-start=\"1826\" data-end=\"1851\">Es &ldquo;segura&rdquo; de manipular.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1853\" data-end=\"1940\">Por eso el BACKPACK es una herramienta de <strong data-start=\"1895\" data-end=\"1913\">log&iacute;stica&nbsp;<\/strong>no de control de flujo. Se transporta cualquier entidad.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p data-start=\"3747\" data-end=\"3808\">Mecanismo para transportar entidades dormidas en un <code>RESTROOM<\/code>.<\/p>\r\n<ul data-start=\"3810\" data-end=\"3992\">\r\n    <li data-start=\"3810\" data-end=\"3883\">\r\n    <p data-start=\"3812\" data-end=\"3883\">LOAD: la entidad cargada es retirada del&nbsp;<code>RESTROOM&nbsp;<\/code>y pasa al backpack.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3884\" data-end=\"3992\">\r\n    <p data-start=\"3886\" data-end=\"3992\">UNLOAD: al llegar al destino, la entidad descargada empieza una nueva vida desde un <code>GENERATE <\/code>especificado.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p><b>En el ejemplo:<\/b><\/p>\r\n<p data-start=\"291\" data-end=\"497\">En este ejemplo se simula una ciudad y un museo conectados por una carretera.<br data-start=\"368\" data-end=\"371\">\r\nLos ciudadanos generan su propio flujo de vida: algunos viajan en veh&iacute;culo particular y otros esperan en la parada de autob&uacute;s.<\/p>\r\n<p data-start=\"499\" data-end=\"658\">Cuando llegan al museo o regresan a la ciudad, las entidades &ldquo;cambian de mundo&rdquo; utilizando <strong data-start=\"590\" data-end=\"599\">SCAPE<\/strong>, iniciando un nuevo flujo en el generador correspondiente.<\/p>\r\n<p data-start=\"660\" data-end=\"933\">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&uacute;a su propio flujo.<\/p>\r\n<p data-start=\"660\" data-end=\"933\">De este modo, cada entidad contin&uacute;a su ciclo natural seg&uacute;n su medio de transporte.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "295",
                "nombre": "Recursos físicos",
                "texto": "",
                "descripcion": "",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "297",
                        "nombre": "Generalidades *",
                        "texto": "POSITION {NAME:Salida,X:615,Y:388}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}\r\n\r\nFacility {NAME:Facility1,X:324,Y:560\r\n\t,capacity:3\r\n\t,ON_ATTEMPT:FACILITY1_attempt\r\n\t,ON_SEIZE:FACILITY1_seize\r\n\t,ON_RELEASE:FACILITY1_release\r\n\t,ON_QUEUE:FACILITY1_queue\r\n    }\r\n\r\nStorage {NAME:Storage1,X:324,Y:235\r\n\t,capacity:30\r\n\t,ON_ATTEMPT:STORAGE1_attempt\r\n\t,ON_ENTER:STORAGE1_enter\r\n\t,ON_LEAVE:STORAGE1_leave\r\n\t,ON_QUEUE:STORAGE1_queue\r\n    }\r\n\r\n\r\n\r\nSTART 1000\r\n\r\n;-------------------------------\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:62,Y:396}\r\n\r\nif (D$N%2==1)\r\n\r\n    advance 10 {to:Facility1}\r\n    seize Facility1\r\n    advance 55,10\r\n    release Facility1\r\nelse\r\n    advance 10 {to:Storage1}\r\n    enter Storage1,random * 25 + 1\r\n    advance 35,10\r\n    leave Storage1\r\nendif\r\n\r\n\r\nadvance 10 {to:Salida}\r\n\r\nENDGENERATE 1\r\n;-------------------------------\r\n\r\nPROCEDURE FACILITY1_release\r\n\tmove {name:Text1,text:\"AC1$ Soy la VE [D$N] de ON_RELEASE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_queue\r\n\tmove {name:Text2,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_seize\r\n\tmove {name:Text3,text:\"AC1$ Soy la VE [D$N] de ON_SEIZE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_attempt\r\n\tmove {name:Text4,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n;-------------------------------\r\n\r\nPROCEDURE STORAGE1_leave\r\n\tmove {name:Text5,text:\"AC1$ Soy la VE [D$N] de ON_LEAVE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_queue\r\n\tmove {name:Text6,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_enter\r\n\tmove {name:Text7,text:\"AC1$ Soy la VE [D$N] de ON_ENTER la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_attempt\r\n\tmove {name:Text8,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\n\r\n",
                        "descripcion": "<h3 data-start=\"649\" data-end=\"683\">Modelo general de los recursos<\/h3>\r\n<p data-start=\"684\" data-end=\"896\">En GPSS-Plus, los recursos comparten una estructura com&uacute;n basada en <strong data-start=\"752\" data-end=\"784\">listas internas de entidades<\/strong>.<br data-start=\"785\" data-end=\"788\">\r\nCada recurso puede tener <strong data-start=\"813\" data-end=\"833\">una o dos listas<\/strong>, y el comportamiento del recurso se define principalmente por:<\/p>\r\n<ul data-start=\"898\" data-end=\"1066\">\r\n    <li data-start=\"898\" data-end=\"924\">\r\n    <p data-start=\"900\" data-end=\"924\">la funci&oacute;n de cada lista<\/p>\r\n    <\/li>\r\n    <li data-start=\"925\" data-end=\"957\">\r\n    <p data-start=\"927\" data-end=\"957\">el criterio de entrada en ella<\/p>\r\n    <\/li>\r\n    <li data-start=\"958\" data-end=\"981\">\r\n    <p data-start=\"960\" data-end=\"981\">el criterio de salida<\/p>\r\n    <\/li>\r\n    <li data-start=\"982\" data-end=\"1066\">\r\n    <p data-start=\"984\" data-end=\"1066\">si esa lista <strong data-start=\"997\" data-end=\"1008\">retiene<\/strong> a la entidad o permite que contin&uacute;e en la cola de eventos<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1068\" data-end=\"1272\">De este modo, la sem&aacute;ntica de los recursos no depende solo de su nombre (<code data-start=\"1141\" data-end=\"1151\">FACILITY<\/code>, <code data-start=\"1153\" data-end=\"1162\">STORAGE<\/code>, etc.), sino de la organizaci&oacute;n interna de sus listas y de c&oacute;mo interact&uacute;an con la cola de eventos del motor.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><b>Tipos de recursos disponibles en GPSS-Plus:<\/b><\/p>\r\n<ul data-start=\"187\" data-end=\"652\">\r\n    <li data-start=\"187\" data-end=\"260\">\r\n    <p data-start=\"189\" data-end=\"260\"><strong data-start=\"189\" data-end=\"201\">Facility<\/strong> &rarr; Recurso exclusivo con capacidad. Ej.: m&aacute;quina, servidor.<\/p>\r\n    <\/li>\r\n    <li data-start=\"261\" data-end=\"321\">\r\n    <p data-start=\"263\" data-end=\"321\"><strong data-start=\"263\" data-end=\"274\">Storage<\/strong> &rarr; Recurso acumulativo. Ej.: almac&eacute;n, dep&oacute;sito.<\/p>\r\n    <\/li>\r\n    <li data-start=\"322\" data-end=\"394\">\r\n    <p data-start=\"324\" data-end=\"394\"><strong data-start=\"324\" data-end=\"334\">Restroom<\/strong> &rarr; Recurso retenedor. Las entidades esperan a ser liberadas.<\/p>\r\n    <\/li>\r\n    <li data-start=\"395\" data-end=\"460\">\r\n    <p data-start=\"397\" data-end=\"460\"><strong data-start=\"397\" data-end=\"408\">Conditions<\/strong>&nbsp;&rarr; Recurso retenedor condicional. Paso seg&uacute;n l&oacute;gica.<\/p>\r\n    <\/li>\r\n    <li data-start=\"461\" data-end=\"520\">\r\n    <p data-start=\"463\" data-end=\"520\"><strong data-start=\"463\" data-end=\"472\">Stock<\/strong> &rarr; Almac&eacute;n inteligente de productos etiquetados.<\/p>\r\n    <\/li>\r\n    <li data-start=\"521\" data-end=\"584\">\r\n    <p data-start=\"523\" data-end=\"584\"><strong data-start=\"523\" data-end=\"533\">Stater<\/strong> &rarr; M&aacute;quina de estados finitos. Control de procesos.<\/p>\r\n    <\/li>\r\n    <li data-start=\"585\" data-end=\"652\">\r\n    <p data-start=\"587\" data-end=\"652\"><strong data-start=\"587\" data-end=\"597\">Queuer<\/strong> &rarr; Recurso abierto. Para conteo, agrupaci&oacute;n o an&aacute;lisis.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p><br>\r\nPermiten modelar capacidades compartidas, espacios f&iacute;sicos, almacenes,  puntos de sincronizaci&oacute;n, etc. Cada tipo de recurso define una l&oacute;gica  propia de uso, pero todos pueden conectarse con procedimientos a trav&eacute;s  de <em data-start=\"588\" data-end=\"597\">eventos<\/em> (hooks) como <code data-start=\"611\" data-end=\"621\">ON_SEIZE<\/code>, <code data-start=\"623\" data-end=\"633\">ON_LEAVE<\/code>, etc.<\/p>\r\n<p><b>Histogramas autom&aacute;ticos:<\/b><\/p>\r\n<p>Todos los recursos permiten generar histogramas autom&aacute;ticos sobre su uso&nbsp;definiendo su tabulaci&oacute;n:<\/p>\r\n<p><b>R_BIN_*:<\/b>&nbsp;Tabula la ocupaci&oacute;n del [R] Recurso.<\/p>\r\n<pre>\r\nR_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:40<\/pre>\r\n<p><b>E_BIN_*:<\/b>&nbsp;Tabula los tiempos de ocupaci&oacute;n de la [E] Entidad.<\/p>\r\n<pre>\r\nE_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40<\/pre>\r\n<p><b>SNA asociados<\/b>:<\/p>\r\n<p>Mediante <code>R$(nombreRecurso,propiedad) <\/code>puedes consultar en tiempo real:<\/p>\r\n<ul>\r\n    <li><code>X, Y<\/code> &rarr; Posici&oacute;n gr&aacute;fica<\/li>\r\n    <li><code>LEFT, IN, QUEUE, ENTRIES, CAPACITY<\/code> &rarr; Estado operativo<\/li>\r\n    <li><code>LOCK <\/code>&rarr; 0 o 1 seg&uacute;n si la entrada est&aacute; bloqueada<\/li>\r\n    <li><code>OCCUPIED <\/code>&rarr; (solo en STORAGE) cantidad actual ocupada<\/li>\r\n<\/ul>\r\n<pre>\r\nR$(Fac1,LEFT)   ; Espacio disponible     \r\nR$(Fac1,QUEUE)  ; Tama&ntilde;o de de cola\r\nR$(Fac1,LOCK)   ; 0|1 Bloqueada la entrada<\/pre>\r\n<p><b>HOOKs<\/b><\/p>\r\n<p>Dependiendo del recurso en particular, pueden disponer de determinados Hooks atendidos por procedures ejecutados por entidades virtuales (VE). <br>\r\nSe crear&aacute;n entidades virtuales (VE) que recorrer&aacute;n el trigger y nacer&aacute;n con el assign <code>ENTITYNUMBER<\/code>.<\/p>\r\n<pre>\r\nFacility {NAME:Fac2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt}\r\n;------------------------\r\nPROCEDURE Fac1_attempt\r\n    MOVE {NAME:Text1,Text:&quot;La entidad P$ENTITYNUMBER ha intentado entrar&quot;}\r\n    TERMINATE_VE\r\nENDPROCEDURE<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "298",
                        "nombre": "Facility",
                        "texto": "\/*\r\n\r\nRecursos. Facility\r\n\r\n\r\n*\/\r\n\r\nPOSITION {NAME:Salida,X:615,Y:388}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}\r\n\r\nFacility {NAME:Facility1,X:324,Y:160\r\n\t,capacity:3\r\n\t,ON_ATTEMPT:FACILITY1_ATTEMPT\r\n\t,ON_SEIZE:FACILITY1_SEIZE\r\n\t,ON_RELEASE:FACILITY1_RELEASE\r\n\t,ON_QUEUE:FACILITY1_QUEUE\r\n    ,R_BIN_SIZE:1,R_BIN_COUNT:10\r\n    ,E_BIN_SIZE:1,E_BIN_COUNT:10\r\n    }\r\n\r\n\r\nSTART 1000\r\n\r\n;-------------------------------\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:62,Y:396}\r\n\r\nadvance 10 {to:Facility1}\r\nseize Facility1\r\nadvance 55,10\r\nrelease Facility1\r\nadvance 10 {to:Salida}\r\n\r\nENDGENERATE 1\r\n;-------------------------------\r\n\r\nPROCEDURE FACILITY1_RELEASE\r\n\tmove {name:Text1,text:\"AC1$ Soy la VE [D$N] de ON_RELEASE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_QUEUE\r\n\tmove {name:Text2,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_SEIZE\r\n\tmove {name:Text3,text:\"AC1$ Soy la VE [D$N] de ON_SEIZE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE FACILITY1_ATTEMPT\r\n\tmove {name:Text4,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\n\r\n\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\"><\/strong><strong data-start=\"678\" data-end=\"690\">FACILITY<\/strong><\/p>\r\n<p>Modela un recurso exclusivo (como una m&aacute;quina, un operador o una cabina).<br data-start=\"769\" data-end=\"772\">\r\nPermite m&uacute;ltiples entidades seg&uacute;n su <code data-start=\"811\" data-end=\"821\">capacity<\/code> y cola si est&aacute; ocupado.<br>\r\nInternamente gestiona dos listas: Entidades ocupantes y de entidades en espera.<\/p>\r\n<p>Soporta varios m&eacute;todos de selecci&oacute;n de entidad entrante a trav&eacute;s del par&aacute;metro <code>METHOD<\/code>:<\/p>\r\n<ul>\r\n    <li>&quot;FIFO&quot;&nbsp; &nbsp; Primero en entrar, primero en salir. Devuelve el primero de la lista (orden de llegada).<\/li>\r\n    <li>&quot;LIFO&quot;&nbsp; &nbsp; &Uacute;ltimo en entrar, primero en salir. Recorre hasta el &uacute;ltimo nodo en la lista.<\/li>\r\n    <li>default&nbsp; &nbsp; Equivale a &quot;FIFO&quot; si no se especifica m&eacute;todo.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>SEIZE <\/b>nombreRecurso ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.<\/li>\r\n    <li><b>RELEASE&nbsp;<\/b>nombreRecurso;&nbsp;Libera el recurso y permite el acceso de otra entidad.<\/li>\r\n<\/ul>\r\n<p>Eventos disponibles:<code>ON_ATTEMPT<\/code>, <code>ON_QUEUE<\/code>, <code>ON_SEIZE<\/code>, <code>ON_RELEASE<\/code><\/p>\r\n<pre>\r\nFacility {NAME:Fac1, CAPACITY:3, X:100, Y:100}\r\nFacility {NAME:Fac2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }\r\n;------------------------\r\nSEIZE Fac1\r\nADVANCE 10\r\nRELEASE Fac1<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "299",
                        "nombre": "Storage",
                        "texto": "\/*\r\n\r\nRecursos. Storage\r\n\r\n\r\n*\/\r\n\r\nPOSITION {NAME:Salida,X:615,Y:388}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}\r\n\r\nStorage {NAME:Storage1,X:324,Y:235\r\n\t,capacity:30\r\n\t,ON_ATTEMPT:STORAGE1_attempt\r\n\t,ON_ENTER:STORAGE1_enter\r\n\t,ON_LEAVE:STORAGE1_leave\r\n\t,ON_QUEUE:STORAGE1_queue\r\n    ,E_BIN_START:34,E_BIN_SIZE:1,E_BIN_COUNT:12\r\n    ,R_BIN_SIZE:1,R_BIN_COUNT:10\r\n    ,EQ_BIN_START:34,EQ_BIN_SIZE:1,EQ_BIN_COUNT:12\r\n    ,RQ_BIN_START:0,RQ_BIN_SIZE:1,RQ_BIN_COUNT:32\r\n    }\r\n\r\n\r\nSTART 1000\r\n\r\n;-------------------------------\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:62,Y:396}\r\n\r\n\r\nadvance 10 {to:Storage1}\r\nenter Storage1,random * 25 + 1\r\nadvance 35,10\r\nleave Storage1\r\n\r\n\r\n\r\nadvance 10 {to:Salida}\r\n\r\nENDGENERATE 1\r\n;-------------------------------\r\n\r\nPROCEDURE STORAGE1_leave\r\n\tmove {name:Text5,text:\"AC1$ Soy la VE [D$N] de ON_LEAVE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_queue\r\n\tmove {name:Text6,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_enter\r\n\tmove {name:Text7,text:\"AC1$ Soy la VE [D$N] de ON_ENTER la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE STORAGE1_attempt\r\n\tmove {name:Text8,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\n\r\n\r\n\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\">STORAGE<\/strong><\/p>\r\n<p>Modela un recurso exclusivo tipo almac&eacute;n en el que la ocupaci&oacute;n viene definica por cantidades a aportar por cada entidad.<br>\r\nPermite m&uacute;ltiples capacidades seg&uacute;n su <code>capacity<\/code> y cola si no hay espacio disponible.<br>\r\nInternamente gestiona dos listas: Entidades ocupantes con su carga y de entidades en espera.<\/p>\r\n<p>Soporta varias l&oacute;gicas de selecci&oacute;n de entrada a trav&eacute;s del par&aacute;metro opcional <code>METHOD<\/code>:<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>ENTER&nbsp;<\/b>nombreRecurso,cantidad ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.<\/li>\r\n    <li><b>LEAVE&nbsp;<\/b>nombreRecurso;&nbsp;Libera el recurso en la cantidad aportada y permite el acceso de otra entidad.<\/li>\r\n<\/ul>\r\n<p>Eventos disponibles:<code>ON_ATTEMPT<\/code>, <code>ON_QUEUE<\/code>, <code>ON_ENTER<\/code>, <code>ON_LEAVE<br>\r\n<\/code><\/p>\r\n<p>Soporta varios m&eacute;todos de selecci&oacute;n de entidad entrante a trav&eacute;s del par&aacute;metro <code>METHOD<\/code>:<\/p>\r\n<ul>\r\n    <li>&quot;FIFO&quot;&nbsp; &nbsp; Primero en entrar, primero en salir. Devuelve el primero de la lista (orden de llegada).<\/li>\r\n    <li>&quot;LIFO&quot;&nbsp; &nbsp; &Uacute;ltimo en entrar, primero en salir. Recorre hasta el &uacute;ltimo nodo en la lista.<\/li>\r\n    <li>&quot;MIN_SPACE&quot;&nbsp; &nbsp; Elige a la entidad que necesita menos espacio (y cabe en lo disponible).<\/li>\r\n    <li>&quot;MAX_SPACE&quot;&nbsp; &nbsp; Elige a la entidad que necesita m&aacute;s espacio (dentro del espacio disponible).<\/li>\r\n    <li>default&nbsp; &nbsp; Equivale a &quot;FIFO&quot; si no se especifica m&eacute;todo.<\/li>\r\n<\/ul>\r\n<p><code> <\/code><br>\r\n<b>Estad&iacute;sticas adicionales:<\/b> EQ_BIN_* RQ_BIN_* Por capacidad utilizada.<\/p>\r\n<pre>\r\nStorage {NAME:Sto1, CAPACITY:3, X:100, Y:100,METHOD:MIN_SPACE}\r\nStorage {NAME:Sto2, CAPACITY:3, X:100, Y:100, ON_ATTEMPT:Fac1_attempt, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }\r\n;------------------------\r\nEnter Sto2,6\r\nADVANCE 10\r\nRELEASE Sto2<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "300",
                        "nombre": "Restroom",
                        "texto": "\/*\r\n\r\nRecursos. Restroom\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n;SYSTEM {TYPE:OPTIONS,Speed:8}\r\n\r\nRestroom {NAME:Restroom_agentes,X:100,Y:100}\r\n\r\n\r\nPOSITION {NAME:POS1,X:237,Y:449}\r\nPOSITION {NAME:POS2,X:269,Y:334}\r\nPOSITION {NAME:POS3,X:272,Y:202,type:terminate,title:end}\r\n\r\nGraphic {NAME:textAgente,Type:TEXT,X:430,Y:471,Text:\"Agente\"}\r\nGraphic {NAME:Text2,Type:TEXT,X:431,Y:523,Text:\"Entidad\"}\r\n\r\ninitial conta,0\r\ninitial nAgente,0\r\nSTART 500\r\n;*****************************************************\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente.main,1\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,0 {NAME:GEN1,X:43,Y:300}\r\nADVANCE 20,0 {TO:POS1}\r\nif (D$N%4==2)\r\n    signalnow agente.Suma,X$nAgente\r\n    signalnow agente.Resta,X$nAgente\r\n    savevalue resultado,P$(agenteDato,X$nAgente)\r\n    move {name:Text2,text:\"Soy Entidad D$N : resultado X$resultado\"}\r\nendif\r\nADVANCE 20,0 {TO:POS2}\r\nADVANCE 20,0 {TO:POS3}\r\nENDGENERATE 1\r\n\r\n;*******************************\r\nprocedure agente.main\r\n\tsavevalue nAgente,D$N\r\n    assign agenteDato,10\r\n    while (1==1)\r\n    move {name:textAgente,text:\"Soy Agente X$nAgente [AC1: AC1$]\"}\r\n    REST Restroom_agentes\r\n    endwhile\r\nendprocedure\r\n;**********************************\r\nprocedure agente.Suma\r\n    ASSIGN agenteDato,P$agenteDato + 2\r\n    return_restore\r\nendprocedure\r\n;**********************************\r\n\r\nprocedure agente.Resta\r\n    ASSIGN agenteDato,P$agenteDato - 1\r\n    return_restore\r\nendprocedure\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\">RESTROOM<\/strong><\/p>\r\n<p>Modela un recurso retenedor.<br data-start=\"769\" data-end=\"772\">\r\nLas entidades entran y quedan retenidas autom&aacute;ticamente en espera de que otra entidad la libre.<br>\r\nInternamente gestiona una lista: Entidades ocupantes.<br>\r\nEste recurso es especialmente &uacute;til para dormir agentes o mantener entidades en espera pasiva (no activa).<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>REST&nbsp;<\/b>nombreRecurso ; La entidad queda retenida<\/li>\r\n    <li><b>WAKE&nbsp;<\/b>nombreRecurso[,cantidad de entidades a  liberar][,n&uacute;mero de entidad a liberar];&nbsp;Libera las entidades  determinadas si se especifica (0 &oacute; -1 si es por numero de entidad) o libera solo el n&uacute;mero de entidad si se  especifica<\/li>\r\n<\/ul>\r\n<p>Eventos disponibles:<code>&nbsp;ON_REST<\/code>, <code>ON_WAKE<\/code><\/p>\r\n<pre>\r\nRestroom {NAME:Agentes, X:100, Y:100, ON_REST:Restroom1_onrest, E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:40 }\r\n;------------------------\r\nREST Agentes ; el agente para a inactivo\r\n;------------------------\r\nWAKE Agentes ; Despiesta todos los agentes\r\nWAKE Agentes,0,X$nAgente ; Despierta un agente en concreto<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "302",
                        "nombre": "Conditions",
                        "texto": "\/*\r\n\r\nRecursos. Conditions\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 350}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER2, INTERVAL: 650}\r\n\r\nPOSITION {NAME:Salida,X:615,Y:388}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}\r\n\r\nConditions {NAME:Conditions1,X:324,Y:160\r\n\t,expression:(D$N % 3 == 0)\r\n\t,ON_ATTEMPT:Conditions1_ATTEMPT\r\n\t,ON_CHECK:Conditions1_CHECK\r\n\t,ON_QUEUE:Conditions1_QUEUE\r\n    }\r\n\r\n\r\nSTART 100\r\n\r\n;-------------------------------\r\n\r\nGENERATE 20,0 {NAME:GEN1,X:62,Y:396}\r\n\r\nadvance 10 {to:Conditions1}\r\n\r\nwaituntil Conditions1,(D$N!=3)\r\n\r\nmove {name:Text1,text:\"AC1$ Soy la Entidad [D$N] y avanzo\"}\r\nadvance 100,50 {to:Salida}\r\n\r\nendgenerate 1\r\n;-------------------------------\r\nPROCEDURE TIMER1\r\n\twaitcheck Conditions1,(D$N%2==1)\r\n\tmove {name:Text6,text:\"AC1$ Soy TIMER [D$N] WAITCHECK las impares\"}\r\n\tTERMINATE_VE \r\nENDPROCEDURE \r\n\r\nPROCEDURE TIMER2\r\n\twaitcheck Conditions1,(1==1)\r\n\tmove {name:Text7,text:\"AC1$ Soy TIMER [D$N] WAITCHECK TODOS\"}\r\n\tTERMINATE_VE \r\nENDPROCEDURE \r\n\r\nPROCEDURE Conditions1_QUEUE\r\n\tmove {name:Text3,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE Conditions1_CHECK\r\n\tmove {name:Text4,text:\"AC1$ Soy la VE [D$N] de ON_CHECK la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\nPROCEDURE Conditions1_ATTEMPT\r\n\tmove {name:Text5,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n;-------------------------------\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\">Conditions<\/strong><\/p>\r\n<p>Modela un recurso retenedor condicional.<br>\r\nUna entidad puede acceder directamente o quedar retenida seg&uacute;n se cumplan ciertas condiciones l&oacute;gicas, tanto generales como particulares.<\/p>\r\n<p><b>L&oacute;gica de retenci&oacute;n<\/b>:<\/p>\r\n<p data-start=\"657\" data-end=\"701\">El recurso eval&uacute;a tres tipos de condiciones:<\/p>\r\n<ul data-start=\"703\" data-end=\"1020\">\r\n    <li data-start=\"703\" data-end=\"806\">\r\n    <p data-start=\"705\" data-end=\"806\"><strong data-start=\"705\" data-end=\"726\">EXPRESI&Oacute;N GENERAL<\/strong> (<code data-start=\"728\" data-end=\"739\">EXPRESSION<\/code>)<br data-start=\"740\" data-end=\"743\">\r\n    Definida en el propio recurso (afecta a todas las entidades).<\/p>\r\n    <\/li>\r\n    <li data-start=\"808\" data-end=\"903\">\r\n    <p data-start=\"810\" data-end=\"903\"><strong data-start=\"810\" data-end=\"834\">EXPRESI&Oacute;N PARTICULAR<\/strong><br data-start=\"834\" data-end=\"837\">\r\n    Especificada en el bloque <code data-start=\"865\" data-end=\"876\">WAITUNTIL<\/code> para una entidad concreta.<\/p>\r\n    <\/li>\r\n    <li data-start=\"905\" data-end=\"1020\">\r\n    <p data-start=\"907\" data-end=\"1020\"><strong data-start=\"907\" data-end=\"929\">EXPRESI&Oacute;N ABSOLUTA<\/strong><br data-start=\"929\" data-end=\"932\">\r\n    Indicada en <code data-start=\"946\" data-end=\"957\">WAITCHECK<\/code>, y sobreescribe las dem&aacute;s. Si se cumple, libera las entidades.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1022\" data-end=\"1202\">Para que una entidad contin&uacute;e, deben cumplirse <strong data-start=\"1069\" data-end=\"1090\">ambas expresiones<\/strong> (GENERAL y PARTICULAR), salvo que se utilice una EXPRESI&Oacute;N ABSOLUTA, que act&uacute;a como &uacute;nica condici&oacute;n liberadora.<\/p>\r\n<p>La liberaci&oacute;n o paso de la entidad se produce a trav&eacute;s del chequeo. Este chequeo se produce siempre que una entidad es liberada o cuando se solicita expresamente.<br>\r\n<br>\r\nLas expresiones siempre se indican entre par&eacute;ntesis.<br>\r\n<br data-start=\"769\" data-end=\"772\">\r\nInternamente gestiona una lista: Entidades ocupantes.<br>\r\nEste recurso es especialmente &uacute;til como multi-sem&aacute;foro general o sala de espera para varios recursos en conjunto.<br>\r\nEl chequeo puede ser cada tick o tiempo determinado a trav&eacute;s de un TIMER.<br>\r\nLo habitual es establecer los chequeos en los HOOKs de los recursos implicados.<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>WAITUNTIL&nbsp;<\/b>nombreRecurso[,(expresionParticular)] ; La entidad queda retenida si no cumple la expresi&oacute;n ABSOLUTA o la PARTICULAR (si est&aacute;n definidas)<\/li>\r\n    <li><b>WAITCHECK&nbsp;<\/b>nombreRecurso[,(expresionAbsoluta)]; Chequea todas las entidades retenidas. Si se indica la expres&iacute;on ABSOLUTA, &eacute;sta ser&aacute; el &uacute;nico criterio liberador.<\/li>\r\n<\/ul>\r\n<p>Eventos disponibles:<code>&nbsp;ON_ATTEPMT<\/code>, <code>ON_CHECK, ON_QUEUE<\/code><\/p>\r\n<pre>\r\nConditions {NAME:semaforo, EXPRESSION:(D$N %2 == 1) X:100, Y:100, ON_CHECK:semaforo_check}\r\n;------------------------\r\nWAITUNTIL semaforo; La entidad contin&uacute;a si es impar; (La entidad queda retenida si su n&uacute;mero es par.)\r\nWAITUNTIL semaforo,(D$N %3 == 0); La entidad contin&uacute;a si es impar y m&uacute;ltiplo de 3. (3,9,15...)\r\n;------------------------\r\nWAITCHECK semaforo; Chequea todas las entidades retenidas\r\nWAITCHECK semaforo,(1==1) ; Libera todas las entidades retenidas\r\nWAITCHECK semaforo,(D$N %2 == 0) ; Libera todas las entidades pares<\/pre>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "301",
                        "nombre": "Stock",
                        "texto": "\/*\r\n\r\nRecursos. Stock\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:MARKET1_TIMER, INTERVAL: 20}\r\n\r\n\r\nSTOCK {NAME:MARKET1,X:331,Y:210\r\n\t ,ON_QUEUE:MARKET1_QUEUE\r\n \t ,ON_STOCKIN:MARKET1_STOCKIN\r\n     ,ON_STOCKOUT:MARKET1_STOCKOUT\r\n     ,ON_ATTEMPT:MARKET1_ATTEMPT\r\n     ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20\r\n     ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100\r\n     }\r\n\r\nPOSITION {NAME:POS1,X:70,Y:113}\r\nPOSITION {NAME:POS2,X:595,Y:283}\r\n\r\ninitial posY,500\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 140}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 160}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 180}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 200}\r\n\r\nSTART 300 \r\n;*****************************************************************\r\n; PROVEEDORES: Generan productos para añadir al STOCK\r\nGENERATE 115,0 {NAME:PROVEEDORES,X:619,Y:194,ECOLOR:#006666,ERADIO:15}\r\n\r\n\r\n\r\nADVANCE 20,0 {TO:MARKET1}\r\n\r\nassign mesa,ROUND(RANDOM * 30)\r\nassign silla,round(random * 100)\r\nassign armario,round(random * 5)\r\nassign lampara,round(random * 5)\r\n\r\n\r\nassign camion,{mesa:P$mesa,silla:P$silla,armario:P$armario}\r\nassign.merge camion,{lampara:P$lampara}\r\nSTOCKIN MARKET1, V$camion\r\n\r\nMOD {RADIO:5}\r\nADVANCE 20,0 {TO:POS2}\r\nTERMINATE 1\r\n\r\n;*****************************************************************\r\n; CLIENTES: Consumen productos del STOCK\r\nGENERATE 20,2 {NAME:CLIENTES,X:81,Y:207,ECOLOR:#666666}\r\n\r\nassign carrito,{}\r\nassign.merge carrito,{mesa:2}\r\nassign.merge carrito,{silla:4}\r\n\r\nif (SC$(MARKET1,carrito)==0)\r\nmove {name:Text1,text:\"SIN STOCK\"}\r\nelse\r\nmove {name:Text1,text:\"STOCK SUFICIENTE\"}\r\nendif\r\n\r\nADVANCE 10,0 {TO:MARKET1}\r\n\r\nSTOCKOUT MARKET1, V$carrito\r\nMOD {RADIO:8}\r\nADVANCE 50,50 {TO:POS1}\r\nTERMINATE 1\r\n\r\n;-------------------------------\r\n\r\nPROCEDURE MARKET1_STOCKOUT\r\n\t; MAPPER es objeto {mesa:N,silla:N}\r\n    move {name:Text2,text:\"ON_STOCKOUT Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\nPROCEDURE MARKET1_STOCKIN\r\n\t; MAPPER es objeto {mesa:N,silla:N}\r\n    move {name:Text3,text:\"ON_STOCKIN Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\nPROCEDURE MARKET1_ATTEMPT\r\n\t; MAPPER es objeto {mesa:N,silla:N}\r\n    move {name:Text4,text:\"ON_ATTEMPT Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\nPROCEDURE MARKET1_QUEUE\r\n\t; MAPPER es objeto {mesa:N,silla:N}\r\n    move {name:Text5,text:\"ON_QUEUE Mesas: P$(MAPPER.mesa) Sillas: P$(MAPPER.silla)\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n;-------------------------------\r\nPROCEDURE MARKET1_TIMER\r\n\tassign tmp,VS$(MARKET1)\r\n\tassign txt,\"\"\r\n\tforeach clave,IN_OBJECT,V$(tmp) \r\n       assign txt,\"P$(txt) | P$(clave) : P$(tmp.P$(clave))\"\r\n    endforeach\r\n    move {name:Text6,text:\"AC1$ ON_TIMER [P$txt]\"}\r\n    TERMINATE_VE\r\nendprocedure \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\">STOCK<\/strong><\/p>\r\n<p>STOCK modela un almac&eacute;n inteligente de productos etiquetados, que permite entradas y salidas de items con tipo y cantidad. Soporta m&uacute;ltiples entradas simult&aacute;neas, comprobaciones de disponibilidad y eventos (hooks) conectables a l&oacute;gica externa.<br>\r\n<br>\r\nEs ideal para modelar inventarios, mercados, almacenes de componentes, etc.<\/p>\r\n<p><br>\r\n<b>Estructura y comportamiento<\/b><br>\r\nEl recurso almacena productos en forma de objetos {clave: cantidad}, organizados internamente por tipo (clave).<br>\r\nPermite m&uacute;ltiples operaciones simult&aacute;neas de carga y descarga, acumulando cantidades por producto.<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>STOCKIN&nbsp;<\/b>nombreRecurso,nombreVariable ; A&ntilde;ade productos al Stock. La variable debe ser un objeto con forma <code data-start=\"1109\" data-end=\"1128\">{clave1: cantidad1 , clave2:cantidad2}<\/code>.<\/li>\r\n    <li><b>STOCKOUT<\/b><b>&nbsp;<\/b>nombreRecurso,nombreVariable ; Extrae productos del STOCK, si hay disponibilidad suficiente.<br>\r\n    La variable debe ser un objeto con forma&nbsp;<code data-start=\"1109\" data-end=\"1128\">{clave1: cantidad1 , clave2:cantidad2}<\/code>.<\/li>\r\n<\/ul>\r\n<p><b>SNA asociados:<\/b><br>\r\n<code>SC$(recurso, objeto)<\/code>&nbsp;Retorna 0 &oacute; 1 si el recurso tiene suficientes cantidades del objeto para poder realizar STOCKOUT.<\/p>\r\n<p><code>R$(nombre,STOCK,clave)<\/code>&nbsp; Devuelve el stock actual de un tipo concreto.<br>\r\n<br>\r\n<code>VS$(nombre)<\/code> &rarr; Devuelve el stock completo como objeto {clave1: qty1 , clave2: qty2, ...}.<\/p>\r\n<p><br>\r\n<b>Eventos disponibles:<\/b><code>ON_ATTEMPT<\/code>, <code>ON_STOCKIN<\/code>, <code>ON_STOCKOUT<\/code>, <code>ON_QUEUE<\/code><\/p>\r\n<pre>\r\nSTOCK {NAME:MARKET1,X:331,Y:210\r\n     ,ON_QUEUE:MARKET1_QUEUE\r\n     ,ON_STOCKIN:MARKET1_STOCKIN\r\n     ,ON_STOCKOUT:MARKET1_STOCKOUT\r\n     ,ON_ATTEMPT:MARKET1_ATTEMPT\r\n     ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20\r\n     ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100\r\n     }\r\n;------------------------\r\nassign mesa,round(random * 30)\r\nassign silla,round(random * 100)\r\nassign armario,round(random * 5)\r\nassign lampara,round(random * 5)\r\nassign camion,{mesa:P$mesa,silla:P$silla,armario:P$armario}\r\nassign.merge camion,{lampara:P$lampara}\r\nSTOCKIN MARKET1, CAMION\r\n;------------------------\r\n\r\n<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "347",
                        "nombre": "Backpack",
                        "texto": "RESTROOM {NAME:almacenReceptor1,X:220,Y:348,E_BIN_SIZE:1,E_BIN_COUNT:10}\r\nRESTROOM {NAME:almacenReceptor2,X:217,Y:182,E_BIN_SIZE:1,E_BIN_COUNT:10}\r\nRESTROOM {NAME:almacenDistribucion,X:662,Y:93}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:100,Y:100,Text:\"Terminado: NO\",font:\"20\",color:red}\r\n\r\n\r\nSTART 1000\r\n;************************************************************************\r\n\r\nGENERATE 8,3 {NAME:Recepcion1,X:66,Y:356}\r\nADVANCE 20,0 {TO:almacenReceptor1}\r\nrest almacenReceptor1\r\nterminate 1\r\n\r\n;************************************************************************\r\n\r\nGENERATE 5,3 {NAME:Recepcion2,X:65,Y:181}\r\nADVANCE 20,0 {TO:almacenReceptor2}\r\nrest almacenReceptor2\r\nterminate 1\r\n\r\n;************************************************************************\r\n\r\nGENERATE 40,0,0,1 {NAME:Furgonetas,X:40,Y:567\r\n\t\t,eradio:16,ecolor:red,visible:0\r\n\t\t,esubtitle:\"P$(mochila1.LENGTH)\"}\r\nADVANCE0 {TO:PuntoDistribucion}\r\nwhile (1==1)\r\n    ADVANCE 20,30 {TO:almacenReceptor1}\r\n    load mochila1,almacenReceptor1\r\n    ADVANCE 20,30 {TO:almacenReceptor2}\r\n    load mochila1,almacenReceptor2\r\n\r\n\tADVANCE 20,30 {TO:PuntoDistribucion}\r\n    unload mochila1,PuntoDistribucion\r\nendwhile\r\nterminate 1\r\n\r\n;************************************************************************\r\n\r\nGENERATE 0,0,0,0 {NAME:PuntoDistribucion,X:669,Y:434}\r\nADVANCE0 {TO:PuntoDistribucion}\r\n\r\nADVANCE 20,50 {TO:almacenDistribucion}\r\nif (D$N>1000)\r\nmove {name:Text1,text:\"TERMINADO: SÍ\",color:green}\r\nstop\r\nendif\r\nrest almacenDistribucion\r\n\r\nterminate 1\r\n\r\n\r\n\r\n",
                        "descripcion": "<p>Backpack no es un recurso como tal ya que en realidad no realiza funciones internas, es m&aacute;s bien una herramienta de transporte de entidades, pero s&iacute; es cierto que las retiene.<\/p>\r\n<p>Para poder llevar entidades de un punto a otro de la simulaci&oacute;n, por ejemplo, una furgoneta con paquetes se necesita algo que indique al sistema que esas entidades ya no est&aacute;n disponibles en la cola de eventos ni en ninguna otra cola.<\/p>\r\n<p>Este concepto podr&iacute;amos llamarlo mochila (Backpack) donde una entidad podr&aacute; transportar entidades. Existen dos limitaciones:<\/p>\r\n<p>Solo se puede cargar desde un <code>RESTROOM <\/code>ya que es un recurso retenedor en la que las entidades no salen por s&iacute; mismas y s&oacute;lo se pueden descargar en un <code>GENERATE<\/code> ya que es el &uacute;nico punto seguro con respecto a la pila de direcciones de la entidad (v&eacute;ase <code>SCAPE<\/code>).<\/p>\r\n<p>Tiene dos bloques asociados:<\/p>\r\n<ul>\r\n    <li><code>LOAD <\/code>nombreMochila,nombreRestroom[,n&uacute;meroEntidad]<\/li>\r\n    <li><code>UNLOAD <\/code>nombreMochila,nombreGenerate[,n&uacute;meroEntidad]<\/li>\r\n<\/ul>\r\n<p>El primero carga en la mochila todo el contenido de un Restroom o la entidad con ese n&uacute;mero y el segundo es su paso inverso en el <code>GENERATE<\/code>.<\/p>\r\n<p>Las entidades en la mochila pueden obtenerse a trav&eacute;s del propio <code>ASSIGN <\/code>generado con el mismo nombre de la mochila. Es un array con los n&uacute;meros de las entidades.<\/p>\r\n<p>El ejemplo es el recorrido de una furgoneta que pasa por dos puntos de recogida y lleva los paquetes al punto de distribuci&oacute;n.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "345",
                "nombre": "Recursos lógicos",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "372",
                        "nombre": "Table",
                        "texto": "FACILITY {NAME:VENTANILLA1,X:380,Y:348,capacity:4}\r\nPOSITION {NAME:POS1,X:218,Y:437}\r\nPOSITION {NAME:POS2,X:591,Y:429}\r\nPOSITION {NAME:POS3,X:713,Y:329}\r\n\r\nGraphic {NAME:Line1,Type:L,color:#FF0000, X1:218,Y1:500,X2:592,Y2:500}\r\nGraphic {NAME:Text1,Type:T,X:410,Y:527,Text:\"Estadistic section\"}\r\n\r\nTABLE {name: TABLA1,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:100,EXPRESSION:(M1$ - P$TIEMPOINICIO)}\r\n\r\nSTART 100 \r\n\r\n;***************************************************************\r\n\r\n\r\nGENERATE 8,3 {NAME:GEN1,X:66,Y:350}\r\n\r\nADVANCE 30,0 {TO:POS1}\r\nASSIGN TIEMPOINICIO,M1$\r\nADVANCE 20,0 {TO:VENTANILLA1}\r\n\r\nSEIZE VENTANILLA1\r\nADVANCE 20,20\r\nRELEASE VENTANILLA1\r\n\r\n\r\nADVANCE 20,0 {TO:POS2}\r\nTABULATE TABLA1\r\nADVANCE 20,0 {TO:POS3}\r\n\r\nTERMINATE 1\r\n\r\n;***************************************************************",
                        "descripcion": "",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "304",
                        "nombre": "FSM - Máquina de Estados Finitos *",
                        "texto": "POSITION {NAME:Salida,X:648,Y:383}\r\n\r\n\r\ninitial posY,500\r\ninitial logic, { STATES: [\"open\", \"close\"],\r\n    TRANSITIONS: [\r\n        {FROM: \"open\", INPUT: \"close\", TO: \"close\", TRIGGER: \"puertaAbierta\"},\r\n        {FROM: \"close\", INPUT: \"open\", TO: \"open\"},\r\n        {FROM: \"close\", INPUT: \"switch\", TO: \"open\"},\r\n        {FROM: \"open\", INPUT: \"switch\", TO: \"close\"}\r\n    ],\r\n    INITIAL: \"open\"\r\n    }\r\n\r\nFSM {NAME:FSM1,X:434,Y:540, LOGIC:V$(logic)}\r\n\r\n\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:X$posY - 0}\r\nGraphic {NAME:Text2,Type:TEXT,X:324,Y:X$posY - 20}\r\nGraphic {NAME:Text3,Type:TEXT,X:324,Y:X$posY - 40}\r\nGraphic {NAME:Text4,Type:TEXT,X:324,Y:X$posY - 60}\r\nGraphic {NAME:Text5,Type:TEXT,X:324,Y:X$posY - 80}\r\nGraphic {NAME:Text6,Type:TEXT,X:324,Y:X$posY - 100}\r\nGraphic {NAME:Text7,Type:TEXT,X:324,Y:X$posY - 120}\r\nGraphic {NAME:Text8,Type:TEXT,X:324,Y:X$posY - 140}\r\n\r\nConditions {NAME:Conditions1,X:317,Y:539\r\n\t,expresion:(\"R$(FSM1,IN_AFTER,close)\"==\"open\")\r\n\t,ON_ATTEMPT:Conditions1_ATTEMPT\r\n\t,ON_CHECK:Conditions1_CHECK\r\n\t,ON_QUEUE:Conditions1_QUEUE\r\n    }\r\n\r\n\r\nSTART 100\r\n\r\n;-------------------------------\r\n\r\nGENERATE 20,0 {NAME:GEN1,X:62,Y:396}\r\n    advance 10 {to:Conditions1}\r\n    waituntil Conditions1\r\n    advance 15,25 {to:Salida}\r\n    STATE FSM1,\"open\"\r\n    waitcheck Conditions1\r\nterminate 1\r\n;-------------------------------\r\nPROCEDURE Conditions1_QUEUE\r\n\tmove {name:Text3,text:\"AC1$ Soy la VE [D$N] de ON_QUEUE la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure 1\r\nPROCEDURE Conditions1_CHECK\r\n\tmove {name:Text4,text:\"AC1$ Soy la VE [D$N] de ON_CHECK la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure 1\r\nPROCEDURE Conditions1_ATTEMPT\r\n\tmove {name:Text5,text:\"AC1$ Soy la VE [D$N] de ON_ATTEMPT la entidad P$ENTITYNUMBER\"}\r\n    TERMINATE_VE\r\nendprocedure 1\r\n;-------------------------------\r\nPROCEDURE puertaAbierta\r\n\tmove {name:Text7,text:\"La entidad P$ENTITYNUMBER ha abierto la puerta\"}\r\n    TERMINATE_VE\r\nendprocedure 1\r\n;-------------------------------\r\n\r\n\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\">FSM - M&aacute;quina de Estados Finitos<\/strong><\/p>\r\n<p data-start=\"231\" data-end=\"434\">El bloque <code data-start=\"241\" data-end=\"249\">FSM<\/code> permite modelar una <strong data-start=\"270\" data-end=\"300\">M&aacute;quina de Estados Finitos<\/strong>. Se trata de un recurso que gestiona estados internos definidos mediante una tabla de transiciones, activadas por entradas (<code>INPUT<\/code>).<\/p>\r\n<p data-start=\"436\" data-end=\"524\">La l&oacute;gica de funcionamiento se define mediante un objeto JSON con los siguientes campos:<\/p>\r\n<ul>\r\n    <li><code data-start=\"528\" data-end=\"536\">STATES<\/code>: Lista de estados posibles (opcional si se usa <code>EVAL<\/code><code data-start=\"584\" data-end=\"593\">: 1<\/code>)<\/li>\r\n    <li><code data-start=\"597\" data-end=\"610\">TRANSITIONS<\/code>: Lista de transiciones entre estados<\/li>\r\n    <li><code data-start=\"650\" data-end=\"659\">INITIAL<\/code>: Estado inicial (*S&oacute;lo para LOCAL:0)<\/li>\r\n    <li><code data-start=\"678\" data-end=\"684\">EVAL<\/code>: (opcional) Si es <code data-start=\"703\" data-end=\"706\">1<\/code>, permite usar expresiones matem&aacute;ticas en <code>TO<\/code><code data-start=\"748\" data-end=\"752\">.<\/code><\/li>\r\n<\/ul>\r\n<p data-start=\"436\" data-end=\"524\">&nbsp;Cada transici&oacute;n puede contener:<\/p>\r\n<ul>\r\n    <li><b>FROM<\/b>: define el estado previo. Si se define como <code>&quot;&quot;<\/code>&nbsp;act&uacute;a como comod&iacute;n (acepta cualquier estado).<\/li>\r\n    <li><b>INPUT<\/b>:&nbsp;nombre del input. <code data-start=\"979\" data-end=\"983\">&quot;&quot;<\/code> act&uacute;a como comod&iacute;n.<\/li>\r\n    <li><b>TO<\/b>:&nbsp; nuevo estado (valor directo o expresi&oacute;n si <code>EVAL:1<\/code>).<\/li>\r\n    <li><b>TRIGGER<\/b>:&nbsp; <em data-start=\"1067\" data-end=\"1079\">(opcional)<\/em> procedimiento que se dispara al hacer la transici&oacute;n.<\/li>\r\n<\/ul>\r\n<pre>\r\n; EJEMPLO B&Aacute;SICO\r\ninitial logic, { STATES: [&quot;open&quot;, &quot;close&quot;],\r\n&nbsp; &nbsp; TRANSITIONS: [\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;open&quot;, INPUT: &quot;close&quot;, TO: &quot;close&quot;, TRIGGER: &quot;puertaAbierta&quot;},\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;close&quot;, INPUT: &quot;open&quot;, TO: &quot;open&quot;},\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;close&quot;, INPUT: &quot;switch&quot;, TO: &quot;open&quot;},\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;open&quot;, INPUT: &quot;switch&quot;, TO: &quot;close&quot;}\r\n&nbsp; &nbsp; ],\r\n&nbsp; &nbsp; INITIAL: &quot;open&quot;\r\n&nbsp; &nbsp; }\r\n\r\nFSM {NAME:FSM1,X:434,Y:540, LOGIC:V$(logic)}\r\n\r\n\r\n; EJEMPLO CICLICO\r\ninitial logic2, { states: [&quot;uno&quot;, &quot;dos&quot;, &quot;tres&quot;],\r\n&nbsp; &nbsp; TRANSITIONS: [\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;uno&quot;, INPUT: &quot;siguiente&quot;, TO: &quot;dos&quot;},\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;dos&quot;, INPUT: &quot;siguiente&quot;, TO: &quot;tres&quot;},\r\n&nbsp; &nbsp; &nbsp; &nbsp; {FROM: &quot;tres&quot;, INPUT: &quot;siguiente&quot;, TO: &quot;uno&quot;, TRIGGER: &quot;reinicio&quot;}\r\n&nbsp; &nbsp; ],\r\n&nbsp; &nbsp; INITIAL: &quot;open&quot;\r\n&nbsp; &nbsp; }\r\nFSM {NAME:FSM2,X:134,Y:140, LOGIC:V$(logic2)}\r\n\r\n\r\n; EJEMPLO L&Oacute;GICA EVALUADA\r\n; En este modo (<code data-start=\"1592\" data-end=\"1601\">eval: 1<\/code>), los estados y transiciones pueden evaluarse como expresiones matem&aacute;ticas din&aacute;micas.\r\n\r\ninitial logicTemp3, {\r\n  TRANSITIONS: [\r\n    {FROM: &quot;&quot;, INPUT: &quot;sube&quot;, TO: &quot;(X + 1 &lt;= 24 ? X + 1.5 : X)&quot;},\r\n    {FROM: &quot;&quot;, INPUT: &quot;baja&quot;, TO: &quot;(X - 1 &gt;= 18 ? X - 1.5 : X)&quot;},\r\n    {FROM: &quot;&quot;, INPUT: &quot;reset&quot;, TO: &quot;21&quot;}\r\n  ],\r\n  EVAL: 1,\r\n  INITIAL: 21\r\n}\r\n\r\nFSM {NAME:Stater3, X:434, Y:540, LOGIC:V$(logicTemp3)}\r\n\r\n\r\n; EJEMPLO L&Oacute;GICA EVALUADA CON PAR&Aacute;METRO ADICIONAL Y\r\n; SE PUEDEN A&Ntilde;ADIR PAR&Aacute;METROS Y, Z A AS FUNCIONES \r\n\r\ninitial logicTemp4, {\r\n  TRANSITIONS: [\r\n    {FROM: &quot;&quot;, INPUT: &quot;sube&quot;, TO: &quot;(X + 1 &lt;= 24 ? X + Y : X)&quot;},\r\n    {FROM: &quot;&quot;, INPUT: &quot;baja&quot;, TO: &quot;(X - 1 &gt;= 18 ? X - Y : X)&quot;},\r\n    {FROM: &quot;&quot;, INPUT: &quot;reset&quot;, TO: &quot;21&quot;}\r\n  ],\r\n  EVAL: 1,\r\n  INITIAL: 21\r\n}\r\n\r\nFSM {NAME:Stater4, X:434, Y:540, LOGIC:V$(logicTemp4)}<\/pre>\r\n<p><b>Bloques asociados:<\/b><\/p>\r\n<ul>\r\n    <li><b>STATE&nbsp;<\/b>nombreRecurso,input[, paramY [,paramZ]] ; Ejecuta una transici&oacute;n de estado seg&uacute;n la l&oacute;gica definida.<\/li>\r\n<\/ul>\r\n<pre><b>STATE<\/b> Fsm1,&quot;OPEN&quot; ; pasa a estado &quot;CLOSE&quot;\r\n<b>STATE<\/b> Fsm1,&quot;SWITCH&quot; ; si estaba en &quot;OPEN&quot; pasa a &quot;CLOSE&quot; y viceversa.\r\n\r\n<b>STATE<\/b> Fsm2,&quot;siguiente&quot; ; si estaba en &quot;DOS&quot; pasa a &quot;TRES&quot;,... \r\n\r\n\r\n<b>STATE<\/b> Fsm4,&quot;subir&quot;,10 ; incrementa el state en 10<\/pre>\r\n<p><b><\/b><b>SNA asociado:<\/b><\/p>\r\n<p>FSM es el &uacute;nico recurso que ofrece una <b>operaci&oacute;n at&oacute;mica de consulta y modificaci&oacute;n del estado<\/b>.<\/p>\r\n<ul>\r\n    <li>R$(nombreFSM,STATE) Devuelve el estado actual.<\/li>\r\n    <li>R$(nombreFSM,IN_AFTER,input) Devuelve el estado actual y ejecuta el input en&nbsp;<b>OPERACI&Oacute;N AT&Oacute;MICA.<\/b><\/li>\r\n    <li>R$(nombreFSM,IN_BEFORE,input)&nbsp;Ejecuta el input y devuelve el estado actual en&nbsp;<b>OPERACI&Oacute;N AT&Oacute;MICA.<\/b><\/li>\r\n    <li>R$(nombreFSM,IN_BEFORE,input,Y,Z)&nbsp;Ejecuta el input con par&aacute;metros Y,Z para FSM tipo EVAL:1 y devuelve el estado actual en&nbsp;<b>OPERACI&Oacute;N AT&Oacute;MICA.<\/b><\/li>\r\n<\/ul>\r\n<p>Esto permite modelar <strong data-start=\"2079\" data-end=\"2140\">zonas cr&iacute;ticas, sem&aacute;foros y sincronizaci&oacute;n entre procesos<\/strong>.<\/p>\r\n<p><b>Usos t&iacute;picos<\/b><\/p>\r\n<ul>\r\n    <li>Control de zonas cr&iacute;ticas<\/li>\r\n    <li>Sem&aacute;foros<\/li>\r\n    <li>Exclusi&oacute;n mutua<\/li>\r\n    <li>Sincronizaci&oacute;n de procesos<\/li>\r\n    <li>Funciones matem&aacute;ticas con estado y par&aacute;metros<\/li>\r\n<\/ul>\r\n<pre>\r\nwaituntil Conditions1,(&quot;R$(Fsm1,IN_AFTER,close)&quot;==&quot;open&quot;) ; la entidad pasa y retiene las posteriores\r\n...\r\n<b>STATE<\/b> Fsm1,&quot;open&quot; ; finaliza la zona de exclusi&oacute;n mutua\r\nwaitcheck Conditions1 ; da paso al siguiente<\/pre>\r\n<p>&nbsp;Y como almac&eacute;n de funciones matem&aacute;ticas con ejecuci&oacute;n de dos par&aacute;metros adicionales:<\/p>\r\n<pre>\r\n{ FROM: &quot;&quot;, INPUT: &quot;incr&quot;, TO: &quot;(X + Y * Z)&quot; } ; X: State actual, Y,Z Par&aacute;metros extra de input\r\n\r\n<\/pre>\r\n<p><b>Eventos disponibles:<\/b>&nbsp;TRIGGER<\/p>\r\n<p>Cada transici&oacute;n puede incluir un trigger, que llamar&aacute; a un <code>PROCEDURE<\/code>:<\/p>\r\n<pre>\r\nPROCEDURE puertaAbierta\r\n&nbsp; &nbsp; move {name:text7,text:&quot;La entidad P$ENTITYNUMBER ha abierto la puerta&quot;}\r\n&nbsp; &nbsp; TERMINATE_VE\r\nendprocedure<\/pre>\r\n<p><b>Modos de almacenamiento:<\/b><\/p>\r\n<p>Su modo por defecto es global, un estado por <code>STATER<\/code>, pero si se define:<\/p>\r\n<p><code>LOCAL:1<\/code><\/p>\r\n<p>su modo es de un estado para cada entidad. Perfecto como discriminador de caminos que evita el uso de <code>SWITCH<\/code> anidados.<\/p>\r\n<p>Dado que no se assigna valor inicialmente, &eacute;ste se asignar&aacute; mediente el acceso directo al assign asociado:<\/p>\r\n<pre>\r\nassign Fsm1,10<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "303",
                        "nombre": "Queuer",
                        "texto": "\/*\r\n\r\nRecursos. Queuer\r\n\r\n*\/\r\n;SYSTEM {TYPE:OPTIONS,Speed:50,Pause:0}\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\n\r\nQUEUER {NAME:Qventanilla1,X:374,Y:424\r\n\t,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 \r\n    ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20}\r\n\r\nFacility {NAME:ventanilla1,X:373,Y:365,capacity:5}\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 10 {TO:ventanilla1}\r\n\r\nqueue Qventanilla1\r\nseize ventanilla1\r\ndepart Qventanilla1\r\nADVANCE 20,10\r\nrelease ventanilla1\r\n\r\nADVANCE 10 {TO:POS1}\r\nENDGENERATE 1\r\n",
                        "descripcion": "<p><strong data-start=\"678\" data-end=\"690\"><\/strong><strong data-start=\"678\" data-end=\"690\">QUEUER<\/strong><\/p>\r\n<p>Modela un recurso abierto.<br data-start=\"769\" data-end=\"772\">\r\nPermite m&uacute;ltiples entidades sin criterio.<br>\r\nInternamente gestiona una lista de entidades en el recurso.<br>\r\n<br>\r\nSu uso principal es estad&iacute;stico o como agrupador de entidades.<\/p>\r\n<p>Bloques asociados:<\/p>\r\n<ul>\r\n    <li><b>QUEUE&nbsp;<\/b>nombreRecurso ; Intenta ocupar el recurso si hay capacidad disponible. Si no, la entidad se encola.<\/li>\r\n    <li><b>DEPART&nbsp;<\/b>nombreRecurso;&nbsp;Libera el recurso y permite el acceso de otra entidad.<\/li>\r\n<\/ul>\r\n<p>Eventos disponibles:<code>ON_QUEUE<\/code>, <code>ON_DEPART<\/code><\/p>\r\n<pre>\r\nQUEUER {NAME:Qventanilla1,X:374,Y:424\r\n    ,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 \r\n    ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20}\r\n;------------------------\r\nqueue Qventanilla1\r\nseize ventanilla1\r\ndepart Qventanilla1<\/pre>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "346",
                        "nombre": "Bridger, Dynamic...",
                        "texto": "<p>Existen varios recursos m&aacute;s con sus propias secciones como Bridger y Dynamic.<\/p>\r\n<p>&nbsp;<\/p>",
                        "descripcion": "",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "294",
                "nombre": "Configuración del sistema",
                "texto": "SYSTEM {TYPE:OPTIONS, SPEED:5, TIME_DECIMALS:1, SHOW_BASICS:0}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:actualizarGraficos, INTERVAL:0.5}\r\n\r\nPROCEDURE actualizarGraficos\r\n   move {name:text1:text: \"Texto actualizado a AC1$\"}\r\n   TERMINATE_VE\r\nENDPROCEDURE",
                "descripcion": "<p>El comando <code data-start=\"273\" data-end=\"281\">SYSTEM<\/code> permite configurar distintos aspectos del motor de simulaci&oacute;n. Su sintaxis b&aacute;sica es:<\/p>\r\n<pre>\r\nSYSTEM {TYPE:..., ...}\r\n\r\n<\/pre>\r\n<p><b>Par&aacute;metros de simulaci&oacute;n (velocidad, precisi&oacute;n...)<\/b><br>\r\nPuedes controlar la velocidad de simulaci&oacute;n, la precisi&oacute;n temporal y la precisi&oacute;n decimal del tiempo:<\/p>\r\n<pre>\r\nSYSTEM {TYPE:OPTIONS, SPEED:3}\r\nSYSTEM {TYPE:OPTIONS, TIME_DECIMALS:2}\r\nSYSTEM {TYPE:OPTIONS, width:1400, height:1400}\r\nSYSTEM {TYPE:OPTIONS, SEED:1400}\r\n<\/pre>\r\n<ul>\r\n    <li><code data-start=\"636\" data-end=\"643\">SPEED<\/code>: Velocidad inicial de reproducci&oacute;n (por defecto: <code data-start=\"693\" data-end=\"696\">5<\/code>).<\/li>\r\n    <li><code>TIME_DECIMALS<\/code>: Permite que el tiempo AC1 tenga valores como 3.14 en lugar de valores enteros (por defecto: <code>0<\/code>).<\/li>\r\n    <li><code>WIDTH<\/code>: Ancho del canvas de simulaci&oacute;n. Por defecto 800.<\/li>\r\n    <li><code>HEIGHT <\/code>: Alto del canvas de simulaci&oacute;n. Por defecto 600.<\/li>\r\n    <li><code>SEED<\/code>: Genera la secuencia de n&uacute;meros aleatorios por Xorshift128+.<\/li>\r\n    <li><code>TRACE_ROUTES<\/code>: Genera las trazas para V&amp;V<br>\r\n    <br>\r\n    <b><br>\r\n    <\/b><\/li>\r\n<\/ul>\r\n<p><b>2.- Mostrar informaci&oacute;n b&aacute;sica del sistema<\/b><\/p>\r\n<p>Puedes activar o desactivar la visualizaci&oacute;n de elementos clave del motor:<\/p>\r\n<pre>\r\nSYSTEM {TYPE:OPTIONS, SHOW_BASICS:1}<\/pre>\r\n<p data-start=\"1184\" data-end=\"1231\">Esto har&aacute; que se muestren en la parte inferior:<\/p>\r\n<ul>\r\n    <li><code data-start=\"1234\" data-end=\"1239\">TG1<\/code>: Tiempo global inicial<code data-start=\"1265\" data-end=\"1270\"><\/code><\/li>\r\n    <li><code data-start=\"1265\" data-end=\"1270\">AC1<\/code>: Tiempo actual<\/li>\r\n    <li><code data-start=\"1288\" data-end=\"1295\">SPEED<\/code>: Velocidad de simulaci&oacute;n<\/li>\r\n<\/ul>\r\n<p><b>3.- Temporizadores (TIMER)<\/b><\/p>\r\n<p><b><\/b>Los temporizadores permiten ejecutar procedimientos peri&oacute;dicamente, sin intervenci&oacute;n de entidades:<\/p>\r\n<pre>\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL:0.1}\r\n<\/pre>\r\n<p>&nbsp;<\/p>\r\n<ul>\r\n    <li><code data-start=\"1529\" data-end=\"1538\">TRIGGER<\/code>: nombre del procedimiento a ejecutar.<\/li>\r\n    <li><code data-start=\"1579\" data-end=\"1589\">INTERVAL<\/code>: intervalo de tiempo (en unidades de simulaci&oacute;n) entre ejecuciones.<\/li>\r\n<\/ul>\r\n<p>Esto es &uacute;til para tareas de monitorizaci&oacute;n, logging, cargas peri&oacute;dicas, etc.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><b><\/b><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "285",
                "nombre": "Seguimiento, informe y debugging",
                "texto": "",
                "descripcion": "<p>La plataforma cuenta con herramientas visuales y t&eacute;cnicas que permiten el seguimiento detallado de la simulaci&oacute;n, tanto a nivel de ejecuci&oacute;n como de resultados.<\/p>\r\n<p><b>Panel de depuraci&oacute;n<\/b><\/p>\r\n<p data-start=\"339\" data-end=\"443\">Desde el tercer panel (canvas de reproducci&oacute;n, seguimiento y debug, editor de c&oacute;digo), puedes acceder a:<\/p>\r\n<ul data-start=\"445\" data-end=\"1331\">\r\n    <li data-start=\"445\" data-end=\"597\">\r\n    <p data-start=\"447\" data-end=\"597\"><strong data-start=\"447\" data-end=\"466\">Cola de eventos<\/strong><br data-start=\"466\" data-end=\"469\">\r\n    Muestra en tiempo real qu&eacute; entidades est&aacute;n programadas, su posici&oacute;n temporal (<code data-start=\"549\" data-end=\"554\">TG1<\/code>) y en qu&eacute; paso del programa se encuentran.<\/p>\r\n    <\/li>\r\n    <li data-start=\"599\" data-end=\"709\">\r\n    <p data-start=\"601\" data-end=\"709\"><strong data-start=\"601\" data-end=\"613\">Recursos<\/strong><br data-start=\"613\" data-end=\"616\">\r\n    Detalla las entidades ocupantes, aquellas en espera (cola), y la capacidad de cada recurso.<\/p>\r\n    <\/li>\r\n    <li data-start=\"711\" data-end=\"815\">\r\n    <p data-start=\"713\" data-end=\"815\"><strong data-start=\"713\" data-end=\"726\">Entidades<\/strong><br data-start=\"726\" data-end=\"729\">\r\n    Visualiza el estado de cada entidad: variables, posici&oacute;n, paso actual, retardo, etc.<\/p>\r\n    <\/li>\r\n    <li data-start=\"817\" data-end=\"1331\">\r\n    <p data-start=\"819\" data-end=\"1072\"><strong data-start=\"819\" data-end=\"831\">Ficheros<\/strong><br data-start=\"831\" data-end=\"834\">\r\n    Se muestran todos los archivos creados mediante el comando <code data-start=\"895\" data-end=\"901\">FILE<\/code> y el bloque <code data-start=\"914\" data-end=\"921\">WRITE<\/code>.<br data-start=\"922\" data-end=\"925\">\r\n    Incluye tambi&eacute;n el archivo especial <code data-start=\"963\" data-end=\"970\">DEBUG<\/code>, que se genera autom&aacute;ticamente si se activan instrucciones con la etiqueta <code data-start=\"1046\" data-end=\"1057\">{debug:1}<\/code> o <code data-start=\"1060\" data-end=\"1071\">{debug:2}<\/code>.<\/p>\r\n    <ul data-start=\"1076\" data-end=\"1229\">\r\n        <li data-start=\"1076\" data-end=\"1149\">\r\n        <p data-start=\"1078\" data-end=\"1149\"><code data-start=\"1078\" data-end=\"1089\">{debug:1}<\/code> registra informaci&oacute;n general de la ejecuci&oacute;n de la l&iacute;nea.<\/p>\r\n        <\/li>\r\n        <li data-start=\"1152\" data-end=\"1229\">\r\n        <p data-start=\"1154\" data-end=\"1229\"><code data-start=\"1154\" data-end=\"1165\">{debug:2}<\/code> incluye valores de variables evaluadas y resultados detallados.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ul>\r\n<pre>\r\nif (P$variable == 1) {debug:1}\r\nassign P$variable, X$origen {debug:2}<\/pre>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"1333\" data-end=\"1353\">Bot&oacute;n de informe<\/h3>\r\n<p data-start=\"1355\" data-end=\"1445\">Mediante el bot&oacute;n <strong data-start=\"1373\" data-end=\"1384\">Informe<\/strong>, se genera una visi&oacute;n estad&iacute;stica completa de la simulaci&oacute;n:<\/p>\r\n<ul>\r\n    <li>Informaci&oacute;n general de la simulaci&oacute;n, tiempo final (<code data-start=\"1463\" data-end=\"1468\">AC1<\/code>), n&uacute;mero de entidades procesadas...<\/li>\r\n    <li>Estado e historial de cada <strong data-start=\"1532\" data-end=\"1543\">recurso y sus gr&aacute;ficas.<\/strong><\/li>\r\n    <li>Informaci&oacute;n de los&nbsp;<strong data-start=\"1561\" data-end=\"1572\">PLOTTER<\/strong>&nbsp;y su gr&aacute;ficas.<\/li>\r\n<\/ul>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "217",
        "nombre": "Season 4: Control de recursos y colas",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "173",
                "nombre": "Accediendo y midiendo un recurso",
                "texto": "\/*\r\n\r\n Accediendo y midiendo un recurso\r\n\r\n*\/\r\nSTORAGE {NAME: ALMACEN1, CAPACITY:40, X:316,Y:401}\r\n\r\nSYSTEM {TYPE:ON_TIMER,INTERVAL:50,TRIGGER:PROCTIMER}\r\n\r\nGRAPHIC {NAME:BANNER1,TYPE:TEXT,X:184,Y:343,TEXT:ALEATORIO.}\r\nGRAPHIC {NAME:BANNER2,TYPE:TEXT,X:305,Y:283,TEXT:SUMA}\r\nGRAPHIC {NAME:BANNER3,TYPE:TEXT,X:306,Y:241,TEXT:DIRECT}\r\n\r\nPOSITION {NAME:POS1,X:176,Y:296}\r\nPOSITION {NAME:POS2,X:452,Y:300}\r\nPOSITION {NAME:POS3,X:559,Y:299}\r\n\r\nSTART 100\r\n\r\n;******************************************************\r\n\r\nGENERATE 30,10 {NAME:GEN1,X:61,Y:297}\r\n\r\n    ASSIGN ALEATORIO,FLOOR(RANDOM * 9 + 1) {debug:1}\r\n\r\n    MOVE {NAME:BANNER1,TEXT:\"ALEATORIO: P$ALEATORIO\"}\r\n    ADVANCE 20 {TO:POS1}\r\n    ADVANCE 20 {TO:ALMACEN1}\r\n    ENTER ALMACEN1  ,P$ALEATORIO\r\n    ADVANCE 110,90\r\n    LEAVE ALMACEN1 \r\n    ADVANCE 20 {TO:POS2}\r\n    ADVANCE 20,0 {TO:POS3}\r\nENDGENERATE 1\r\n\r\n;*****************************************************\r\nPROCEDURE PROCTIMER\r\n\r\n    ASSIGN SUMA,0\r\n\r\n    FOREACH NUMERO,IN_RESOURCE,ALMACEN1\r\n      ASSIGN SUMA,(P$(ALEATORIO,P$NUMERO) + P$SUMA)\r\n    ENDFOREACH\r\n\r\n    MOVE {NAME:BANNER2,TEXT:\"SUMA ALEATORIOS: P$SUMA\"}\r\n    ASSIGN DIRECT,40-R$(ALMACEN1,LEFT)\r\n    MOVE {NAME:BANNER3,TEXT:\"DIRECT: P$DIRECT\"}\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n;*****************************************************\r\n",
                "descripcion": "<p data-start=\"149\" data-end=\"376\">Este ejemplo presenta el uso b&aacute;sico de un recurso tipo <code data-start=\"204\" data-end=\"213\">STORAGE<\/code>, &uacute;til para modelar inventarios, almacenes o zonas de capacidad limitada. Las entidades consumen unidades aleatorias del recurso, representando una carga variable.<\/p>\r\n<p data-start=\"378\" data-end=\"493\">El inter&eacute;s del ejemplo est&aacute; en c&oacute;mo obtener el contenido del recurso en tiempo real por <strong data-start=\"466\" data-end=\"492\">dos m&eacute;todos diferentes<\/strong>:<\/p>\r\n<ul data-start=\"495\" data-end=\"825\">\r\n    <li data-start=\"495\" data-end=\"677\">\r\n    <p data-start=\"497\" data-end=\"677\"><strong data-start=\"497\" data-end=\"528\">Suma de datos individuales:<\/strong> usando <code data-start=\"536\" data-end=\"545\">FOREACH<\/code>, se recorre cada entidad que ocupa el recurso y se suman sus valores asociados (en este caso, el n&uacute;mero de unidades que ha tomado).<\/p>\r\n    <\/li>\r\n    <li data-start=\"678\" data-end=\"825\">\r\n    <p data-start=\"680\" data-end=\"825\"><strong data-start=\"680\" data-end=\"701\">Consulta directa:<\/strong> mediante <code data-start=\"711\" data-end=\"730\">R$(ALMACEN1,LEFT)<\/code> se obtiene el n&uacute;mero de unidades <strong data-start=\"764\" data-end=\"774\">libres<\/strong>, por lo que el total ocupado es <code data-start=\"807\" data-end=\"824\">CAPACITY - LEFT<\/code>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"827\" data-end=\"994\">Ambos enfoques permiten contrastar y validar la informaci&oacute;n, y se pueden aplicar para c&aacute;lculos estad&iacute;sticos, control de inventarios o gesti&oacute;n de alertas de saturaci&oacute;n.<\/p>\r\n<p data-start=\"996\" data-end=\"1123\">El <code data-start=\"999\" data-end=\"1006\">TIMER<\/code> peri&oacute;dicamente actualiza la informaci&oacute;n mostrada en pantalla, ofreciendo una visi&oacute;n continua del estado del recurso.<\/p>",
                "parametros": null,
                "parametros_json": "",
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "310",
                "nombre": "Gestionando el tiempo de uso",
                "texto": "\/*\r\n\r\n Gestionando el tiempo de uso\r\n\r\n*\/\r\nFacility {NAME:Facility_1,X:400,Y:84,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}\r\nFacility {NAME:Facility_2,X:400,Y:282,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}\r\nFacility {NAME:Facility_3,X:400,Y:471,capacity:3,E_BIN_SIZE:1,E_BIN_COUNT:200}\r\n\r\nQueuer {NAME:Queuer_1,X:400,Y:174,R_BIN_SIZE:1,R_BIN_COUNT:30}\r\nQueuer {NAME:Queuer_2,X:400,Y:363,R_BIN_SIZE:1,R_BIN_COUNT:30}\r\nQueuer {NAME:Queuer_3,X:400,Y:557,R_BIN_SIZE:1,R_BIN_COUNT:30}\r\n\r\n\r\n\r\nGraphic {NAME:Text_1,Type:TEXT,X:400,Y:132}\r\nGraphic {NAME:Text_2,Type:TEXT,X:400,Y:323}\r\nGraphic {NAME:Text_3,Type:TEXT,X:400,Y:512}\r\n\r\nPOSITION {NAME:POS_1,X:700,Y:137}\r\nPOSITION {NAME:POS_2,X:700,Y:328}\r\nPOSITION {NAME:POS_3,X:700,Y:506}\r\n\r\nSTART 1000\r\n\r\n;*********************************************************\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:100,Y:126}\r\n    CALL caminoVentanilla,1\r\nENDGENERATE 1\r\n\r\nGENERATE 15,0 {NAME:GEN2,X:100,Y:319}\r\n    CALL caminoVentanilla,2\r\nENDGENERATE 1\r\n\r\nGENERATE 20,0 {NAME:GEN3,X:100,Y:492}\r\n    CALL caminoVentanilla,3\r\nENDGENERATE 1\r\n;----------------------------------\r\nPROCEDURE caminoVentanilla\r\n\tassign ventanilla,\"Facility_P$PARAM_A\"\r\n\tassign cola,\"Queuer_P$PARAM_A\" {debug:1}\r\n\tassign nVentanilla,\"P$PARAM_A\"\r\n    ADVANCE 20 {TO:\"P$ventanilla\"}\r\n    CALL calcularTiempo , \"P$ventanilla\",P$nVentanilla\r\n\tQUEUE P$cola\r\n\tSEIZE P$ventanilla\r\n\tDEPART P$cola\r\n    ADVANCE P$calcularTiempo\r\n    RELEASE P$ventanilla\r\n    ADVANCE 20 {TO:\"POS_P$nVentanilla\"}\r\nENDPROCEDURE\r\n;----------------------------------\r\nPROCEDURE calcularTiempo\r\n\tassign ventanilla,\"P$PARAM_A\"\r\n\tassign nVentanilla,P$PARAM_B\r\n    assign tamCola,R$(P$ventanilla,QUEUE) + 1\r\n    \r\n    ;ASSIGN tiempoAtencion, round(max(2, 120 \/ P$tamCola))\r\n    ;ASSIGN tiempoAtencion, round(max(2, 120 \/ sqrt(P$tamCola)))\r\n    ASSIGN tiempoAtencion, round(120 \/ log(P$tamCola + 1))\r\n    move {name:\"Text_P$nVentanilla\",text:\"tiempo Atención: P$tiempoAtencion\"}\r\n\tRETURN P$tiempoAtencion  \r\nENDPROCEDURE\r\n\r\n",
                "descripcion": "<p data-start=\"316\" data-end=\"591\">No todos los recursos deben atender con la misma duraci&oacute;n a cada entidad. En ciertos escenarios &mdash;como servicios al cliente, centros de atenci&oacute;n m&eacute;dica o estaciones de procesamiento&mdash; puede ser razonable adaptar el <strong data-start=\"529\" data-end=\"551\">tiempo de atenci&oacute;n<\/strong> en funci&oacute;n de la <strong data-start=\"569\" data-end=\"590\">carga del sistema<\/strong>.<\/p>\r\n<p data-start=\"593\" data-end=\"823\">En este ejemplo, tres ventanillas (<code data-start=\"628\" data-end=\"640\">Facility_1<\/code>, <code data-start=\"642\" data-end=\"654\">Facility_2<\/code>, <code data-start=\"656\" data-end=\"668\">Facility_3<\/code>) reciben flujos de entidades con distinta intensidad. Cada una est&aacute; acompa&ntilde;ada por un <code data-start=\"755\" data-end=\"763\">Queuer<\/code>, que permite medir y registrar el tama&ntilde;o actual de la cola.<\/p>\r\n<p data-start=\"825\" data-end=\"992\">Antes de ser atendida, cada entidad llama a un procedimiento que calcula din&aacute;micamente su <strong data-start=\"915\" data-end=\"937\">tiempo de atenci&oacute;n<\/strong>, bas&aacute;ndose en el tama&ntilde;o actual de la cola del recurso.<\/p>\r\n<p>La l&oacute;gica usada en este caso es una de las siguientes:<\/p>\r\n<pre>\r\nassign tamCola,R$(P$ventanilla,QUEUE) + 1\r\n    \r\nASSIGN tiempoAtencion, round(max(2, 120 \/ P$tamCola))\r\nASSIGN tiempoAtencion, round(max(2, 120 \/ sqrt(P$tamCola)))\r\nASSIGN tiempoAtencion, round(120 \/ log(P$tamCola + 1))<\/pre>\r\n<p data-start=\"1096\" data-end=\"1115\">Esto significa que:<\/p>\r\n<ul data-start=\"1116\" data-end=\"1261\">\r\n    <li data-start=\"1116\" data-end=\"1182\">\r\n    <p data-start=\"1118\" data-end=\"1182\">A mayor cola, <strong data-start=\"1132\" data-end=\"1160\">menor tiempo de atenci&oacute;n<\/strong>, priorizando fluidez.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1183\" data-end=\"1261\">\r\n    <p data-start=\"1185\" data-end=\"1261\">A menor cola, <strong data-start=\"1199\" data-end=\"1225\">m&aacute;s tiempo por entidad<\/strong>, mejorando la calidad del servicio.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1263\" data-end=\"1336\">Es una estrategia de <strong data-start=\"1284\" data-end=\"1335\">balanceo autom&aacute;tico entre calidad y rendimiento<\/strong>.<\/p>\r\n<p data-start=\"1338\" data-end=\"1361\">Esta t&eacute;cnica demuestra:<\/p>\r\n<ul data-start=\"1362\" data-end=\"1577\">\r\n    <li data-start=\"1362\" data-end=\"1422\">\r\n    <p data-start=\"1364\" data-end=\"1422\">C&oacute;mo vincular un recurso con su <code data-start=\"1396\" data-end=\"1404\">Queuer<\/code> para medir carga.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1423\" data-end=\"1499\">\r\n    <p data-start=\"1425\" data-end=\"1499\">C&oacute;mo usar funciones matem&aacute;ticas para adaptar la duraci&oacute;n de los <code data-start=\"1489\" data-end=\"1498\">ADVANCE<\/code>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1500\" data-end=\"1577\">\r\n    <p data-start=\"1502\" data-end=\"1577\">C&oacute;mo mantener bajo control el tama&ntilde;o de la cola sin sacrificar la atenci&oacute;n.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1579\" data-end=\"1623\">El ejemplo puede ser f&aacute;cilmente extendido a:<\/p>\r\n<ul data-start=\"1624\" data-end=\"1753\">\r\n    <li data-start=\"1624\" data-end=\"1662\">\r\n    <p data-start=\"1626\" data-end=\"1662\">Funciones m&aacute;s complejas de atenci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1663\" data-end=\"1702\">\r\n    <p data-start=\"1665\" data-end=\"1702\">Decisi&oacute;n sobre qu&eacute; ventanilla elegir.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1703\" data-end=\"1753\">\r\n    <p data-start=\"1705\" data-end=\"1753\">Optimizaci&oacute;n del rendimiento global del sistema.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "177",
                "nombre": "Redirigir las colas",
                "texto": "\/*\r\n\r\n Redirigir las colas\r\n\r\n*\/\r\nFACILITY {NAME:VENTANILLA1,X:320,Y:450,capacity:3}\r\nFACILITY {NAME:VENTANILLA2,X:320,Y:300,capacity:1}\r\nFACILITY {NAME:VENTANILLA3,X:320,Y:150,capacity:2}\r\n\r\nPOSITION {NAME:POS1,X:152,Y:300}\r\nPOSITION {NAME:POS2,X:497,Y:300}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:100,Y:100}\r\n\r\nSTART 200 \r\n;*************************************************************************\r\nGENERATE 6,0 {NAME:GEN1,X:54,Y:300}\r\nADVANCE 20,0 {TO:POS1,flow:1}\r\n\r\nCALL DECIDE.SELECCIONA ; return strings : \"CAMINO1\", \"CAMINO1\" ó \"CAMINO1\" creando un ASSIGN llamado P$SELECCIONA\r\nCALL P$SELECCIONA\r\n\r\nADVANCE 20 {TO:POS2,flow:1,merge:\"camino\"}\r\n\r\nENDGENERATE 1\r\n\r\n;*************************************************************************\r\nPROCEDURE DECIDE.SELECCIONA\r\n\r\nassign mimap, [  \r\n{cola:\"CAMINO1\",encola: R$(VENTANILLA1,QUEUE)}, \r\n{cola:\"CAMINO2\",encola: R$(VENTANILLA2,QUEUE)},  \r\n{cola:\"CAMINO3\",encola: R$(VENTANILLA3,QUEUE)}\r\n]\r\n\r\nASSIGN MINKEY,\"CAMINO1\"; inicializamos el KEY seleccionado\r\nASSIGN MINVAL,100000 ; inicializamos el VAL de menor valor para las comparaciones \r\n\r\nFOREACH tmp,IN,V$mimap\r\n\r\n  IF (P$(tmp.encola) < P$MINVAL)\r\n    ASSIGN MINKEY,\"P$(tmp.cola)\" \r\n    ASSIGN MINVAL,P$(tmp.encola)\r\n    move {name:Text1,Text:\"Decisión: P$(MINKEY) Cola: P$(MINVAL)\"}\r\n  ENDIF\r\nENDFOREACH\r\n\r\n\r\nENDPROCEDURE \"P$MINKEY\" ; retornará, por ejemplo, \"C1\"\r\n\r\n;**************************************************************************\r\n\r\nPROCEDURE CAMINO1\r\nADVANCE 20  {TO:VENTANILLA1,flow:1,decision:\"camino\"}\r\nSEIZE VENTANILLA1\r\nADVANCE 25,10\r\nRELEASE VENTANILLA1\r\nENDPROCEDURE\r\n\r\nPROCEDURE CAMINO2\r\nADVANCE 20 {TO:VENTANILLA2,flow:1,decision:\"camino\"}\r\nSEIZE VENTANILLA2\r\nADVANCE 80,70\r\nRELEASE VENTANILLA2\r\nENDPROCEDURE\r\n\r\nPROCEDURE CAMINO3\r\nADVANCE 20 {TO:VENTANILLA3,flow:1,decision:\"camino\"}\r\nSEIZE VENTANILLA3\r\nADVANCE 30,20\r\nRELEASE VENTANILLA3\r\nENDPROCEDURE\r\n\r\n;***************************************************************\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"134\" data-end=\"508\">En este ejemplo, las entidades deben decidir por cu&aacute;l de tres caminos avanzar, cada uno asociado a una ventanilla con su propia cola (<code data-start=\"268\" data-end=\"278\">FACILITY<\/code> con distinta capacidad). La decisi&oacute;n se toma en la funci&oacute;n <code data-start=\"338\" data-end=\"357\">DECIDE.SELECCIONA<\/code>, que recorre una lista con los nombres de los caminos y el tama&ntilde;o actual de sus colas (<code data-start=\"445\" data-end=\"452\">QUEUE<\/code>). El resultado es el nombre del camino con menos carga.<\/p>\r\n<p data-start=\"510\" data-end=\"804\">Ese valor es un <code data-start=\"526\" data-end=\"534\">STRING<\/code> que se devuelve con <code data-start=\"555\" data-end=\"569\">ENDPROCEDURE<\/code> y se almacena autom&aacute;ticamente en la variable <code data-start=\"615\" data-end=\"629\">P$SELECCIONA<\/code>, que luego se usa directamente como llamada: <code data-start=\"675\" data-end=\"694\">CALL P$SELECCIONA<\/code>. Esta mec&aacute;nica permite una selecci&oacute;n din&aacute;mica sin necesidad de condicionales o m&uacute;ltiples llamadas expl&iacute;citas.<\/p>\r\n<p data-start=\"806\" data-end=\"918\" data-is-last-node=\"\" data-is-only-node=\"\">Las estructuras <code data-start=\"711\" data-end=\"720\">.decide<\/code> se usan as&iacute; para devolver directamente el nombre del procedimiento a ejecutar, ahorrando al programador tener que hacer un doble <code data-start=\"850\" data-end=\"858\">SWITCH<\/code> o una cascada de <code data-start=\"876\" data-end=\"880\">IF<\/code>.<\/p>\r\n<p data-start=\"806\" data-end=\"918\" data-is-last-node=\"\" data-is-only-node=\"\">Esto simplifica notablemente la l&oacute;gica de decisi&oacute;n ya que con simplemente:<\/p>\r\n<pre>\r\nCALL DECIDE.SELECCIONA\r\nCALL P$SELECCIONA<\/pre>\r\n<p data-start=\"806\" data-end=\"918\" data-is-last-node=\"\" data-is-only-node=\"\">sustituye a:<\/p>\r\n<pre>\r\nCALL DECIDE.SELECCIONA\r\nSWITCH P$SELECCIONA\r\n&nbsp; CASE ==, &quot;CAMINO1&quot;\r\n&nbsp; &nbsp; CALL CAMINO1\r\n&nbsp; CASE ==, &quot;CAMINO2&quot;\r\n&nbsp; &nbsp; CALL CAMINO2\r\n&nbsp; ...\r\nENDSWITCH<\/pre>",
                "parametros": null,
                "parametros_json": "",
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "202",
                "nombre": "Actualizando la cola de eventos: UPDATE",
                "texto": "\/*\r\n\r\n Actualizando la cola de eventos: UPDATE\r\n\r\n*\/\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 100}\r\n\r\nQueuer {NAME:Queuer1,X:350,Y:324}\r\nGraphic {NAME:Text1,Type:TEXT,X:352,Y:381}\r\n\r\nPOSITION {NAME:POS1,X:90,Y:513}\r\nPOSITION {NAME:POS2,X:638,Y:512}\r\nPOSITION {NAME:POS3,X:635,Y:322}\r\n\r\nSTART 100\r\n\r\n;*********************************************************\r\nINITIAL defaultTime,100\r\nINITIAL conditions,1.0\r\n;*********************************************************\r\nPROCEDURE TIMER1\r\n\tsavevalue oldConditions,X$conditions\r\n\tsavevalue conditions,round(random + 0.5,2)\r\n    MOVE {NAME:Text1,TEXT:\"Old Conditions: X$oldConditions \\nNew conditions: X$conditions\"}\r\n    FOREACH number,IN_RESOURCE,Queuer1\r\n    \r\n    assign myConditions,X$conditions,P$number\r\n\r\n    ASSIGN from, D$(ADVANCESTART, P$number)\r\n    ASSIGN lapse, D$(ADVANCELAPSE, P$number)\r\n    \r\n    ASSIGN completedRatio , (AC1$ - P$from) \/ P$lapse\r\n\tASSIGN remainingRatio , 1 - P$completedRatio \r\n\r\n\tASSIGN adjustedRemainingTime , P$remainingRatio  * X$defaultTime * X$conditions\r\n\tASSIGN newTime, AC1$ + P$adjustedRemainingTime \r\n\r\n      if (X$conditions<1)\r\n     \t MOD {number:P$number,color:#00DD00}\r\n      else\r\n      \tMOD {number:P$number,color:#000000}\r\n      endif\r\n      if (P$newTime < AC1$)\r\n     \t assign newTime,AC1$\r\n      endif\r\n      Update P$number,P$newTime\r\n    ENDFOREACH\r\n\r\n    \r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n;*********************************************************\r\n\r\n\r\nGENERATE 20,0 {NAME:GEN1,X:102,Y:203, ECOLOR:#FF3333, ERADIO:8,esubtitle:P$myConditions}\r\n    ADVANCE 16,5 {TO:POS1}\r\n    assign myTime,X$defaultTime * X$conditions\r\n    assign myConditions,X$conditions\r\n    Queue Queuer1\r\n    ADVANCE P$myTime,0 {TO:POS2}\r\n    Depart Queuer1\r\n    MOD {color:#FF3333}\r\n    ADVANCE 16,5 {TO:POS3}\r\nENDGENERATE 1\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Hemos visto anteriormente el bloque  ADVANCE como algo que parec&iacute;a inmutable. Por ejemplo:<\/p>\r\n<pre>\r\nADVANCE 20,10 \r\n<\/pre>\r\n<p><b>El rol de <code data-start=\"871\" data-end=\"879\">UPDATE<\/code><\/b><code data-start=\"871\" data-end=\"879\"><\/code><\/p>\r\n<p data-start=\"881\" data-end=\"1154\">Una entidad que est&aacute; dentro de un <code data-start=\"915\" data-end=\"924\">ADVANCE<\/code>&nbsp;no puede auto-modificar su tiempo de activaci&oacute;n, ya que est&aacute; inactiva. Por tanto, debe ser otra entidad (usualmente una <strong data-start=\"1049\" data-end=\"1068\">entidad virtual<\/strong>, activada por un <code data-start=\"1086\" data-end=\"1093\">TIMER<\/code> o un <code data-start=\"1099\" data-end=\"1105\">HOOK<\/code>) la que ejecute <code data-start=\"1122\" data-end=\"1130\">UPDATE<\/code> para ajustar su tiempo.<\/p>\r\n<p><strong><\/strong><\/p>\r\n<p>T&eacute;cnicamente, UPDATE no modifica un ADVANCE: modifica la cola de eventos.<br>\r\nExtrae la entidad de su activaci&oacute;n futura y la reprograma en un nuevo tiempo absoluto.<br>\r\nEl motor no interpreta el cambio: solo mantiene la cola ordenada.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><b>Escenario:<\/b> Condiciones cambiantes en una carretera<\/p>\r\n<p data-start=\"1216\" data-end=\"1316\">Simulamos un tramo de carretera bajo ciertas condiciones almacenadas en el <code data-start=\"1291\" data-end=\"1302\">SAVEVALUE<\/code> <code data-start=\"1303\" data-end=\"1315\">conditions<\/code>.<\/p>\r\n<ul data-start=\"1318\" data-end=\"1678\">\r\n    <li data-start=\"1318\" data-end=\"1408\">\r\n    <p data-start=\"1320\" data-end=\"1408\">En condiciones normales (<code data-start=\"1345\" data-end=\"1363\">conditions = 1.0<\/code>), se tarda 100 unidades de tiempo en cruzar.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1409\" data-end=\"1474\">\r\n    <p data-start=\"1411\" data-end=\"1474\">Si <code data-start=\"1414\" data-end=\"1432\">conditions = 1.5<\/code>, el trayecto dura 150 unidades de tiempo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1475\" data-end=\"1542\">\r\n    <p data-start=\"1477\" data-end=\"1542\">Una entidad calcula su tiempo de avance cuando entra en el tramo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1543\" data-end=\"1678\">\r\n    <p data-start=\"1545\" data-end=\"1678\">Si las condiciones cambian mientras una entidad est&aacute; en camino, queremos <strong data-start=\"1618\" data-end=\"1650\">ajustar su tiempo de llegada<\/strong>, para reflejar este cambio.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>&iquest;C&oacute;mo se calcula?<\/b><\/p>\r\n<p data-start=\"1707\" data-end=\"1965\">Imagina que una entidad entr&oacute; cuando <code data-start=\"1744\" data-end=\"1762\">conditions = 1.2<\/code> (tiempo de recorrido = 120), pero ahora las condiciones mejoran a <code data-start=\"1829\" data-end=\"1834\">1.0<\/code>. Si ya ha recorrido el 50%, le quedar&iacute;an 50 unidades con las nuevas condiciones. En total, habr&iacute;a tardado 110 en lugar de 120.<\/p>\r\n<p><b>El bloque <code data-start=\"1985\" data-end=\"1993\">UPDATE<\/code><code data-start=\"1985\" data-end=\"1993\"><\/code><\/b><code data-start=\"1985\" data-end=\"1993\"><\/code><\/p>\r\n<pre>\r\nUPDATE entidadID, nuevoTiempo\r\n<\/pre>\r\n<p>Este comando reprograma a una entidad con un nuevo tiempo de activaci&oacute;n, siempre que dicho tiempo sea &ge; <code data-start=\"2142\" data-end=\"2148\">AC1$<\/code> (el tiempo actual de simulaci&oacute;n).<\/p>\r\n<p><b>Qu&eacute; demuestra este ejemplo:<\/b><\/p>\r\n<ul data-start=\"2221\" data-end=\"2537\">\r\n    <li data-start=\"2221\" data-end=\"2319\">\r\n    <p data-start=\"2223\" data-end=\"2319\">Que el comportamiento de las entidades <strong data-start=\"2262\" data-end=\"2318\">puede ser modificado incluso mientras est&aacute;n en curso<\/strong>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2320\" data-end=\"2415\">\r\n    <p data-start=\"2322\" data-end=\"2415\">Que <code data-start=\"2326\" data-end=\"2352\">TIMER + FOREACH + UPDATE<\/code> forman una combinaci&oacute;n para reaccionar din&aacute;micamente.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2416\" data-end=\"2537\">\r\n    <p data-start=\"2418\" data-end=\"2537\">Que se permite representar escenarios del mundo real, donde las condiciones cambian y el sistema debe adaptarse.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "249",
        "nombre": "Season 5: Gráficos",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "272",
                "nombre": "Crear y modificar",
                "texto": "\/*\r\n\r\n Crear y modificar gráficos\r\n\r\n*\/\r\nSYSTEM {TYPE:OPTIONS, SPEED:8, TIME_DECIMALS:1}\r\n\r\n;==============================\r\n; VARIABLES DINÁMICAS\r\n;==============================\r\nINITIAL GRADO, 0\r\nINITIAL ALTURA, 80\r\nINITIAL ESCALA, 100\r\nINITIAL CRECIENDO, 1\r\n\r\n;==============================\r\n; ELEMENTOS GRÁFICOS\r\n;==============================\r\n\r\nGRAPHIC {NAME:txt, TYPE:TEXT, X:300, Y:50,text:\"--test--\"}\r\n\r\n\r\nGRAPHIC {NAME:radarGrupo, TYPE:GROUP, X:300, Y:400}\r\n\r\nGRAPHIC {\r\n  GROUP:radarGrupo,\r\n  NAME:radarTexto,\r\n  TYPE:TEXT,\r\n  TEXT:\"GRADO: X$GRADO\",\r\n  COLOR:#00AAFF,\r\n  X:0, Y:-50\r\n}\r\n\r\nGRAPHIC {\r\n  GROUP:radarGrupo,\r\n  NAME:radarImagen,\r\n  TYPE:IMAGE,\r\n  SRC:\"PUERTA\",\r\n  X:100, Y:100,\r\n  OPACITY:0.9,\r\n  ROTATE:0\r\n}\r\n\r\nGRAPHIC {\r\n  GROUP:radarGrupo,\r\n  NAME:ondaVisual,\r\n  TYPE:CURVE,\r\n  POINTS:\"[0,0],[40,X$ALTURA],[80,0]\",\r\n  COLOR:red,\r\n  WIDTH:2,\r\n  OPACITY:0.6\r\n}\r\n\r\nGRAPHIC {\r\n  NAME:puerta,\r\n  TYPE:IMAGE,\r\n  SRC:\"PUERTA\",\r\n  X:400, Y:100,\r\n  OPACITY:0.9,\r\n  ROTATE:0\r\n}\r\n\r\n\r\nGRAPHIC {\r\n  NAME:barreraCircular,\r\n  TYPE:ARC,\r\n  X:100, Y:400,\r\n  COLOR:#FFAA00,\r\n  RADIUS:60,\r\n  START_ANGLE:0,\r\n  END_ANGLE:X$GRADO,\r\n  OPACITY:1,\r\n  CLOSE:0\r\n}\r\n\r\n\r\n;==============================\r\n; ENTIDAD PRINCIPAL\r\n;==============================\r\nGENERATE 5, 0 {NAME:GEN1, X:50, Y:200}\r\n\r\n;move {name:txt,text:\"Pos X GD$(barreraCircular,X)\"}\r\n; Aumentar grado (rotación)\r\nSAVEVALUE GRADO, (X$GRADO + 15) % 360\r\n\r\n; Subir\/bajar la onda\r\nIF ((X$GRADO % 180) < 90)\r\n  SAVEVALUE ALTURA, X$ALTURA + 4\r\nELSE\r\n  SAVEVALUE ALTURA, X$ALTURA - 4\r\nENDIF\r\n\r\n; Animar el tamaño\r\nIF (X$CRECIENDO == 1)\r\n  SAVEVALUE ESCALA, X$ESCALA + 5\r\n  IF (X$ESCALA >= 140)\r\n    SAVEVALUE CRECIENDO, 0\r\n  ENDIF\r\nELSE\r\n  SAVEVALUE ESCALA, X$ESCALA - 5\r\n  IF (X$ESCALA <= 100)\r\n    SAVEVALUE CRECIENDO, 1\r\n  ENDIF\r\nENDIF\r\n\r\nADVANCE 5, 0\r\n\r\n;==============================\r\n; ACTUALIZACIONES VISUALES\r\n;==============================\r\n\r\nMOVE {NAME:radarTexto, TEXT:\"Grado: X$GRADO\"}\r\nMOVE {NAME:radarImagen, ROTATE:X$GRADO}\r\nMOVE {NAME:radarImagen, RESIZE:X$ESCALA}\r\nMOVE {NAME:ondaVisual, Y2:X$ALTURA}\r\n\r\n\r\nMOVE {NAME:radarGrupo, Y:(GD$(radarGrupo,Y)+(RANDOM*10)-5)}\r\n\r\nMOVE {NAME:puerta, RESIZE_X:X$ESCALA * 2}\r\nMOVE {NAME:barreraCircular, END_ANGLE:X$GRADO}\r\n\r\n\r\nTERMINATE 1\r\n\r\nSTART 500\r\n",
                "descripcion": "<h3>Crear y modificar<\/h3>\r\n<p>GPSS-Plus permite definir elementos gr&aacute;ficos que se pueden mover, transformar y actualizar durante la simulaci&oacute;n. Estos objetos pueden ser <b>textos, curvas, im&aacute;genes, arcos o l&iacute;neas<\/b>, y pueden agruparse para formar unidades visuales completas.<\/p>\r\n<p>Los gr&aacute;ficos pueden moverse de forma individual o en grupo, y se pueden modificar sus propiedades como:<\/p>\r\n<ul>\r\n    <li>Posici&oacute;n (<code>X<\/code>, <code>Y<\/code>)<\/li>\r\n    <li>Rotaci&oacute;n (<code>ROTATE<\/code>)<\/li>\r\n    <li>Tama&ntilde;o (<code>RESIZE<\/code>)<\/li>\r\n    <li>Opacidad (<code>OPACITY<\/code>)<\/li>\r\n    <li>Contenido textual (<code>TEXT<\/code>)<\/li>\r\n    <li>Color y &aacute;ngulos, en figuras como <code>ARC<\/code> o <code>LINE<\/code><\/li>\r\n<\/ul>\r\n<p>Los comandos <code>MOVE<\/code> permiten usar:<\/p>\r\n<ul>\r\n    <li>Valores absolutos, como <code>X:100<\/code><\/li>\r\n    <li>Valores relativos, accediendo al valor actual mediante <code>GD$<\/code>, por ejemplo: <code>X:GD$(objeto,X) + 10<\/code><\/li>\r\n<\/ul>\r\n<p>Este ejemplo muestra c&oacute;mo una entidad, a lo largo de sus pasos, modifica variables internas (<code>SAVEVALUE<\/code>) y usa esas variables para actualizar objetos gr&aacute;ficos en cada avance.<\/p>\r\n<p>Se ilustra el uso de gr&aacute;ficos de tipo <code>TEXT<\/code>, <code>CURVE3<\/code>, <code>CURVE<\/code>, <code>ARC<\/code>, <code>IMAGE<\/code> y <code>GROUP<\/code>, as&iacute; como el uso combinado de animaciones relativas, escalado y rotaci&oacute;n.<\/p>\r\n<h3>Sobre curvas y relleno<\/h3>\r\n<p>GPSS-Plus admite dos tipos de curvas:<\/p>\r\n<ul>\r\n    <li><b>CURVE3<\/b>: curva de tres puntos (Bezier simple), declarada con coordenadas directas <code>X1<\/code>, <code>Y1<\/code>, <code>X2<\/code>, <code>Y2<\/code>, <code>X3<\/code>, <code>Y3<\/code><\/li>\r\n    <li><b>CURVE<\/b>: curva suavizada tipo Catmull-Rom, definida con una lista de puntos<\/li>\r\n<\/ul>\r\n<p>Para declarar los puntos, existen dos formas equivalentes:<\/p>\r\n<ul>\r\n    <li><b>Sintaxis compacta<\/b>: <code>POINTS:&quot;[x1,y1],[x2,y2],[x3,y3],...&quot;<\/code><\/li>\r\n    <li><b>Sintaxis directa por pares<\/b>: <code>X1:1,Y1:2, X2:3,Y2:4, X3:5,Y3:6<\/code><\/li>\r\n<\/ul>\r\n<p>Las curvas, l&iacute;neas o arcos pueden ser <b>abiertos o cerrados<\/b>. Para representarlos como figuras cerradas con relleno, es necesario establecer:<\/p>\r\n<ul>\r\n    <li><code>CLOSE:1<\/code><\/li>\r\n    <li><code>FCOLOR:<\/code> para el color de relleno<\/li>\r\n<\/ul>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "273",
                "nombre": "Modificación por procedimiento",
                "texto": "\/*\r\n\r\n Gráficos. Modificación por procedimiento\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\n\r\n;==============================\r\n; CONFIGURACIÓN DEL DEPÓSITO\r\n;==============================\r\nINITIAL config, {deposito_coords:[0, 0, 20, 200],deposito_position:[180,180]} \r\n\r\n;==============================\r\n; GRÁFICOS\r\n;==============================\r\nGRAPHIC {NAME:Deposito, TYPE:GROUP, X:0, Y:0}\r\n\r\n; Marco exterior\r\nGRAPHIC {\r\n  NAME:Marco,\r\n  GROUP:Deposito,\r\n  TYPE:LINE,\r\n  POINTS:\"[0,0],[0,10],[10,10],[10,10]\",\r\n  COLOR:#555555,\r\n  CLOSE:1\r\n}\r\n\r\n; Nivel interior (relleno)\r\nGRAPHIC {\r\n  NAME:Nivel,\r\n  GROUP:Deposito,\r\n  TYPE:LINE,\r\n  POINTS:\"[0,300],[0,300],[40,300],[40,300]\",\r\n  COLOR:#3399FF,\r\n  FCOLOR:#99CCFF,\r\n  CLOSE:1\r\n}\r\n\r\n; Texto de porcentaje\r\nGRAPHIC {\r\n  NAME:TextoNivel,\r\n  GROUP:Deposito,\r\n  TYPE:TEXT,\r\n  X:0, Y:0,\r\n  TEXT:\"Nivel: 0%\",\r\n  COLOR:#000000\r\n}\r\nSTART 100\r\n\r\n\r\nPROCEDURE PRE_RUN\r\n\ttimeout nivel_init,0\r\n\tTERMINATE \r\nENDPROCEDURE 1\r\n\r\n;==============================\r\n; PROCEDIMIENTO DE ACTUALIZACIÓN\r\n;==============================\r\nPROCEDURE nivel_init\r\nmove {name:Marco\r\n\t,X1:X$(config.deposito_coords.0)-4,Y1:X$(config.deposito_coords.1)-4\r\n\t,X2:X$(config.deposito_coords.2)+4,Y2:X$(config.deposito_coords.1)-4\r\n\t,X3:X$(config.deposito_coords.2)+4,Y3:X$(config.deposito_coords.3)+4\r\n\t,X4:X$(config.deposito_coords.0)-4,Y4:X$(config.deposito_coords.3)+4\r\n\t}\r\n\r\nmove {name:Nivel\r\n\t,X1:X$(config.deposito_coords.0),Y1:X$(config.deposito_coords.1)\r\n\t,X2:X$(config.deposito_coords.2),Y2:X$(config.deposito_coords.1)\r\n\t,X3:X$(config.deposito_coords.2),Y3:X$(config.deposito_coords.3)\r\n\t,X4:X$(config.deposito_coords.0),Y4:X$(config.deposito_coords.3)\r\n\t}\r\n\r\nMOVE {NAME:TextoNivel\r\n\t\t, x:(GD$(Marco,X1) + GD$(Marco,X2))\/ 2\r\n\t\t, y:X$(config.deposito_coords.1)-14, TEXT:\"Nivel: PP$A%\"}\r\n\r\nMOVE {NAME:Deposito\r\n\t\t, x:X$(config.deposito_position.0)\r\n\t\t, y:X$(config.deposito_position.1)}   \r\nterminate\r\nendprocedure 1\r\n\r\n\r\nPROCEDURE deposito_set\r\n\r\n; PP$A = porcentaje deseado (0 a 100)\r\n\r\nASSIGN yBase, X$(config.deposito_coords.1) ; y inferior \r\nASSIGN yTop, X$(config.deposito_coords.3)  ; y superior\r\n\r\nASSIGN alturaMaxima, P$yBase - P$yTop\r\nASSIGN alturaNivel, P$alturaMaxima * P$PARAM_A \/ 100\r\nASSIGN yActual, P$yBase - P$alturaNivel\r\n\r\n; Mover vértices del nivel\r\nMOVE {\r\n  NAME:Nivel,\r\n  Y3:P$yActual,\r\n  Y4:P$yActual\r\n}\r\nMOVE {NAME:TextoNivel,TEXT:\"Nivel: P$PARAM_A%\"}\r\n\r\nENDPROCEDURE 1\r\n\r\nPROCEDURE deposito_locate\r\n\r\nMOVE {NAME:Deposito\r\n\t\t, x:P$PARAM_A\r\n\t\t, y:P$PARAM_B}   \r\n        \r\nENDPROCEDURE 1\r\n\r\n;==============================\r\n; ENTIDAD QUE LO USA\r\n;==============================\r\nGENERATE 25,0 {NAME:Gen1}\r\n\r\nCALL deposito_set, 25\r\nADVANCE 5\r\n\r\nCALL deposito_set, 50\r\nADVANCE 5\r\n\r\nCALL deposito_set, 75\r\nADVANCE 5\r\n\r\nCALL deposito_set, 90\r\nADVANCE 5\r\n\r\nCALL deposito_set, 10\r\nADVANCE 5\r\nCALL deposito_locate, GD$(Deposito,X)+4,GD$(Deposito,Y)+4\r\n\r\nTERMINATE 1\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"571\" data-end=\"861\">Antes de introducir el sistema de contextos <code data-start=\"414\" data-end=\"419\">CX$<\/code> para instanciar componentes gr&aacute;ficos reutilizables en LIBRERIAS INDEPENDIENTES, es importante comprender c&oacute;mo se puede construir un componente visual <strong data-start=\"542\" data-end=\"557\">manualmente<\/strong>, paso a paso, utilizando procedimientos (<code data-start=\"599\" data-end=\"610\">PROCEDURE<\/code>) y estructuras de configuraci&oacute;n (<code data-start=\"644\" data-end=\"653\">INITIAL<\/code>).<\/p>\r\n<p data-start=\"657\" data-end=\"828\">Este ejemplo <strong data-start=\"670\" data-end=\"726\">no representa la forma &oacute;ptima ni la m&aacute;s recomendable<\/strong> para implementar componentes gr&aacute;ficos reutilizables, pero cumple un papel esencial en el aprendizaje:<\/p>\r\n<ul data-start=\"830\" data-end=\"1138\">\r\n    <li data-start=\"830\" data-end=\"899\">\r\n    <p data-start=\"832\" data-end=\"899\">Permite entender c&oacute;mo se declaran los gr&aacute;ficos agrupados (<code data-start=\"890\" data-end=\"897\">GROUP<\/code>).<\/p>\r\n    <\/li>\r\n    <li data-start=\"900\" data-end=\"986\">\r\n    <p data-start=\"902\" data-end=\"986\">Muestra c&oacute;mo parametrizar su geometr&iacute;a y posici&oacute;n mediante diccionarios (<code data-start=\"975\" data-end=\"984\">INITIAL<\/code>).<\/p>\r\n    <\/li>\r\n    <li data-start=\"987\" data-end=\"1063\">\r\n    <p data-start=\"989\" data-end=\"1063\">Ense&ntilde;a c&oacute;mo encapsular transformaciones visuales dentro de procedimientos.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1064\" data-end=\"1138\">\r\n    <p data-start=\"1066\" data-end=\"1138\">Introduce la llamada de &ldquo;funciones visuales&rdquo; desde entidades con <code data-start=\"1131\" data-end=\"1137\">CALL<\/code>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1299\" data-end=\"1487\">Este enfoque ser&aacute; m&aacute;s adelante reemplazado por el uso de contextos <code data-start=\"1207\" data-end=\"1212\">CX$<\/code>, pero <strong data-start=\"1219\" data-end=\"1267\">conviene dominar primero este nivel &ldquo;manual&rdquo;<\/strong> para comprender en profundidad c&oacute;mo GPSS-Plus convierte procedimientos en componentes visuales aut&oacute;nomos y reutilizables.<\/p>\r\n<p data-start=\"1973\" data-end=\"2075\">En este ejemplo se representa un <strong data-start=\"2006\" data-end=\"2035\">dep&oacute;sito con nivel visual<\/strong>, compuesto por un <code data-start=\"2054\" data-end=\"2061\">GROUP<\/code> que contiene:<\/p>\r\n<ul data-start=\"2077\" data-end=\"2221\">\r\n    <li data-start=\"2077\" data-end=\"2113\">\r\n    <p data-start=\"2079\" data-end=\"2113\">un marco exterior (<code data-start=\"2098\" data-end=\"2104\">LINE<\/code> cerrado)<\/p>\r\n    <\/li>\r\n    <li data-start=\"2114\" data-end=\"2173\">\r\n    <p data-start=\"2116\" data-end=\"2173\">un nivel de relleno (<code data-start=\"2137\" data-end=\"2143\">LINE<\/code> cerrado, con altura variable)<\/p>\r\n    <\/li>\r\n    <li data-start=\"2174\" data-end=\"2221\">\r\n    <p data-start=\"2176\" data-end=\"2221\">un texto con el porcentaje de llenado visible<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"475\" data-end=\"878\">Toda la geometr&iacute;a y la posici&oacute;n del dep&oacute;sito est&aacute;n definidas mediante una estructura de configuraci&oacute;n (<code data-start=\"578\" data-end=\"586\">config<\/code>) y se aplican directamente a gr&aacute;ficos con nombres fijos. Aunque este enfoque <strong data-start=\"664\" data-end=\"703\">simula una forma de parametrizaci&oacute;n<\/strong>, <strong data-start=\"705\" data-end=\"727\">no es reutilizable<\/strong> tal como est&aacute;: los nombres de los objetos gr&aacute;ficos est&aacute;n codificados de forma est&aacute;tica, lo que impide instanciar m&uacute;ltiples dep&oacute;sitos sin conflictos.<\/p>\r\n<p data-start=\"884\" data-end=\"1094\">Esta limitaci&oacute;n se resolver&aacute; m&aacute;s adelante con el uso de <strong data-start=\"940\" data-end=\"971\">contextos <code data-start=\"952\" data-end=\"957\">CX$<\/code> y librer&iacute;as<\/strong>, que permitir&aacute;n crear m&uacute;ltiples instancias visuales de un mismo componente, cada una con su propio estado y configuraci&oacute;n.<\/p>\r\n<p data-start=\"2223\" data-end=\"2449\">&nbsp;<\/p>\r\n<p data-start=\"2451\" data-end=\"2661\">El procedimiento <code data-start=\"2468\" data-end=\"2480\">nivel_init<\/code> se encarga de generar los v&eacute;rtices del gr&aacute;fico al inicio (<code data-start=\"2539\" data-end=\"2548\">PRE_RUN<\/code>), y el procedimiento <code data-start=\"2570\" data-end=\"2584\">deposito_set<\/code> actualiza visualmente el nivel de llenado seg&uacute;n un porcentaje (<code data-start=\"2648\" data-end=\"2659\">P$PARAM_A<\/code>).<\/p>\r\n<p data-start=\"2663\" data-end=\"2754\">Tambi&eacute;n se incluye <code data-start=\"2682\" data-end=\"2699\">deposito_locate<\/code>, que permite mover todo el grupo a una nueva posici&oacute;n.<\/p>\r\n<p data-start=\"1305\" data-end=\"1396\">&nbsp;<\/p>\r\n<p data-start=\"495\" data-end=\"640\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "194",
                "nombre": "Ejemplo: Reloj analógico animado",
                "texto": "\/*\r\n\r\n Gráficos. Reloj analógico animado\r\n \r\n*\/\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:0, SPEED:7}   \r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:TIMER1, INTERVAL: 10}\r\n\r\n\r\n; ANALOGIC CLOCK\r\nGRAPHIC {NAME:OUT,TYPE:ARC,X:0,Y:0,COLOR:#FFFF99,RADIUS:200,START_ANGLE:0,END_ANGLE:360,CLOSE:0}\r\nGRAPHIC {NAME:AT12,TYPE:TEXT,X:300,Y:500,TEXT:\"12\"}\r\nGRAPHIC {NAME:AT3,TYPE:TEXT,X:500,Y:300,TEXT:\"3\"}\r\nGRAPHIC {NAME:AT6,TYPE:TEXT,X:300,Y:100,TEXT:\"6\"}\r\nGRAPHIC {NAME:AT9,TYPE:TEXT,X:100,Y:300,TEXT:\"9\"}\r\n\r\nGRAPHIC {NAME:MINLINE,TYPE:LINE,COLOR:#F00,X1:268,Y1:361,X2:265,Y2:332}\r\nGRAPHIC {NAME:HOURLINE,TYPE:LINE,COLOR:#000,X1:240,Y1:361,X2:233,Y2:328}\r\n; DIGITAL CLOCK\r\nGRAPHIC {NAME:TIME,TYPE:TEXT,X:300,Y:536,TEXT:\"IT'S.. \",FONT:\"26PX\"}\r\nGRAPHIC {NAME:REALTIME1,TYPE:TEXT,X:575,Y:536,TEXT:\"REALTIME.. \",FONT:\"14PX\"}\r\nGRAPHIC {NAME:REALTIME2,TYPE:TEXT,X:575,Y:516,TEXT:\"REALTIME.. \",FONT:\"14PX\"}\r\n\r\nSTART 1\r\n\r\n; THE CLOCK\r\n\r\nGENERATE 100,0 {NAME:GEN1,VISIBLE:0,EVISIBLE:0,X:647,Y:99}\r\n\r\nTERMINATE 0\r\n\r\n\r\nPROCEDURE PRE_RUN\r\n\tSAVEVALUE MIN,0\r\n    SAVEVALUE A,0\r\n    SAVEVALUE B,0\r\n    SAVEVALUE CENTROX,300\r\n    SAVEVALUE CENTROY,300\r\n    SAVEVALUE RADIOM,180\r\n    SAVEVALUE RADIOH,120\r\n    MOVE {NAME:OUT,X:X$CENTROX,Y:X$CENTROY}\r\n    MOVE {NAME:MINLINE,x1:0,y1:0,x2:0,y2:0}\r\n\tMOVE {NAME:HOURLINE,x1:0,y1:0,x2:0,y2:0}\r\n\tTERMINATE_VE \r\nENDPROCEDURE \r\n\r\nPROCEDURE TIMER1\r\n\r\n\tassign sys,SYS$\r\n    \r\n    ASSIGN sys, SYS$\r\n  \tMOVE {name: REALTIME1, text:\"P$(sys.date.year)\/P$(sys.date.month)\/P$(sys.date.day)\"}\r\n  \tMOVE {name: REALTIME2, text:\"P$(sys.date.hour):P$(sys.date.min):P$(sys.date.sec)\"}\r\n  \r\n\r\n\tMOVE {NAME:TIME,TEXT:\"[X$HOR:X$MIN2]\"}\r\n    MOVE {NAME:MINLINE\r\n    \t\t,X1: (X$CENTROX)\r\n    \t\t,Y1:(X$CENTROY)\r\n            ,X2:(X$CENTROX+SIN(X$A)*X$RADIOM)\r\n            ,Y2:(X$CENTROY+COS(X$A)*X$RADIOM)}\r\n    MOVE {NAME:HOURLINE\r\n    \t\t,X1:(X$CENTROX)\r\n    \t\t,Y1:(X$CENTROY)\r\n            ,X2:(X$CENTROX+SIN(X$B)*X$RADIOH)\r\n            ,Y2:(X$CENTROY+COS(X$B)*X$RADIOH)}\r\n\r\n    SAVEVALUE MIN, (X$MIN+1)\r\n    SAVEVALUE MIN2,(X$MIN %60)\r\n    SAVEVALUE HOR, (FLOOR(X$MIN\/60))\r\n    SAVEVALUE A, (MODULO(X$MIN,60) *2*PI)\/60\r\n    SAVEVALUE B, (MODULO(X$MIN,720) *2*PI)\/720\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"944\" data-end=\"1317\">Este ejemplo representa un reloj de agujas tradicional (anal&oacute;gico) utilizando primitivas gr&aacute;ficas. A trav&eacute;s de un <code data-start=\"1058\" data-end=\"1069\">PROCEDURE<\/code> se inicializa el escenario con un arco de fondo, marcas horarias (<code data-start=\"1136\" data-end=\"1142\">TEXT<\/code>) y las dos agujas (<code data-start=\"1162\" data-end=\"1168\">LINE<\/code>). Una entidad virtual <code>(TIMER)<\/code> se genera peri&oacute;dicamente y actualiza las posiciones de las agujas usando trigonometr&iacute;a y variables temporales (<code data-start=\"1304\" data-end=\"1315\">SAVEVALUE<\/code>).<\/p>\r\n<p data-start=\"1319\" data-end=\"1331\">Se combinan:<\/p>\r\n<ul data-start=\"1332\" data-end=\"1457\">\r\n    <li data-start=\"1332\" data-end=\"1373\">\r\n    <p data-start=\"1334\" data-end=\"1373\"><code data-start=\"1334\" data-end=\"1339\">ARC<\/code> para la circunferencia principal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1374\" data-end=\"1419\">\r\n    <p data-start=\"1376\" data-end=\"1419\"><code data-start=\"1376\" data-end=\"1382\">TEXT<\/code> para los n&uacute;meros y el reloj digital.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1420\" data-end=\"1457\">\r\n    <p data-start=\"1422\" data-end=\"1457\"><code data-start=\"1422\" data-end=\"1428\">LINE<\/code> para representar las agujas.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1459\" data-end=\"1580\">Es un caso claro donde <strong data-start=\"1482\" data-end=\"1528\">la animaci&oacute;n no viene de entidades m&oacute;viles<\/strong>, sino de <strong data-start=\"1538\" data-end=\"1579\">transformaciones visuales programadas<\/strong>.<\/p>\r\n<p>Se puede observar la velocidad de reproducci&oacute;n, con un valor de <code>speed: 5<\/code> aproximadamente se acompasar&iacute;a el <code>AC1 <\/code>con el segundero.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "327",
                "nombre": "En 3D",
                "texto": "\/*\r\n\r\n Gráficos. 3D\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN, TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:40, V_HEIGHT:20, CAMERA:0}\r\nSYSTEM {TYPE:OPTIONS, TIME_DECIMALS:1, SPEED:5}\r\n\r\nGRAPHIC {NAME:suelo, TYPE:PLANE, X:25, POINTS:\"[0,0,0],[0,0,40],[40,0,0]\", color:#fabada}\r\n\r\nGRAPHIC {NAME:nut, TYPE:OBJECT, src:NUT, X:0, Y:0, Z:0,WIDTH:20, HEIGHT:20, DEPTH:20,rotate_x:90,opacity:0.9}\r\n\r\nGraphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}\r\nGraphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}\r\nGraphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}\r\n\r\n\r\nGraphic {NAME:linea4,Type:LINE,POINTS:\"[10,0,10],[150,150,150]\",color:red}\r\nGraphic {NAME:Text1,Type:TEXT,X:20,Y:20,text:\"sss\",font:\"2px\"}\r\n\r\n\r\n\r\nSTART 1\r\n;===========================\r\n; PROCEDIMIENTOS\r\n;===========================\r\n\r\nPROCEDURE agente.init\r\n\r\nsavevalue DT,0.1\r\nsavevalue velocidad,1\r\nsavevalue X,4\r\nsavevalue estado,0\r\n\r\n  WHILE (1==1)\r\n\r\n\tif (X$estado==0)\r\n    savevalue X,X$X + X$DT * X$velocidad\r\n\telse \r\n    savevalue X,X$X - X$DT * X$velocidad\r\n\tendif\r\n    \r\n    if (X$X>10)\r\n    savevalue estado,1\r\n    endif\r\n    if (X$X<4)\r\n    savevalue estado,0\r\n    endif\r\n   \r\n    assign altura, round(X$X,3)\r\n\tmove {name:Text1,text:\"Altura: P$altura\"}\r\n    ; La tuerca se coloca y rota sobre su eje\r\n    MOVE {name:nut, MOVE_BETWEEN:\"[10,0,10],[150,150,150]\", rotate_y:P$altura * 60,y:P$altura}\r\n\r\n    ADVANCE 1, 0\r\n\r\n  ENDWHILE\r\n\r\nSTOP\r\nENDPROCEDURE\r\n;===========================\r\nPROCEDURE PRE_RUN\r\n\r\n  TIMEOUT agente.init, 0\r\n\r\n  TERMINATE_VE\r\n\r\nENDPROCEDURE\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Aunque todo es representable en 2D, en ciertas simulaciones &mdash;especialmente las de naturaleza mec&aacute;nica o con &eacute;nfasis visual&mdash; puede ser deseable utilizar representaciones en 3D.<\/p>\r\n<p data-start=\"543\" data-end=\"753\">GPSS-Plus no pretende ser una herramienta de modelado 3D como tal; el enfoque est&aacute; en facilitar simulaciones visualmente efectivas y f&aacute;ciles de implementar. Por ello, el dise&ntilde;o 3D puede provenir de dos fuentes:<\/p>\r\n<ul data-start=\"755\" data-end=\"827\">\r\n    <li data-start=\"755\" data-end=\"791\">\r\n    <p data-start=\"757\" data-end=\"791\">Ficheros externos <code data-start=\"775\" data-end=\"781\">.GLB<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"792\" data-end=\"827\">\r\n    <p data-start=\"794\" data-end=\"827\">Primitivas geom&eacute;tricas integradas<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<h4 data-start=\"829\" data-end=\"859\">Primitivas 3D disponibles<\/h4>\r\n<div class=\"_tableContainer_80l1q_1\">\r\n<div tabindex=\"-1\" class=\"_tableWrapper_80l1q_14 group flex w-fit flex-col-reverse\">\r\n<table data-start=\"861\" data-end=\"1370\" class=\"w-fit min-w-(--thread-content-width)\">\r\n    <thead data-start=\"861\" data-end=\"933\">\r\n        <tr data-start=\"861\" data-end=\"933\">\r\n            <th data-start=\"861\" data-end=\"874\" data-col-size=\"sm\">Tipo<\/th>\r\n            <th data-start=\"874\" data-end=\"933\" data-col-size=\"md\">Descripci&oacute;n<\/th>\r\n        <\/tr>\r\n    <\/thead>\r\n    <tbody data-start=\"1007\" data-end=\"1370\">\r\n        <tr data-start=\"1007\" data-end=\"1078\">\r\n            <td data-start=\"1007\" data-end=\"1020\" data-col-size=\"sm\"><code data-start=\"1009\" data-end=\"1014\">BOX<\/code><\/td>\r\n            <td data-start=\"1020\" data-end=\"1078\" data-col-size=\"md\">Caja tridimensional con <code data-start=\"1046\" data-end=\"1053\">WIDTH<\/code>, <code data-start=\"1055\" data-end=\"1063\">HEIGHT<\/code>, <code data-start=\"1065\" data-end=\"1072\">DEPTH<\/code><\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1079\" data-end=\"1151\">\r\n            <td data-start=\"1079\" data-end=\"1092\" data-col-size=\"sm\"><code data-start=\"1081\" data-end=\"1089\">SPHERE<\/code><\/td>\r\n            <td data-start=\"1092\" data-end=\"1151\" data-col-size=\"md\">Esfera con <code data-start=\"1105\" data-end=\"1113\">RADIUS<\/code>, <code data-start=\"1115\" data-end=\"1125\">SEGMENTS<\/code> opcionales<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1152\" data-end=\"1224\">\r\n            <td data-start=\"1152\" data-end=\"1165\" data-col-size=\"sm\"><code data-start=\"1154\" data-end=\"1161\">TRIANGLE<\/code><\/td>\r\n            <td data-start=\"1165\" data-end=\"1224\" data-col-size=\"md\">Tri&aacute;ngulo<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1152\" data-end=\"1224\">\r\n            <td data-start=\"1152\" data-end=\"1165\" data-col-size=\"sm\"><code data-start=\"1154\" data-end=\"1161\">PLANE<\/code><\/td>\r\n            <td data-start=\"1165\" data-end=\"1224\" data-col-size=\"md\">Plano formado por dos tri&aacute;ngulos que comparten arista<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1225\" data-end=\"1297\">\r\n            <td data-start=\"1225\" data-end=\"1238\" data-col-size=\"sm\"><code data-start=\"1227\" data-end=\"1234\">PRISM<\/code><\/td>\r\n            <td data-start=\"1238\" data-end=\"1297\" data-col-size=\"md\">Prisma regular entre dos puntos, con <code data-start=\"1277\" data-end=\"1284\">SIDES<\/code> lados<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1298\" data-end=\"1370\">\r\n            <td data-start=\"1298\" data-end=\"1311\" data-col-size=\"sm\"><code data-start=\"1300\" data-end=\"1308\">OBJECT<\/code><\/td>\r\n            <td data-start=\"1311\" data-end=\"1370\" data-col-size=\"md\">Objeto externo en formato <code data-start=\"1339\" data-end=\"1345\">.GLB<\/code><\/td>\r\n        <\/tr>\r\n    <\/tbody>\r\n<\/table>\r\n<\/div>\r\n<\/div>\r\n<p>&nbsp;<\/p>\r\n<h4 data-start=\"1372\" data-end=\"1416\">Transformaciones permanentes (al crear)<\/h4>\r\n<div class=\"_tableContainer_80l1q_1\">\r\n<div tabindex=\"-1\" class=\"_tableWrapper_80l1q_14 group flex w-fit flex-col-reverse\">\r\n<table data-start=\"1418\" data-end=\"1911\" class=\"w-fit min-w-(--thread-content-width)\">\r\n    <thead data-start=\"1418\" data-end=\"1516\">\r\n        <tr data-start=\"1418\" data-end=\"1516\">\r\n            <th data-start=\"1418\" data-end=\"1440\" data-col-size=\"sm\">Propiedad<\/th>\r\n            <th data-start=\"1440\" data-end=\"1460\" data-col-size=\"sm\">Aplicaci&oacute;n<\/th>\r\n            <th data-start=\"1460\" data-end=\"1516\" data-col-size=\"md\">Descripci&oacute;n<\/th>\r\n        <\/tr>\r\n    <\/thead>\r\n    <tbody data-start=\"1616\" data-end=\"1911\">\r\n        <tr data-start=\"1616\" data-end=\"1714\">\r\n            <td data-start=\"1616\" data-end=\"1645\" data-col-size=\"sm\"><code data-start=\"1618\" data-end=\"1625\">WIDTH<\/code>, <code data-start=\"1627\" data-end=\"1635\">HEIGHT<\/code>, <code data-start=\"1637\" data-end=\"1644\">DEPTH<\/code><\/td>\r\n            <td data-start=\"1645\" data-end=\"1658\" data-col-size=\"sm\">Permanente<\/td>\r\n            <td data-start=\"1658\" data-end=\"1714\" data-col-size=\"md\">Redimensiona la geometr&iacute;a base<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1715\" data-end=\"1812\">\r\n            <td data-start=\"1715\" data-end=\"1737\" data-col-size=\"sm\"><code data-start=\"1717\" data-end=\"1733\">displace_x\/y\/z<\/code><\/td>\r\n            <td data-start=\"1737\" data-end=\"1757\" data-col-size=\"sm\">Permanente<\/td>\r\n            <td data-start=\"1757\" data-end=\"1812\" data-col-size=\"md\">Desplaza el modelo con respecto a su pivote interno<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"1813\" data-end=\"1911\">\r\n            <td data-start=\"1813\" data-end=\"1835\" data-col-size=\"sm\"><code data-start=\"1815\" data-end=\"1829\">rotate_x\/y\/z<\/code><\/td>\r\n            <td data-start=\"1835\" data-end=\"1855\" data-col-size=\"sm\">Permanente<\/td>\r\n            <td data-start=\"1855\" data-end=\"1911\" data-col-size=\"md\">Gira el modelo respecto a su pivote interno<\/td>\r\n        <\/tr>\r\n    <\/tbody>\r\n<\/table>\r\n<\/div>\r\n<\/div>\r\n<h4 data-start=\"1913\" data-end=\"1954\">Transformaciones din&aacute;micas (en MOVE)<\/h4>\r\n<div class=\"_tableContainer_80l1q_1\">\r\n<div tabindex=\"-1\" class=\"_tableWrapper_80l1q_14 group flex w-fit flex-col-reverse\">\r\n<table data-start=\"1956\" data-end=\"2563\" class=\"w-fit min-w-(--thread-content-width)\">\r\n    <thead data-start=\"1956\" data-end=\"2057\">\r\n        <tr data-start=\"1956\" data-end=\"2057\">\r\n            <th data-start=\"1956\" data-end=\"1978\" data-col-size=\"sm\">Acci&oacute;n<\/th>\r\n            <th data-start=\"1978\" data-end=\"2057\" data-col-size=\"md\">Descripci&oacute;n<\/th>\r\n        <\/tr>\r\n    <\/thead>\r\n    <tbody data-start=\"2160\" data-end=\"2563\">\r\n        <tr data-start=\"2160\" data-end=\"2260\">\r\n            <td data-start=\"2160\" data-end=\"2182\" data-col-size=\"sm\"><code data-start=\"2162\" data-end=\"2176\">MOVE_BETWEEN<\/code><\/td>\r\n            <td data-start=\"2182\" data-end=\"2260\" data-col-size=\"md\">Posiciona el objeto entre dos puntos manteniendo su tama&ntilde;o<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2261\" data-end=\"2361\">\r\n            <td data-start=\"2261\" data-end=\"2283\" data-col-size=\"sm\"><code data-start=\"2263\" data-end=\"2280\">STRETCH_BETWEEN<\/code><\/td>\r\n            <td data-start=\"2283\" data-end=\"2361\" data-col-size=\"md\">Escala el objeto en Y para ajustarse entre dos puntos<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2362\" data-end=\"2462\">\r\n            <td data-start=\"2362\" data-end=\"2384\" data-col-size=\"sm\"><code data-start=\"2364\" data-end=\"2374\">rotate_y<\/code><\/td>\r\n            <td data-start=\"2384\" data-end=\"2462\" data-col-size=\"md\">A&ntilde;ade rotaci&oacute;n adicional sobre su eje Y, &uacute;til para tornillos o engranajes<\/td>\r\n        <\/tr>\r\n        <tr data-start=\"2463\" data-end=\"2563\">\r\n            <td data-start=\"2463\" data-end=\"2485\" data-col-size=\"sm\"><code data-start=\"2465\" data-end=\"2468\">y<\/code><\/td>\r\n            <td data-start=\"2485\" data-end=\"2563\" data-col-size=\"md\">En <code data-start=\"2490\" data-end=\"2504\">MOVE_BETWEEN<\/code>, permite desplazar a lo largo de la l&iacute;nea<\/td>\r\n        <\/tr>\r\n    <\/tbody>\r\n<\/table>\r\n<\/div>\r\n<\/div>\r\n<p><i><\/i><br>\r\n<i>Estas acciones son an&aacute;logas a las uniones <strong data-start=\"2612\" data-end=\"2625\">PRISMATIC<\/strong> y <strong data-start=\"2628\" data-end=\"2640\">REVOLUTE<\/strong> en sistemas mec&aacute;nicos, pero aplicadas directamente desde el conocimiento matem&aacute;tico de la posici&oacute;n del objeto.<\/i><\/p>\r\n<p><b>Ejemplos<\/b><\/p>\r\n<pre>\r\n; Coloca un muelle descrito en un fichero entre dos puntos definidos\r\nMOVE {name:muelle, STRETCH_BETWEEN:&quot;[0,0,0],[0,P$x_masa,0]&quot;}\r\n\r\n; coloca un pist&oacute;n o segmento telesc&oacute;pico a lo largo de un segmento en la posici&oacute;n &quot;X&quot; con respecto al su punto central\r\nMOVE {name:amortiguador, MOVE_BETWEEN:&quot;[0,0,0],[0,P$x_masa,0]&quot;,y:P$x_masa -10}<\/pre>\r\n<p><b>Activar el modo 3D<\/b><\/p>\r\n<pre>\r\nSYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:30, CAMERA:1}<\/pre>\r\n<p><code data-start=\"3164\" data-end=\"3171\">V_WIDTH<\/code>, <code data-start=\"3173\" data-end=\"3181\">V_HEIGHT<\/code>: &aacute;rea que ocupar&aacute;n los objetos en unidades internas<br>\r\n<code data-start=\"3236\" data-end=\"3244\">CAMERA<\/code>: preset de c&aacute;mara (1 = isom&eacute;trico, 2 = cenital, etc.)<\/p>\r\n<p><b>El ejemplo:<\/b><\/p>\r\n<p>Para el ejemplo, se usan dos puntos para rotar, en ese mismo eje, una tuerca.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "252",
        "nombre": "Season 6: Agentes y componentes",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "247",
                "nombre": "Entidad virtual persistente. El agente",
                "texto": "\/*\r\n\r\n Entidad virtual persistente. El agente\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSTART 100\r\n\r\n\r\n;===> Configuración de recursos y posiciones\r\nRestroom {NAME:Restroom1, X:337, Y:344}\r\nRestroom {NAME:Restroom2, X:337, Y:258}\r\nRestroom {NAME:Restroom_AGENTE2, X:567, Y:104}\r\n\r\nPOSITION {NAME:ENTRADA, X:179, Y:301}\r\nPOSITION {NAME:SALIDA, X:559, Y:299}\r\n\r\nGRAPHIC {NAME:INFO1, TYPE:TEXT, X:335, Y:178, TEXT:\"Agente1\"}\r\nGRAPHIC {NAME:INFO2, TYPE:TEXT, X:569, Y:62, TEXT:\"Agente2\"}\r\nINITIAL contador,0\r\n\r\n\r\n\r\n;*****************************************************\r\n;===> Inicialización del agente\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente1.main,0\r\n\ttimeout agente2.main,0\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\n;===> Flujo principal de las entidades\r\nGENERATE 15,5 {NAME:GEN1, X:50, Y:300}\r\nADVANCE 10 {TO:ENTRADA}\r\n\r\nASSIGN aleatorio, FLOOR(RANDOM * 2) + 1\r\nIF (P$aleatorio==1)\r\n\tMOD {color:#990000}\r\n    ADVANCE 10 {TO:Restroom1}\r\n    REST Restroom1\r\nELSE\r\n    MOD {color:#999900}\r\n    ADVANCE 10 {TO:Restroom2}\r\n    REST Restroom2\r\nENDIF\r\n\r\n\r\n\r\nADVANCE 30,30 {TO:SALIDA}\r\n\r\nif (D$N %20 == 0)\r\n\tSIGNAL agente2.liberacion_total,X$nAgente2\r\nendif\r\n\r\nENDGENERATE 1\r\n\r\n;*******************************\r\nprocedure agente2.main\r\n    ; Asigna su nombre único como variable si hace falta\r\n    SAVEVALUE nAgente2,D$N\r\n    MOVE {NAME:INFO2, TEXT:\"Agente 1 activo\"}\r\n\r\n    WHILE (1==1) ; inútil si siempre se usa RETURN_RESTORE\r\n        REST Restroom_AGENTE2\r\n    ENDWHILE\r\n\r\n    TERMINATE_VE ; No se alcanzará nunca\r\nENDPROCEDURE\r\n\r\n;*******************************\r\nprocedure agente2.liberacion_total\r\n    MOVE {NAME:INFO2, TEXT:\"T= AC1$ Agente 2 liberando todo\"}\r\n\tWAKE Restroom1\r\n\tWAKE Restroom2\r\n\tRETURN_RESTORE\r\nENDPROCEDURE\r\n\r\n;**********************************\r\n\r\n\r\nPROCEDURE agente1.main\r\n    ; Asigna su nombre único como variable si hace falta\r\n    SAVEVALUE nAgente1,D$N\r\n    MOVE {NAME:INFO1, TEXT:\"Agente 2 activo\"}\r\n\r\n    WHILE (1==1)\r\n        ADVANCE 50 ; El agente revisa cada 50 unidades\r\n\r\n        SAVEVALUE contador, X$contador + 1\r\n        MOVE {NAME:INFO1, TEXT:\"Agente: X$contador\"}\r\n\r\n        IF (X$contador % 3==0)\r\n        \tassign liberar,R$(Restroom1,IN)\r\n            MOVE {NAME:INFO1, TEXT:\"Liberando Restroom 1 (P$liberar DE R$(Restroom1,IN))\"}\r\n            WAKE Restroom1\r\n        ENDIF\r\n\r\n        IF (X$contador % 5 ==0)\r\n        \tassign liberar,round(R$(Restroom2,IN) * 2 \/ 3)\r\n            MOVE {NAME:INFO1, TEXT:\"Liberando Restroom 2 (P$liberar DE R$(Restroom2,IN))\"}\r\n            WAKE Restroom2,P$liberar\r\n            \r\n        ENDIF\r\n\r\n    ENDWHILE\r\n\r\n    TERMINATE_VE ; No se alcanzará nunca\r\nENDPROCEDURE\r\n;**********************************\r\n\r\n\r\n",
                "descripcion": "<p>En GPSS-Plus, una <strong data-start=\"179\" data-end=\"203\">entidad virtual (VE)<\/strong> suele ser ef&iacute;mera: nace, ejecuta una serie de bloques, y termina. Sin embargo, algunos modelos requieren procesos <strong data-start=\"318\" data-end=\"333\">permanentes<\/strong> que puedan actuar como <em data-start=\"357\" data-end=\"372\">controladores<\/em>, <em data-start=\"374\" data-end=\"384\">gestores<\/em>, o <em data-start=\"388\" data-end=\"406\">procesos pasivos<\/em> que esperan &oacute;rdenes. A estas VEs especiales las llamamos <strong data-start=\"464\" data-end=\"475\">agentes<\/strong>.<\/p>\r\n<h3 data-start=\"478\" data-end=\"498\">Qu&eacute; es un agente<\/h3>\r\n<p data-start=\"500\" data-end=\"730\">Un <strong data-start=\"503\" data-end=\"513\">agente<\/strong> es simplemente una entidad virtual que nunca termina su ejecuci&oacute;n. Para ello, se inicializa con el bloque <code data-start=\"620\" data-end=\"629\">TIMEOUT<\/code> (fuera de los bloques <code data-start=\"652\" data-end=\"662\">GENERATE<\/code>), lo que hace que comience sin depender de ning&uacute;n flujo de llegada.<\/p>\r\n<p data-start=\"732\" data-end=\"802\">Una vez creada, el agente puede comportarse de dos formas principales:<\/p>\r\n<ol data-start=\"804\" data-end=\"994\">\r\n    <li data-start=\"804\" data-end=\"912\">\r\n    <p data-start=\"807\" data-end=\"912\"><strong data-start=\"807\" data-end=\"817\">Activa<\/strong>: ejecuta ciclos continuamente (por ejemplo, usando <code data-start=\"869\" data-end=\"878\">ADVANCE<\/code>) para revisar y tomar decisiones. Como un TIMER que es siempre la misma entidad.<\/p>\r\n    <\/li>\r\n    <li data-start=\"913\" data-end=\"994\">\r\n    <p data-start=\"916\" data-end=\"994\"><strong data-start=\"916\" data-end=\"926\">Pasiva<\/strong>: entra en espera con <code data-start=\"948\" data-end=\"954\">HOLD<\/code>&nbsp;y ah&iacute; permanecer&aacute; mientras no se le ordene hacer otra cosa.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<h3>C&oacute;mo se crea:<\/h3>\r\n<p data-start=\"996\" data-end=\"1067\">En el <code>PRE_RUN <\/code>llamamos a su <code>PROCEDURE <\/code>inicializador:<\/p>\r\n<pre>\r\ntimeout agente1.main,0 ; Se ejecutar&aacute; en el instante 0.<\/pre>\r\n<p data-start=\"996\" data-end=\"1067\">Que por convenio se llamar&aacute; &quot;.main&quot;.<\/p>\r\n<p data-start=\"996\" data-end=\"1067\">Este PROCEDURE se encargar&aacute; de almacenar en un SAVEVALUE su identificador para que cualquier entidad pueda acceder a ella y tras esto entrar&aacute; en el bucle infinito con espera activa o pasiva:<\/p>\r\n<pre>\r\nPROCEDURE agente1.main\r\n    SAVEVALUE nAgente1,D$N\r\n    WHILE (1==1)\r\n    ADVANCE 100\r\n    ... ; Acciones peri&oacute;dicas\r\n    ENDWHILE\r\n    TERMINATE_VE ; No se alcanzar&aacute; nunca\r\nENDPROCEDURE\r\n\r\n;----------------------------------------\r\n\r\nPROCEDURE agente2.main\r\n    SAVEVALUE nAgente2,D$N\r\n    HOLD HOLDER_AGENTES\r\n    TERMINATE_VE ; No se alcanzar&aacute; nunca\r\nENDPROCEDURE<\/pre>\r\n<p>Y sus m&eacute;todos o PROCEDURES asociados&nbsp;siguen la convenci&oacute;n <code data-start=\"1850\" data-end=\"1865\">agente.metodo<\/code>, por ejemplo:<\/p>\r\n<pre>\r\nprocedure agente2.liberacion_total\r\n&nbsp; &nbsp; MOVE {NAME:INFO2, TEXT:&quot;T= AC1$ Agente 2 liberando todo&quot;}\r\n&nbsp; &nbsp; UNHOLD HOLDER1\r\n&nbsp; &nbsp; UNHOLD HOLDER2\r\n&nbsp; &nbsp; RETURN_RESTORE ; Vuelve exactamente a su estado anterior\r\nENDPROCEDURE<\/pre>\r\n<p>Donde lo m&aacute;s importante a destacar es su finalizaci&oacute;n especial:&nbsp;<code><b>RETURN_RESTORE<\/b><\/code><\/p>\r\n<p>Este bloque devulver&aacute; al agente a la misma situaci&oacute;n en la que se encontraba al ser interrumpida mediante el <code>SIGNAL <\/code>\/ <code>SIGNALNOW<\/code>.<br>\r\nSi estaba en un <code>ADVANCE<\/code>, se reanuda con el tiempo restante ajustado.<br>\r\nSi estaba en una cola de un recurso, no se alterar&aacute; su situaci&oacute;n.<\/p>\r\n<p>N&oacute;tese que si se utilizase <code>RETURN <\/code>o <code>ENDPROCEDURE<\/code>, saltar&iacute;a a la siguiente l&iacute;nea de c&oacute;digo. &Uacute;til si se est&aacute; en una espera activa y se quiere reiniciar el tiempo de actuaci&oacute;n peri&oacute;dica.<\/p>\r\n<p><b>En el ejemplo:<\/b><\/p>\r\n<p>Vemos a dos agentes de ambos tipos liberando entidades de dos HOLDERS de formas arbitrarias.<br>\r\nEl segundo, cada 20 entidades que completan el recorrido, libera todas las atrapadas.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "312",
                "nombre": "Agente como controlador",
                "texto": "\/*\r\n\r\n  \tAgente como controlador\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nQueuer {NAME:sensor,X:403,Y:575}\r\n\r\nPOSITION {NAME:POS1,X:265,Y:454}\r\nPOSITION {NAME:POS2,X:523,Y:449}\r\nPOSITION {NAME:POS3,X:720,Y:450}\r\n\r\nGraphic {NAME:Line1,Type:LINE,color:#FF0000, X1:418,Y1:370,X2:413,Y2:383,X3:387,Y3:383,X4:381,Y4:370}\r\nGraphic {NAME:Line2,Type:LINE,color:#FF0000, X1:422,Y1:509,X2:414,Y2:486,X3:387,Y3:486,X4:382,Y4:508}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:431,Y:262,Text:\" \"}\r\n\r\ninitial nAgente,0\r\nSTART 200\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente.main,0\r\n\tTERMINATE_VE\r\nENDPROCEDURE 1\r\n\r\n;*****************************************************\r\nGENERATE 30,50 {NAME:GEN1,X:86,Y:450}\r\n\r\n\tADVANCE 20,0 {TO:POS1}\r\n\tqueue sensor\r\n\tADVANCE 20,0 {TO:POS2}\r\n\tdepart sensor\r\n\tADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE 1\r\n\r\n\r\n;*******************************\r\nprocedure agente.main\r\n\tsavevalue nAgente,D$N\r\n    assign usuarios,0\r\n    assign estado,100\r\n    assign posY2,GD$(Line1,Y2)\r\n    while (1==1)\r\n    \tadvance 1\r\n        call agente.moverPuerta\r\n    endwhile\r\n    terminate_ve\r\nendprocedure\r\n\r\n\r\nprocedure agente.moverPuerta\r\n    IF (R$(sensor,IN)<=0)\r\n    assign estado,P$estado + 5\r\n    assign estado,MIN(100,P$estado)\r\n    else \r\n    assign estado,P$estado -5\r\n    assign estado,MAX(10,P$estado)\r\n    ENDIF\r\n\r\nmove {name:Line1,Y2:P$estado + P$posY2,Y3:P$estado + P$posY2}\r\n;move {name:Text1,TEXT:\"p$usuarios P$estado | P$posY1\"}\r\n\r\nendprocedure\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>En este cap&iacute;tulo exploramos c&oacute;mo un agente puede actuar como un <strong data-start=\"345\" data-end=\"376\">controlador visual aut&oacute;nomo<\/strong>, que reacciona a los cambios de su entorno y produce efectos animados. Veremos un ejemplo t&iacute;pico: una <strong data-start=\"479\" data-end=\"500\">puerta autom&aacute;tica<\/strong> que se abre cuando detecta entidades cerca y se cierra cuando no hay nadie.<\/p>\r\n<p><b>Idea base<\/b><\/p>\r\n<p data-start=\"596\" data-end=\"810\">El agente es una VE que <strong data-start=\"638\" data-end=\"658\">no termina nunca<\/strong> y que ejecuta repetidamente una tarea: comprobar si hay entidades en una zona determinada (simulada con un <code data-start=\"766\" data-end=\"774\">QUEUER<\/code>) y mover la puerta en consecuencia.<\/p>\r\n<p>Este tipo de l&oacute;gica es ideal para comportamientos animados, sensores, sem&aacute;foros o cualquier objeto visual que act&uacute;e en base al entorno.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "250",
                "nombre": "SIGNAL Vs SIGNALNOW",
                "texto": "\/*\r\n\r\n  \tSIGNAL Vs SIGNALNOW\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nRestroom {NAME:RestroomAgentes,X:252,Y:60,visible:1}\r\n\r\nPOSITION {NAME:POS1,X:552,Y:194}\r\nPOSITION {NAME:POS2,X:552,Y:394}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:313,Y:252,Text:\"Entidad\"}\r\nGraphic {NAME:Text2,Type:TEXT,X:313,Y:232,Text:\"Entidad\"}\r\nGraphic {NAME:Text3,Type:TEXT,X:326,Y:444,Text:\"Entidad\"}\r\nGraphic {NAME:TextAgente,Type:TEXT,X:311,Y:325,Text:\"---\",color:#ff3333}\r\n\r\ninitial nAgente,0\r\nSTART 2\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente.main,0\r\n\tTERMINATE_VE\r\nENDPROCEDURE 1\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,1 {NAME:GEN1,X:100,Y:200}\r\n\t\r\n    assign resultado,0,X$nAgente\r\n\tSIGNAL agente.suma2,X$nAgente\r\n\tSIGNAL agente.multiplica10,X$nAgente\r\n\tSIGNAL agente.suma4,X$nAgente\r\n\tmove {name:Text1,TEXT:\"SIGNAL Previo: P$(resultado,X$nAgente)\"}\r\n    advance 0\r\n\tmove {name:Text2,TEXT:\"SIGNAL Resultado: P$(resultado,X$nAgente)\"}\r\n\t\r\n\tADVANCE 20,0 {TO:POS1}\r\n    \r\nENDGENERATE 1\r\n\r\nGENERATE 60,0,0,1 {NAME:GEN2,X:100,Y:400}\r\n\r\n\tassign resultado,0,X$nAgente\r\n\tSIGNALNOW agente.suma2,X$nAgente\r\n\tSIGNALNOW agente.multiplica10,X$nAgente\r\n\tSIGNALNOW agente.suma4,X$nAgente\r\n\tmove {name:Text3,TEXT:\"SIGNALNOW Resultado: P$(resultado,X$nAgente)\"}\r\n\t\r\n\tADVANCE 20,60 {TO:POS2}\r\n    \r\nENDGENERATE 1\r\n\r\n\r\n\r\n\r\n;*******************************\r\nprocedure agente.main\r\n\tsavevalue nAgente,D$N\r\n    assign resultado,0\r\n    REST RestroomAgentes\r\n    terminate_ve\r\nendprocedure\r\n\r\nprocedure agente.suma2\r\n    assign resultado,P$resultado + 2\r\n    \tmove {name:TextAgente,TEXT:\"ResultadoSUMA2: P$resultado T= AC1$\"}\r\n    RETURN_RESTORE\r\nendprocedure\r\nprocedure agente.suma4\r\n    assign resultado,P$resultado + 4\r\n    \tmove {name:TextAgente,TEXT:\"ResultadoSUMA4: P$resultado T= AC1$\"}\r\n    RETURN_RESTORE\r\nendprocedure\r\nprocedure agente.multiplica10\r\n    assign resultado,P$resultado * 10\r\n    move {name:TextAgente,TEXT:\"Resultadomultiplica10: P$resultado T= AC1$\"}\r\n    RETURN_RESTORE\r\nendprocedure\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>En este cap&iacute;tulo vamos a desgranar el funcionamiento conjunto de <strong data-start=\"367\" data-end=\"389\">la cola de eventos<\/strong> y <strong data-start=\"392\" data-end=\"415\">la pila de contexto<\/strong> (stack) dentro de GPSS-Plus, y c&oacute;mo esto afecta a los comportamientos as&iacute;ncronos con <code data-start=\"501\" data-end=\"509\">SIGNAL<\/code> y s&iacute;ncronos con&nbsp;<code data-start=\"512\" data-end=\"523\">SIGNALNOW<\/code>.<\/p>\r\n<h4 data-start=\"531\" data-end=\"557\">La cola de eventos<\/h4>\r\n<p data-start=\"559\" data-end=\"821\">Cuando una entidad ejecuta un bloque <code data-start=\"596\" data-end=\"605\">ADVANCE<\/code>, pasa a la <strong data-start=\"617\" data-end=\"636\">cola de eventos<\/strong>, que se ordena <strong data-start=\"652\" data-end=\"672\">cronol&oacute;gicamente<\/strong>. En caso de coincidencia de tiempo, la entidad que entra <strong data-start=\"730\" data-end=\"741\">primero<\/strong> tendr&aacute; prioridad (<strong data-start=\"760\" data-end=\"768\">FIFO<\/strong>).<br data-start=\"770\" data-end=\"773\">\r\nEsta cola contiene solo tres datos por elemento:<\/p>\r\n<ul>\r\n    <li>Tiempo de ejecuci&oacute;n<\/li>\r\n    <li>Referencia a la entidad<\/li>\r\n    <li>Tipo de elemento<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<h4 data-start=\"900\" data-end=\"928\">El stack de contexto<\/h4>\r\n<p data-start=\"930\" data-end=\"1150\">Cada entidad dispone de un <strong data-start=\"957\" data-end=\"974\">stack privado<\/strong>, una pila de contexto que permite reanudar su ejecuci&oacute;n tras un salto (<code data-start=\"1046\" data-end=\"1052\">CALL<\/code>, <code data-start=\"1054\" data-end=\"1063\">FOREACH<\/code>, etc.).<br data-start=\"1071\" data-end=\"1074\">\r\nEste stack es <strong data-start=\"1088\" data-end=\"1096\">LIFO<\/strong>: el &uacute;ltimo contexto en entrar es el primero en salir.<\/p>\r\n<p data-start=\"1152\" data-end=\"1315\">En llamadas <code data-start=\"1164\" data-end=\"1172\">SIGNAL<\/code> o <code data-start=\"1175\" data-end=\"1186\">SIGNALNOW<\/code> a otras entidades (agentes), se guarda en esta pila el <strong data-start=\"1242\" data-end=\"1272\">estado de ejecuci&oacute;n actual<\/strong>, para que pueda retomarse tras la llamada.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<h4 data-start=\"1322\" data-end=\"1355\">&iquest;Qu&eacute; sucede con <code data-start=\"1346\" data-end=\"1354\">SIGNAL<\/code>?<\/h4>\r\n<p data-start=\"1357\" data-end=\"1494\"><code data-start=\"1357\" data-end=\"1365\">SIGNAL<\/code> es <strong data-start=\"1369\" data-end=\"1382\">as&iacute;ncrono<\/strong>. La entidad que lo emite <strong data-start=\"1408\" data-end=\"1425\">no se detiene<\/strong>: simplemente agenda la ejecuci&oacute;n de un procedimiento en otro agente\/entidad.<\/p>\r\n<p data-start=\"1496\" data-end=\"1547\">Si enviamos tres <code data-start=\"1513\" data-end=\"1521\">SIGNAL<\/code> seguidos al mismo agente:<\/p>\r\n<pre>\r\nSIGNAL agente.suma2,X$nAgente\r\nSIGNAL agente.multiplica10,X$nAgente\r\nSIGNAL agente.suma4,X$nAgente<\/pre>\r\n<p data-start=\"1659\" data-end=\"1781\">la entidad emisora seguir&aacute; su camino, pero el agente receptor quedar&aacute; <strong data-start=\"1732\" data-end=\"1758\">planificado tres veces<\/strong> en la cola de eventos.<\/p>\r\n<p data-start=\"1783\" data-end=\"1954\">Cuando el agente tome el control, usar&aacute; su stack para ejecutar los procedimientos pendientes. Pero al ser una pila <strong data-start=\"1902\" data-end=\"1910\">LIFO<\/strong>, ejecutar&aacute; <strong data-start=\"1922\" data-end=\"1942\">en orden inverso<\/strong> al deseado:<\/p>\r\n<p data-start=\"1783\" data-end=\"1954\">(((0 + 4) * 10) + 2) = 42<\/p>\r\n<h4 data-start=\"2000\" data-end=\"2033\">&iquest;Qu&eacute; pasa con <code data-start=\"2021\" data-end=\"2032\">SIGNALNOW<\/code>?<\/h4>\r\n<p data-start=\"2035\" data-end=\"2173\"><code data-start=\"2035\" data-end=\"2046\">SIGNALNOW<\/code> es <strong data-start=\"2050\" data-end=\"2062\">s&iacute;ncrono<\/strong>. Detiene temporalmente la entidad que lo invoca, ejecuta inmediatamente al agente y luego la entidad contin&uacute;a.<\/p>\r\n<pre>\r\nSIGNALNOW agente.suma2,X$nAgente\r\nSIGNALNOW agente.multiplica10,X$nAgente\r\nSIGNALNOW agente.suma4,X$nAgente<\/pre>\r\n<p>Aqu&iacute;, las llamadas se ejecutan <strong data-start=\"2325\" data-end=\"2348\">en el orden escrito<\/strong>, porque se intercalan correctamente:<\/p>\r\n<p>(((0 + 2) * 10) + 4) = 24<\/p>\r\n<h4 data-start=\"2431\" data-end=\"2456\">Ejemplo en acci&oacute;n<\/h4>\r\n<p data-start=\"2458\" data-end=\"2665\">Este modelo define un agente con tres m&eacute;todos (<code data-start=\"2505\" data-end=\"2512\">suma2<\/code>, <code data-start=\"2514\" data-end=\"2528\">multiplica10<\/code>, <code data-start=\"2530\" data-end=\"2537\">suma4<\/code>). Dos entidades lo invocan: una usando <code data-start=\"2577\" data-end=\"2585\">SIGNAL<\/code> y otra con <code data-start=\"2597\" data-end=\"2608\">SIGNALNOW<\/code>. En pantalla se puede ver la diferencia en el resultado.<\/p>\r\n<p>En el caso de <code>SIGNAL<\/code>, se observa que inicialmente el resultado sigue siendo 0 puesto que los <code>SIGNAL <\/code>no se han ejecutado. Basta un <code>ADVANCE 0<\/code>&nbsp;(la entidad se vuelve a programar en la cola de eventos) para que los agentes se ejecuten y tras ello, el resultado est&aacute; disponible.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "253",
                "nombre": "Colas de mensajes",
                "texto": "\/*\r\n\r\n  \t \tColas de mensajes\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSTART 1000\r\n\r\nFacility {NAME:mesas,X:221,Y:60,capacity:7}\r\nRestroom {NAME:esperaServir,X:323,Y:60}\r\nRestroom {NAME:agentesDormidos,X:348,Y:357}\r\n\r\nPOSITION {NAME:COMIENDO, X:579, Y:46}\r\n\r\nGRAPHIC {NAME:INFOA_1, TYPE:TEXT, X:149, Y:296, TEXT:\"Agente1 listo\"}\r\nGRAPHIC {NAME:INFOB_1, TYPE:TEXT, X:147, Y:237, TEXT:\"infoB1\"}\r\nGRAPHIC {NAME:INFOC_1, TYPE:TEXT, X:147, Y:268, TEXT:\"infoC1\"}\r\n\r\nGRAPHIC {NAME:INFOA_2, TYPE:TEXT, X:577, Y:292, TEXT:\"Agente2 listo\"}\r\nGRAPHIC {NAME:INFOB_2, TYPE:TEXT, X:576, Y:238, TEXT:\"infoB2\"}\r\nGRAPHIC {NAME:INFOC_2, TYPE:TEXT, X:576, Y:266, TEXT:\"infoC2\"}\r\n\r\nGraphic {NAME:TextComanda,Type:TEXT,X:270,Y:132,Text:\"Comanda\"}\r\nGraphic {NAME:TextA,Type:TEXT,X:271,Y:102,Text:\"Sin servir\"}\r\nGraphic {NAME:TextB,Type:TEXT,X:469,Y:84,Text:\"Comiendo\"}\r\n\r\n;--------------------------------\r\n\r\n;\t\tCLIENTES\r\n\r\n;--------------------------------\r\nGENERATE 8,5,0,8 {NAME:Clientes, X:53, Y:61}\r\n    ADVANCE 3 {TO:mesas,FLOW:1}\r\n    seize mesas\r\n    call anadir_comanda\r\n    rest esperaServir\r\n    ADVANCE 120,30 {FROM:esperaServir,TO:COMIENDO,FLOW:1} ; comiendo\r\n    release mesas\r\n    if (R$(mesas,IN)<=0)\r\n\t    stop\r\n    endif\r\nENDGENERATE 1\r\n\r\n;**********************************\r\nprocedure anadir_comanda\r\n\r\n    ASSIGN cHorno, FLOOR(RANDOM * 4)\r\n    ASSIGN cFogon, FLOOR(RANDOM * 6)\r\n    ASSIGN cFreidora, FLOOR(RANDOM * 3)\r\n    \r\n    ASSIGN nPlatosPendientes, P$cHorno + P$cFogon + P$cFreidora\r\n\r\n    savevalue.push comandas, [1,D$N,P$cHorno,P$cFogon,P$cFreidora] ; Añade comanda\r\n    MOVE {name:TextComanda,text:\"Horno: P$cHorno ; Fogon: P$cFogon ; Freidora: P$cFreidora\"}\r\n    \r\n\t; avisamos a cocina despertando a todos, el primero que llegue atenderá la comanda\r\n    ; el segundo volverá a dormirse\r\n    wake agentesDormidos\r\n\r\nENDPROCEDURE\r\n;--------------------------------\r\n\r\n;\t\tCOCINEROS \/ AGENTES\r\n\r\n;--------------------------------\r\n\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente.main,0,1 ; agente 1\r\n\ttimeout agente.main,0,2 ; agente 2\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n\r\nprocedure agente.main\r\n    ; Asigna su nombre único como variable\r\n    if (P$PARAM_A==1)\r\n    savevalue nAgente1,D$N\r\n    assign nAgente,1\r\n    else\r\n    savevalue nAgente2,D$N\r\n    assign nAgente,2\r\n    endif\r\n    \r\n    MOVE {NAME:\"INFOA_P$nAgente\", TEXT:\"Agente activo P$PARAM_A\"}\r\n\tsavevalue comandas, []\r\n    assign tareaEnCurso,0\r\n\r\n\tWHILE (1==1)\r\n    \t\r\n        if (VD$(comandas,LENGTH)<=0)\r\n        ; Si no hay tareas, duerme (espera que un cliente lo despierte)\r\n        \tMOVE {NAME:\"INFOA_P$nAgente\", TEXT:\"T: AC1$ Cocinero P$nAgente : DURMIENDO...\", color:red}\r\n            rest agentesDormidos\r\n        endif\r\n\t\tassign nTareas,VD$(comandas,LENGTH)\r\n\t\tMOVE {NAME:\"INFOA_P$nAgente\", TEXT:\"Cocinero: tengo P$nTareas tareas, Despierto\", color:green}\r\n  \r\n    \tsavevalue.pop comandas,comanda\r\n\t\tassign tareaEnCurso,P$(comanda.0)\r\n\t\tassign nEntidad,P$(comanda.1)\r\n\t\tassign cHorno,P$(comanda.2)\r\n\t\tassign cFogon,P$(comanda.3)\r\n\t\tassign cFreidora,P$(comanda.4)\r\n  \r\n\t\tif (P$tareaEnCurso > 0) \r\n\r\n        MOVE {NAME:\"INFOB_P$nAgente\", TEXT:\"Tengo P$nTareas tareas, ejecuto la Cliente P$nEntidad\"}\r\n        MOVE {NAME:\"INFOC_P$nAgente\", TEXT:\"Horno: P$cHorno ; Fogon: P$cFogon ; Freidora: P$cFreidora\"}\r\n\r\n\t\tassign contador,0\r\n        while (P$contador<P$cHorno)\r\n        \tassign contador,P$contador + 1\r\n           \tMOVE {NAME:\"INFOB_P$nAgente\", TEXT:\"mandando comanda HORNO P$contador de P$cHorno\"}\r\n            advance 8,10\r\n            NEW platos,0,P$nEntidad,\"Horno\"\r\n        endwhile\r\n\r\n\t\tassign contador,0\r\n        while (P$contador<P$cFogon)\r\n        \tassign contador,P$contador + 1\r\n           \tMOVE {NAME:\"INFOB_P$nAgente\", TEXT:\"mandando comanda FOGON P$contador de P$cFogon\"}\r\n             advance 8,10\r\n            NEW platos,0,P$nEntidad,\"Fogon\"\r\n        endwhile\r\n\r\n\t\tassign contador,0\r\n        while (P$contador<P$cFreidora)\r\n        \tassign contador,P$contador + 1\r\n           \tMOVE {NAME:\"INFOB_P$nAgente\", TEXT:\"mandando comanda FREIDORA P$contador de P$cFreidora\"}\r\n             advance 5,5\r\n            NEW platos,0,P$nEntidad,\"Freidora\"\r\n        endwhile 3\r\n\r\n\t\tMOVE {NAME:\"INFOC_P$nAgente\", TEXT:\" \"}\r\n\t\tMOVE {NAME:\"INFOB_P$nAgente\", TEXT:\"terminada\"}\r\n        assign tareaEnCurso,0\r\n    \tendif\r\n    ENDWHILE  ; while infinito\r\n\r\n    TERMINATE ; No se alcanzará, pero por sintaxis\r\nENDPROCEDURE\r\n\r\n\r\n;--------------------------------\r\n\r\n;\t\tCOCINA\r\n\r\n;--------------------------------\r\n\r\n;===> Configuración de recursos y posiciones\r\nFacility {NAME:Horno,X:348,Y:566,capacity:2}\r\nFacility {NAME:Fogon,X:347,Y:499,capacity:4}\r\nFacility {NAME:Freidora,X:347,Y:437,capacity:3}\r\n\r\nGraphic {NAME:TextPlatos1,Type:TEXT,X:160,Y:579,Text:\"Platos...\"}\r\nGraphic {NAME:TextPlatos2,Type:TEXT,X:533,Y:578,Text:\"Platos2...\"}\r\n\r\nPOSITION {NAME:FINCOCINA, X:649, Y:509}\r\n\r\nGENERATE 0,0,0,0,0 {NAME:platos, X:69, Y:508}\r\nmove {name:TextPlatos1,text:\"Realizando plato para P$PARAM_A tipo P$PARAM_B\"}\r\nassign entidadDestino,P$PARAM_A\r\nassign tipoComanda,\"P$PARAM_B\"\r\n\r\nswitch \"P$tipoComanda\"\r\ncase ==,\"Horno\"\r\n\tADVANCE 4 {TO:Horno,FLOW:1,DECISION:COCINA}\r\n    seize Horno\r\n    ADVANCE 40,5\r\n    release Horno\r\nendcase\r\n\r\ncase ==,\"Fogon\"\r\n\tADVANCE 4 {TO:Fogon,FLOW:1,DECISION:COCINA}\r\n    seize Fogon\r\n    ADVANCE 30,5\r\n    release Fogon\r\nendcase\r\n\r\ncase ==,\"Freidora\"\r\n\tADVANCE 4 {TO:Freidora,FLOW:1,DECISION:COCINA}\r\n    seize Freidora\r\n    ADVANCE 22,3\r\n    release Freidora\r\nendcase\r\n\r\nendswitch\r\n\r\nADVANCE 5 {TO:FINCOCINA,FLOW:1,MERGE:COCINA}\r\nmove {name:TextPlatos2,text:\"Terminado plato para P$entidadDestino tipo P$tipoComanda\"}\r\n\r\nassign nPlatosPendientes, P$(nPlatosPendientes,P$entidadDestino) - 1,P$entidadDestino\r\n\r\n; si ya no hay más plazos que cocinar, empiezan a comer\r\nif (P$(nPlatosPendientes,P$entidadDestino)<=0)\r\n\twake esperaServir,0,P$entidadDestino\r\nendif\r\n\r\nENDGENERATE 0\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"819\" data-end=\"1004\">Vamos a ver un c&oacute;digo con una narrativa compleja pero cuya realizaci&oacute;n resulta natural en GPSS-Plus gracias a sus primitivas para agentes, listas de tareas y sincronizaci&oacute;n por eventos.<\/p>\r\n<p data-start=\"1009\" data-end=\"1361\">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 &quot;plato&quot; para cada tarea. Cuando todos los platos de un cliente est&aacute;n listos, este puede empezar a comer.<\/p>\r\n<p data-start=\"1366\" data-end=\"1633\">Toda la coordinaci&oacute;n se resuelve con una <strong data-start=\"1407\" data-end=\"1431\">lista FIFO de tareas<\/strong> (<code data-start=\"1433\" data-end=\"1453\">SAVEVALUE.push\/pop<\/code>) y un <code data-start=\"1460\" data-end=\"1468\">RESTROOM<\/code> com&uacute;n que regula el sue&ntilde;o y despertar de los agentes.<br data-start=\"1522\" data-end=\"1525\">\r\nNo se necesita <code data-start=\"1542\" data-end=\"1550\">SIGNAL<\/code>, ni <code data-start=\"1555\" data-end=\"1565\">ON_QUEUE<\/code>, ni <code data-start=\"1570\" data-end=\"1582\">ON_RELEASE<\/code>&nbsp;y, sin embargo, todo fluye perfectamente.<\/p>\r\n<p data-start=\"1689\" data-end=\"1837\">Este modelo muestra c&oacute;mo los agentes pueden autogestionar su agenda sin interrupciones externas, simplemente leyendo una cola de trabajo compartida.<\/p>\r\n<p data-start=\"1842\" data-end=\"1955\">Aqu&iacute;, el&nbsp;<code data-start=\"1460\" data-end=\"1468\">RESTROOM<\/code> funciona como una <em data-start=\"1878\" data-end=\"1898\">cerradura de sue&ntilde;o<\/em>: el primero que se despierte atiende, los dem&aacute;s esperan.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "311",
                "nombre": "Gestión de entidades",
                "texto": "\/*\r\n\r\n  \t \tGestión de entidades\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nRestroom {NAME:RestroomAgentes,X:100,Y:121,visible:1}\r\nqueuer {NAME:Queuer1,X:321,Y:123,on_queue:Queuer1on_queue,on_depart:Queuer1on_depart}\r\n\r\n\r\nPOSITION {NAME:POS1,X:251,Y:335}\r\nPOSITION {NAME:POS2,X:469,Y:341}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:425,Y:514,Text:\"Entidad\"}\r\nGraphic {NAME:TextAgente1,Type:TEXT,X:433,Y:462,Text:\"---\"}\r\nGraphic {NAME:TextAgente2,Type:TEXT,X:433,Y:416,Text:\"---\"}\r\n\r\n\r\ninitial nAgente,0\r\nSTART 500\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n\ttimeout agente.main,0\r\n\tTERMINATE_VE\r\nENDPROCEDURE 1\r\n\r\n;*****************************************************\r\nGENERATE 10,0 {NAME:GEN1,X:43,Y:300}\r\n\r\n\tmove {name:Text1,TEXT:\"Entidad Resultado P$(resultado,X$nAgente) AC1$\"}\r\n\t\r\n\tADVANCE 20,0 {TO:POS1}\r\n\tqueue Queuer1\r\n\tADVANCE 20,60 {TO:POS2}\r\n    depart Queuer1\r\n    \r\nENDGENERATE 1\r\n\r\n;*******************************\r\nprocedure agente.main\r\n\tsavevalue nAgente,D$N\r\n    assign datos,{}\r\n    assign resultado,0\r\n    assign media,0\r\n    assign nEntidades,0\r\n    assign tiempoTotal,0\r\n    while (1==1)\r\n    REST RestroomAgentes\r\n    endwhile\r\n    terminate_ve\r\nendprocedure\r\n;**********************************\r\nprocedure agente.IN\r\n    assign datos.entidad_P$PARAM_A,P$PARAM_B\r\n    move {name:TextAgente1,TEXT:\"IN Entidad P$PARAM_A : Tiempo Inicio: P$(datos.entidad_P$PARAM_A)\"}\r\n    RETURN_RESTORE\r\nendprocedure\r\n;**********************************\r\n\r\nprocedure agente.OUT\r\n\tassign nEntidades,P$nEntidades + 1\r\n\tassign tiempoInicio,P$(datos.entidad_P$PARAM_A)\r\n\tassign tiempoFin,P$PARAM_B\r\n    assign tiempoTotal,P$tiempoTotal + P$tiempoFin - P$tiempoInicio\r\n    assign media,P$tiempoTotal \/ P$nEntidades\r\n    move {name:TextAgente2,TEXT:\"OUT Entidad: P$PARAM_A Tiempo Total: P$tiempoTotal Media: P$media\"}\r\n    ASSIGN.DELETE datos,entidad_P$PARAM_A ; eliminamos la entrada de la lista\r\n    RETURN_RESTORE\r\nendprocedure\r\n\r\nprocedure Queuer1on_queue\r\n\tSIGNAL agente.IN,X$nAgente,P$ENTITYNUMBER,AC1$\r\n    TERMINATE_VE\r\nendprocedure\r\n\r\nprocedure Queuer1on_depart\r\n    SIGNAL agente.OUT,X$nAgente,P$ENTITYNUMBER,AC1$\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p data-start=\"321\" data-end=\"532\">En este cap&iacute;tulo exploramos c&oacute;mo un agente puede encargarse de <strong data-start=\"384\" data-end=\"420\">monitorizar el paso de entidades<\/strong> por una zona del modelo y <strong data-start=\"447\" data-end=\"477\">calcular m&eacute;tricas globales<\/strong> como el tiempo total de uso o la media de permanencia.<\/p>\r\n<p data-start=\"534\" data-end=\"776\">El agente no act&uacute;a directamente sobre las entidades, sino que <strong data-start=\"596\" data-end=\"615\">escucha eventos<\/strong> generados autom&aacute;ticamente por un QUEUER (a trav&eacute;s de sus <code data-start=\"673\" data-end=\"683\">ON_QUEUE<\/code> y <code data-start=\"686\" data-end=\"697\">ON_DEPART<\/code>), y en base a esos eventos, <strong data-start=\"726\" data-end=\"760\">registra cu&aacute;ndo entran y salen<\/strong> las entidades.<\/p>\r\n<p data-start=\"778\" data-end=\"1044\">Para ello, el agente mantiene una <strong data-start=\"812\" data-end=\"854\">estructura de datos (array asociativo)<\/strong> 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&oacute;gico del resto del modelo.<\/p>\r\n<p data-start=\"1046\" data-end=\"1239\">Este tipo de l&oacute;gica es &uacute;til para implementar <strong data-start=\"1091\" data-end=\"1122\">estad&iacute;sticas personalizadas<\/strong>, <strong data-start=\"1124\" data-end=\"1144\">auditor&iacute;a de uso<\/strong>, <strong data-start=\"1146\" data-end=\"1157\">alertas<\/strong> o cualquier control que dependa de eventos externos y del historial de entidades.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "262",
                "nombre": "Gestor de recursos",
                "texto": "\/*\r\n\r\n  \t \tGestor de recursos\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nINITIAL tramos,  [\r\n    { tiempo: 10, color: \"#009900\", nInicio: \"InicioN\", nFin: \"FinN\", nCentro: \"CentroN\", nRestroom: \"RestroomN\", nSemaforo: \"SemaforoN\", nTexto: \"TextN\" },\r\n    { tiempo: 20, color: \"#FF0099\", nInicio: \"InicioS\", nFin: \"FinS\", nCentro: \"CentroS\", nRestroom: \"RestroomS\", nSemaforo: \"SemaforoS\", nTexto: \"TextS\" },\r\n    { tiempo: 30, color: \"#000099\", nInicio: \"InicioE\", nFin: \"FinE\", nCentro: \"CentroE\", nRestroom: \"RestroomE\", nSemaforo: \"SemaforoE\", nTexto: \"TextE\" },\r\n    { tiempo: 40, color: \"#0099FF\", nInicio: \"InicioO\", nFin: \"FinO\", nCentro: \"CentroO\", nRestroom: \"RestroomO\", nSemaforo: \"SemaforoO\", nTexto: \"TextO\" }\r\n  ]\r\n  \r\n\r\nPOSITION {NAME:InicioN,X:368,Y:541}\r\nPOSITION {NAME:InicioS,X:393,Y:30}\r\nPOSITION {NAME:InicioE,X:756,Y:270}\r\nPOSITION {NAME:InicioO,X:41,Y:269}\r\n\r\nPOSITION {NAME:FinN,X:398,Y:540}\r\nPOSITION {NAME:FinS,X:416,Y:31}\r\nPOSITION {NAME:FinE,X:754,Y:298}\r\nPOSITION {NAME:FinO,X:40,Y:301}\r\n\r\nPOSITION {NAME:CentroN,X:381,Y:347}\r\nPOSITION {NAME:CentroS,X:393,Y:249}\r\nPOSITION {NAME:CentroE,X:447,Y:294}\r\nPOSITION {NAME:CentroO,X:325,Y:290}\r\n\r\nGraphic {NAME:SemaforoN,TYPE:ARC,X:413,Y:346,FCOLOR:\"red\",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}\r\nGraphic {NAME:SemaforoS,TYPE:ARC,X:363,Y:246,FCOLOR:\"red\",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}\r\nGraphic {NAME:SemaforoE,TYPE:ARC,X:453,Y:263,FCOLOR:\"red\",RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}\r\nGraphic {NAME:SemaforoO,TYPE:ARC,X:326,Y:320,FCOLOR:red,RADIUS:16,START_ANGLE:0,END_ANGLE:360,CLOSE:1}\r\n\r\nRestroom {NAME:RestroomN,X:713,Y:570,on_rest:actualiza}\r\nRestroom {NAME:RestroomS,X:713,Y:473,on_rest:actualiza}\r\nRestroom {NAME:RestroomE,X:713,Y:521,on_rest:actualiza}\r\nRestroom {NAME:RestroomO,X:713,Y:422,on_rest:actualiza}\r\n\r\nGraphic {NAME:TextN,Type:TEXT,X:412,Y:347,Text:\"N\",font:\"18px\",color:#ffffff}\r\nGraphic {NAME:TextS,Type:TEXT,X:363,Y:247,Text:\"S\",font:\"18px\",color:#ffffff}\r\nGraphic {NAME:TextE,Type:TEXT,X:453,Y:263,Text:\"E\",font:\"18px\",color:#ffffff}\r\nGraphic {NAME:TextO,Type:TEXT,X:326,Y:320,Text:\"O\",font:\"18px\",color:#ffffff}\r\n\r\nGraphic {NAME:textSem,Type:TEXT,X:165,Y:381,Text:\"Semaforo\",font:\"18px\",color:#000000}\r\n\r\nSTART 3000\r\n\r\n;----------------------------- PROCEDURE PRINCIPAL DE MOVIMIENTO\r\nprocedure toSemaforo ; P$(PARAM_A) = origen, P$PARAM_B = destino\r\n\r\nADVANCE X$(tramos.P$(PARAM_A).tiempo),10 {from:X$(tramos.P$(PARAM_A).nInicio), to:X$(tramos.P$PARAM_A.nCentro)}\r\n\r\nif (X$semaforoAbierto!=P$PARAM_A)\r\n    rest X$(tramos.P$(PARAM_A).nRestroom)\r\nendif\r\n\r\nADVANCE 10,3 {to:X$(tramos.P$PARAM_B.nCentro)}\r\nADVANCE X$(tramos.P$(PARAM_B).tiempo),10 {to:X$(tramos.P$(PARAM_B).nFin)}\r\n\r\nendprocedure\r\n\r\n;----------------------------- AGENTE SEMÁFORO\r\nprocedure agenteSemaforo.main\r\n\r\nsavevalue semaforoAbierto,0\r\n\r\nwhile (1==1)\r\n    savevalue semaforoAbiertoAnterior, X$semaforoAbierto\r\n    move {name:X$(tramos.X$(semaforoAbierto).nSemaforo),FCOLOR:yellow}\r\n    \r\n    savevalue semaforoAbierto,-1\r\n    advance 50\r\n\r\n    call calcularDestino,X$semaforoAbiertoAnterior\r\n\r\n    move {name:X$(tramos.X$(semaforoAbiertoAnterior).nSemaforo),FCOLOR:red}\r\n    savevalue semaforoAbierto,P$calcularDestino\r\n    move {name:textSem,text:\"Semaforo Abierto: X$semaforoAbierto\"}\r\n     \r\n    move {name:X$(tramos.X$(semaforoAbierto).nSemaforo),FCOLOR:green}\r\n    wake X$(tramos.X$(semaforoAbierto).nRestroom)\r\n\r\n    advance 200\r\nendwhile\r\n\r\nendprocedure\r\n\r\n;----------------------------- PROCEDURE PARA CAMBIAR DESTINO\r\nprocedure calcularDestino ; P$PARAM_A origen\r\nassign tmp, (P$PARAM_A + 1 + floor(random*3)) % 4 \r\nendprocedure P$tmp\r\n\r\n;----------------------------- ACTUALIZACIÓN DE VALORES EN PANTALLA\r\nprocedure actualiza\r\n\r\nmove {name:X$(tramos.0.nTexto),text:\"R$(X$(tramos.0.nRestroom),IN)\"}\r\nmove {name:X$(tramos.1.nTexto),text:\"R$(X$(tramos.1.nRestroom),IN)\"}\r\nmove {name:X$(tramos.2.nTexto),text:\"R$(X$(tramos.2.nRestroom),IN)\"}\r\nmove {name:X$(tramos.3.nTexto),text:\"R$(X$(tramos.3.nRestroom),IN)\"}\r\n\r\nterminate\r\n\r\nendprocedure\r\n\r\n;----------------------------- GENERADOR ÚNICO\r\n\r\nGENERATE 10,5 {NAME:GEN,X:577,Y:575,ERADIO:10}\r\n\r\nassign origen,floor(random*4)\r\nmod {color:X$(tramos.P$origen.color)}\r\n\r\ncall calcularDestino,P$origen\r\ncall toSemaforo,P$origen,P$calcularDestino\r\n\r\nENDGENERATE 1\r\n\r\n;----------------------------- ARRANQUE DEL AGENTE\r\n\r\nPROCEDURE PRE_RUN\r\n    TIMEOUT agenteSemaforo.main, 0\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"327\" data-end=\"439\">En este cap&iacute;tulo presentamos un agente que act&uacute;a como gestor inteligente de un recurso compartido: un sem&aacute;foro.<\/p>\r\n<p data-start=\"441\" data-end=\"725\">El objetivo es permitir el paso de entidades desde distintos or&iacute;genes hacia distintos destinos, pero <strong data-start=\"542\" data-end=\"563\">solo uno a la vez<\/strong>, de forma controlada. Se simula as&iacute; un cruce en el que el sem&aacute;foro cambia de direcci&oacute;n cada cierto tiempo, desbloqueando una de las cuatro posibles trayectorias.<\/p>\r\n<p data-start=\"727\" data-end=\"750\"><strong data-start=\"727\" data-end=\"750\">Estructura general:<\/strong><\/p>\r\n<ul data-start=\"752\" data-end=\"1235\">\r\n    <li data-start=\"752\" data-end=\"887\">\r\n    <p data-start=\"754\" data-end=\"887\">Un <code data-start=\"757\" data-end=\"766\">INITIAL<\/code> define la tabla <code data-start=\"783\" data-end=\"791\">tramos<\/code>, que contiene la informaci&oacute;n de cada direcci&oacute;n (nombres de posiciones, holders, colores, etc.).<\/p>\r\n    <\/li>\r\n    <li data-start=\"888\" data-end=\"1001\">\r\n    <p data-start=\"890\" data-end=\"1001\">Las entidades que circulan pasan por un <code data-start=\"930\" data-end=\"938\">HOLDER<\/code> si su direcci&oacute;n no est&aacute; activa, y contin&uacute;an si est&aacute; permitida.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1002\" data-end=\"1235\">\r\n    <p data-start=\"1004\" data-end=\"1050\">El agente <code data-start=\"1014\" data-end=\"1035\">agenteSemaforo.main<\/code> se encarga de:<\/p>\r\n    <ul data-start=\"1053\" data-end=\"1235\">\r\n        <li data-start=\"1053\" data-end=\"1088\">\r\n        <p data-start=\"1055\" data-end=\"1088\">Apagar el sem&aacute;foro actual (<code data-start=\"1082\" data-end=\"1087\">red<\/code>)<\/p>\r\n        <\/li>\r\n        <li data-start=\"1091\" data-end=\"1137\">\r\n        <p data-start=\"1093\" data-end=\"1137\">Elegir aleatoriamente el siguiente (<code data-start=\"1129\" data-end=\"1136\">green<\/code>)<\/p>\r\n        <\/li>\r\n        <li data-start=\"1140\" data-end=\"1175\">\r\n        <p data-start=\"1142\" data-end=\"1175\">Actualizar el texto y los colores<\/p>\r\n        <\/li>\r\n        <li data-start=\"1178\" data-end=\"1235\">\r\n        <p data-start=\"1180\" data-end=\"1235\">Hacer <code data-start=\"1186\" data-end=\"1194\">UNHOLD<\/code> a las entidades en espera para ese tramo<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1237\" data-end=\"1592\"><strong data-start=\"1237\" data-end=\"1259\">Aprendizaje clave:<\/strong><br data-start=\"1259\" data-end=\"1262\">\r\nEl agente act&uacute;a como <strong data-start=\"1283\" data-end=\"1318\">planificador visual y operativo<\/strong>, y permite entender c&oacute;mo un <em data-start=\"1347\" data-end=\"1353\">loop<\/em> de trabajo constante puede gobernar m&uacute;ltiples entidades, simplemente ajustando cu&aacute;ndo se permite continuar y cu&aacute;ndo no.<br data-start=\"1473\" data-end=\"1476\">\r\nEste patr&oacute;n es &uacute;til para gestionar accesos a zonas exclusivas, turnos de paso, recursos &uacute;nicos o flujos alternantes.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "371",
                "nombre": "Entidad Componente",
                "texto": "\/*\r\n\r\nComponente\r\n\r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n;SYSTEM {TYPE:OPTIONS,Speed:8}\r\n\r\nRestroom {NAME:Restroom_autobus,X:333,Y:282}\r\nPOSITION {NAME:SALIDA_URGENTE,X:335,Y:119}\r\nPOSITION {NAME:SALIDA,X:615,Y:286,type:terminate,title:end}\r\n\r\nGraphic {NAME:textAgente,Type:TEXT,X:494,Y:336,Text:\"Grupo\"}\r\nGraphic {NAME:Text2,Type:TEXT,X:331,Y:76,Text:\"Urgencia\"}\r\n\r\nSTART 500\r\n;*****************************************************\r\nPROCEDURE PRE_RUN\r\n\ttimeout verifica_cola,1\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,0 {NAME:GEN1,X:61,Y:288}\r\n\r\nASSIGN salidaUrgente,0\r\ntimeout componente_mirar_movil,0,D$N\r\nADVANCE 20,0 {TO:Restroom_autobus}\r\nREST Restroom_autobus\r\n\r\nif (P$salidaUrgente == 1)\r\nADVANCE 20,10 {TO:SALIDA_URGENTE}\r\nelse\r\nADVANCE 20,10 {TO:SALIDA}\r\nendif\r\nENDGENERATE 1\r\n\r\n;*******************************\r\nprocedure verifica_cola\r\n    while (1==1)\r\n        advance 5\r\n        if (R$(Restroom_autobus,OCCUPIED) > 5)\r\n        move {name:textAgente,text:\"Liberando [AC1: AC1$]\"}\r\n        WAKE Restroom_autobus ; todos salen\r\n        endif\r\n    endwhile\r\nendprocedure\r\n;**********************************\r\n\r\nprocedure componente_mirar_movil\r\n\tassign miEntidad,P$PARAM_A\r\n    while (D$(EXIST,P$miEntidad)==1)\r\n        advance 5\r\n        if (R$(Restroom_autobus,IS_OCCUPIED_BY,P$miEntidad) == 1 && RANDOM < 0.02)\r\n        move {name:Text2,text:\"Liberando la entidad P$miEntidad [AC1: AC1$]\"}\r\n        assign salidaUrgente,1,P$miEntidad\r\n        mod {number:P$miEntidad, color:red}\r\n        WAKE Restroom_autobus,-1,P$miEntidad ; sale solo esta entidad\r\n        endif\r\n    endwhile\r\n    terminate_ve\r\nendprocedure\r\n;**********************************\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>En un motor de simulaci&oacute;n por eventos discreto, una entidad suele ser un &uacute;nico hilo de ejecuci&oacute;n:&nbsp;un flujo secuencial que avanza bloque&aacute;ndose y reanud&aacute;ndose seg&uacute;n el modelo.<\/p>\r\n<p data-start=\"1148\" data-end=\"1237\">Este enfoque funciona, pero no coincide con el comportamiento real de muchas situaciones.<\/p>\r\n<p data-start=\"1239\" data-end=\"1377\">Una persona que espera en la cola del autob&uacute;s no solo espera:<br data-start=\"1300\" data-end=\"1303\">\r\nrespira, consulta el m&oacute;vil, piensa, se distrae y puede cambiar de opini&oacute;n.<\/p>\r\n<p data-start=\"1379\" data-end=\"1497\">Un veh&iacute;culo que se desplaza tambi&eacute;n act&uacute;a en paralelo:<br data-start=\"1433\" data-end=\"1436\">\r\nconsume energ&iacute;a, se desgasta, recibe se&ntilde;ales, ajusta su ruta.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">Modelar todo esto con un &uacute;nico flujo lineal es una simplificaci&oacute;n excesiva.<\/p>\r\n<h3>Entidades componente: simultaneidad dentro de la entidad<\/h3>\r\n<p data-start=\"1499\" data-end=\"1574\">Por lo tanto, vamos a ampliar la forma en la que podemos ver una entidad para que pueda tener varias funciones en paralelo.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">Una <b>entidad componente<\/b> es una entidad virtual que ejecuta una funci&oacute;n <em data-start=\"1927\" data-end=\"1941\">en nombre de<\/em> otra entidad principal.<br data-start=\"1955\" data-end=\"1958\">\r\nSu existencia depende de la entidad principal, pero tiene su propio flujo de ejecuci&oacute;n.<\/p>\r\n<p data-start=\"1901\" data-end=\"2057\">Esto permite que una entidad principal tenga varios comportamientos activos simult&aacute;neamente, cada uno representado por una <b>entidad componente<\/b> independiente.<\/p>\r\n<p data-start=\"2059\" data-end=\"2081\">Caracter&iacute;sticas clave:<\/p>\r\n<ul data-start=\"2083\" data-end=\"2416\">\r\n    <li data-start=\"2083\" data-end=\"2150\">\r\n    <p data-start=\"2085\" data-end=\"2150\">La entidad componente se crea cuando nace la entidad principal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2151\" data-end=\"2218\">\r\n    <p data-start=\"2153\" data-end=\"2218\">Ejecuta su l&oacute;gica en paralelo, sin bloquear el flujo principal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2219\" data-end=\"2280\">\r\n    <p data-start=\"2221\" data-end=\"2280\">Puede leer y modificar atributos de la entidad principal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2281\" data-end=\"2344\">\r\n    <p data-start=\"2283\" data-end=\"2344\">Puede despertarla, cambiar su estado o provocar decisiones.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2345\" data-end=\"2416\">\r\n    <p data-start=\"2347\" data-end=\"2416\">Finaliza autom&aacute;ticamente cuando la entidad principal deja de existir.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2418\" data-end=\"2537\">No es un &ldquo;hijo&rdquo; en sentido POO, ni un objeto, ni una propiedad.<br data-start=\"2481\" data-end=\"2484\">\r\nEs un <strong data-start=\"2490\" data-end=\"2513\">comportamiento vivo<\/strong>, modelado como entidad.<\/p>\r\n<p data-start=\"2583\" data-end=\"2690\">La cola de eventos sigue siendo estrictamente secuencial, pero ahora la <strong data-start=\"2655\" data-end=\"2689\">entidad deja de ser monol&iacute;tica<\/strong>:&nbsp;ya no representa un &uacute;nico flujo&nbsp;sino un peque&ntilde;o sistema compuesto por varias entidades cooperando y modelando m&aacute;s profundamente a la entidad principal.<\/p>\r\n<p data-start=\"2799\" data-end=\"2913\">Esto permite modelar fen&oacute;menos naturales de simultaneidad sin recurrir a hilos, sem&aacute;foros ni programaci&oacute;n externa.<\/p>\r\n<h3><strong data-start=\"2923\" data-end=\"2971\">Ciclo de vida de una entidad componente<\/strong><\/h3>\r\n<p data-start=\"2973\" data-end=\"3014\">El funcionamiento es sencillo y uniforme:<\/p>\r\n<ol data-start=\"3016\" data-end=\"3427\">\r\n    <li data-start=\"3016\" data-end=\"3047\">\r\n    <p data-start=\"3019\" data-end=\"3047\">La entidad principal nace.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3048\" data-end=\"3137\">\r\n    <p data-start=\"3051\" data-end=\"3137\">Inmediatamente se crea una entidad componente indicandole el n&uacute;mero de la entidad principal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3138\" data-end=\"3242\">\r\n    <p data-start=\"3141\" data-end=\"3242\">La entidad componente ejecuta su l&oacute;gica en bucle WHILE:<br data-start=\"3199\" data-end=\"3202\">\r\n    <code data-start=\"3205\" data-end=\"3240\">D$(EXIST,P$idEntidadPrincipal) == 1<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"3243\" data-end=\"3345\">\r\n    <p data-start=\"3246\" data-end=\"3345\">Puede influir en la entidad principal asign&aacute;ndole valores, modificando su estado o despert&aacute;ndola.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3346\" data-end=\"3427\">\r\n    <p data-start=\"3349\" data-end=\"3427\">Cuando la entidad principal desaparece, el componente termina autom&aacute;ticamente.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"3429\" data-end=\"3563\">As&iacute; se definen comportamientos paralelos naturales, como respiraci&oacute;n, desgaste, decisiones internas, sensores activos o distracciones.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">En el ejemplo vemos a personas que llegan a una parada de autob&uacute;s muy simplificado. Cuando llegan a 6, simplemente se van y mientras se encuentran en <code>REST <\/code>fuera de la cola de eventos.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">Pero las entidades tambi&eacute;n est&aacute;n leyendo el m&oacute;vil y algunos pueden decidir dejar la cola e irse con urgencia.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">Cuando la entidad componente decide que hay que abandonar la cola, ejecuta lo necesario mientras la principal est&aacute; aun dormida en el <code>RESTROOM<\/code>.<\/p>\r\n<p data-start=\"1499\" data-end=\"1574\">&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "351",
        "nombre": "Season 7: Contextos (CX$) y módulos (.mod)",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "349",
                "nombre": "Creación de recursos dinámicos 1",
                "texto": "\/*\r\n\r\nContextos y módulos. Creación dinámica 1\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:OPTIONS, SPEED:5}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nPosition {NAME:pos_exit,X:581,Y:407}\r\n\r\n; --- Facility definido de forma clásica ---\r\nFACILITY {NAME:resource_static, CAPACITY:1,  X:496, Y:164}\r\n\r\nSTART 200\r\n\r\nPROCEDURE PRE_RUN\r\n    ; Crear un segundo facility dinámico en el arranque\r\n    NEWFACILITY {NAME:resource_dynamic, CAPACITY:1,  X:300, Y:400}\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;------------------------------------------------------\r\n; --- Flujo de entidades ---\r\nGENERATE 10,0 {NAME:GEN1}     ; cada 10 ticks una entidad\r\nadvance 15 {to:resource_static}\r\n\r\nSEIZE resource_static      \r\nADVANCE 15        \r\nRELEASE resource_static\r\n\r\nadvance 15 {to:resource_dynamic}\r\n\r\nSEIZE resource_dynamic    \r\nADVANCE 17          \r\nRELEASE resource_dynamic\r\n\r\nadvance 15 {to:pos_exit}\r\n\r\nTERMINATE 1\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Antes de comenzar a ver los contextos debemos tener en cuenta cierta caracter&iacute;stica de GPSS-Plus.<\/p>\r\n<p>En general, los motores basados en Drag&amp;drop los recursos se arrastran f&aacute;cilmente sobre el canvas y ya est&aacute;n listos para usar. En GPSS-Plus hemos visto que es diferente, hay que escribir el c&oacute;digo del COMANDO.<\/p>\r\n<p>Por contra, esa caracter&iacute;stica de los Drag&amp;drop hace muy complicada la creaci&oacute;n en tiempo de ejecuci&oacute;n de un recurso. No estamos ah&iacute; para arrastrarlo al canvas. En GPSS-Plus basta con cambiar el COMANDO <code>FACILITY<\/code> por el BLOQUE <code>NEWFACILITY<\/code>.<\/p>\r\n<p>Solo se exige que est&eacute; creado antes de ser usado y para eso se suele hacer con el <code>PRE_RUN<\/code>.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "348",
                "nombre": "Creación de recursos dinámicos 2",
                "texto": "SYSTEM {TYPE:OPTIONS, SPEED:5}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nPosition {NAME:POS1,X:147,Y:311}\r\nPosition {NAME:POS2,X:542,Y:327}\r\nPosition {NAME:POS3,X:756,Y:322}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:305,Y:571,Text:\"Hello\"}\r\n\r\n; --- Facility definido de forma clásica ---\r\nFACILITY {NAME:f_static, CAPACITY:4, X:657, Y:325,color:blue}\r\n\r\nSTART 200\r\n\r\nPROCEDURE PRE_RUN\r\n    ; Crear un segundo facility dinámico en el arranque\r\n\tassign t,0\r\n    \r\n    while (P$t < 5) \r\n        assign t,P$t+1\r\n        assign pos_y,(P$t * 80 + 20)\r\n        assign nombre,\"f_dynamic_P$t\"\r\n        NEWFACILITY {NAME:P$nombre, CAPACITY:1, X:300, Y:(P$pos_y)}\r\n        NEWPOSITION {NAME:pos_P$nombre, X:200, Y:(P$pos_y)}\r\n    endwhile\r\n   \r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;------------------------------------------------------\r\n; --- Flujo de entidades ---\r\nGENERATE 6,0 {NAME:GEN1,x:58,y:310} \r\n\r\nadvance 15,0 {to:POS1,flow:1}\r\n\r\nASSIGN MINKEY,0 \r\nASSIGN MINVAL,100000\r\n\r\nassign t,0\r\nwhile (P$t < 5) \r\n    assign t,P$t+1\r\n    assign nombre,\"f_dynamic_P$t\"\r\n    if (R$(P$nombre,QUEUE) < P$MINVAL)\r\n        ASSIGN MINKEY,P$t \r\n\t\tASSIGN MINVAL,R$(P$nombre,QUEUE)\r\n    endif\r\nendwhile\r\n\r\nassign siguiente_recurso,\"f_dynamic_P$MINKEY\"\r\n\r\nmove {name:Text1,text:\"Consulta entidad D$N: P$(siguiente_recurso) Cola: R$(P$siguiente_recurso,QUEUE)\"}\r\n\r\n\r\n\r\n\r\nadvance 15,0 {to:\"pos_P$siguiente_recurso\",flow:1,DECISION:\"inicio\"}\r\nadvance 15,0 {to:\"P$siguiente_recurso\",flow:1}\r\n\r\n\r\nSEIZE P$siguiente_recurso   \r\nADVANCE 25,15   \r\nRELEASE P$siguiente_recurso\r\n\r\nadvance 15 {to:POS2,flow:1,MERGE:\"salida\"}\r\n\r\nadvance 15 {to:f_static,flow:1}\r\n\r\nSEIZE f_static  \r\nADVANCE 7,8        \r\nRELEASE f_static\r\n\r\nadvance 15 {to:POS3,flow:1}\r\n\r\nTERMINATE 1\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Ahora que ya sabemos que se pueden crear en runtime, veamos su utilidad... la m&aacute;s evidente, la de crear multitud con un iterador.<\/p>\r\n<p>Solo debemos tener en cuenta qu&eacute; nombre les hemos puesto para poder usarlos.<\/p>\r\n<p>En GPSS‑Plus, los nombres de recursos son strings reales, por lo que pueden construirse din&aacute;micamente y usarse directamente en SEIZE, RELEASE, R$(), etc.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "350",
                "nombre": "Creación de recursos dinámicos 3",
                "texto": "\/*\r\n\r\nContextos y módulos. Creación dinámica 3\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:OPTIONS, SPEED:5}\r\nPosition {NAME:pos_exit,X:581,Y:407}\r\n\r\n; --- Facility definido de forma clásica ---\r\nFACILITY {NAME:resource_static, CAPACITY:1,  X:496, Y:164}\r\n\r\nSTART 200\r\n\r\n\r\n;------------------------------------------------------\r\n; --- Flujo de entidades ---\r\nGENERATE 10,0 {NAME:GEN1}\r\nadvance 15 {to:resource_static}\r\n\r\nSEIZE resource_static      \r\nADVANCE 15        \r\nRELEASE resource_static\r\n\r\nif (R$(resource_dynamic,EXIST)==0)\r\n\tNEWFACILITY {NAME:resource_dynamic, CAPACITY:1,  X:300, Y:400}\r\nendif\r\n\r\nadvance 15 {to:resource_dynamic}\r\n\r\nSEIZE resource_dynamic    \r\nADVANCE 17          \r\nRELEASE resource_dynamic\r\n\r\nadvance 15 {to:pos_exit}\r\n\r\nTERMINATE 1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Y por &uacute;ltimo, tambi&eacute;n se pueden crear si surge la necesidad.<\/p>\r\n<p>Solo hay que tener en cuenta que un recurso no se puede modificar, si se crea, solo puede bloquearse con el bloque&nbsp;<code>LOCK<\/code>.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "275",
                "nombre": "Reutilización del código. CX$",
                "texto": "\/*\r\n\r\nContextos y módulos. Librerías gráficas\r\n\r\n\r\n*\/\r\n\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nGraphic {NAME:Text1,Type:TEXT,X:100,Y:319,Text:\"Valor actual\"}\r\nGraphic {NAME:Text2,Type:TEXT,X:300,Y:312,Text:\"Valor actual\"}\r\n\r\nSTART 100\r\n\r\ninclude .\/library_graphics\/tank.lib\r\n\r\n;-----------------------------------------------------------\r\n; Instanciamos dos depósitos con parámetros distintos\r\n; Cada uno se genera en su propio contexto: aaa y bbb\r\n; Los procedimientos serán los mismos, pero los datos y gráficos serán independientes\r\nPROCEDURE PRE_RUN\r\n\tassign config,{title:\"tank\"\r\n    \t\t,x:100,y:100 \r\n            ,width:50 ,height:180\r\n            ,value:88\r\n            ,max_value:100\r\n            ,\"color\":\"#ff0000\"}\r\n\tcall aaa.tank.init,V$config\r\n\r\n\tassign config2,{title:\"tank\"\r\n    \t\t,x:300,y:100 \r\n            ,width:50 ,height:180\r\n            ,value:88\r\n            ,max_value:100\r\n            ,\"color\":\"#ff00ff\"}\r\n            \r\n\tcall bbb.tank.init,V$config2\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n\r\n;============================================================\r\nGENERATE 25,0 {NAME:Gen1,x:100,y:400}\r\n\r\n\r\nCALL aaa.tank.set, 25\r\nCALL bbb.tank.set, 35\r\nADVANCE 5\r\n\r\nCALL aaa.tank.set, 52\r\nCALL bbb.tank.setcolor, \"blue\"\r\nADVANCE 5\r\n\r\nCALL aaa.tank.get\r\nMOVE {NAME:Text1, TEXT:\"Valor actual aaa: P$(get)\"}\r\n\r\nCALL bbb.tank.get\r\nMOVE {NAME:Text2, TEXT:\"Valor actual bbb: P$(get)\"}\r\n\r\nCALL aaa.tank.set, 75\r\nCALL bbb.tank.set, 10\r\nADVANCE 5\r\nCALL aaa.tank.set, 90\r\nADVANCE 5\r\n\r\nCALL aaa.tank.set, 10\r\nADVANCE 5\r\n\r\nENDGENERATE 1\r\n\r\n",
                "descripcion": "<p data-start=\"181\" data-end=\"370\">Seguro que a estas alturas te habr&aacute;s dado cuenta de lo que puede suponer hacer una l&oacute;gica que simule un taller completo.<br data-start=\"292\" data-end=\"295\">\r\nY nos podemos volver locos si nos dicen que son dos talleres. Y luego 10.<\/p>\r\n<p data-start=\"372\" data-end=\"625\">Parece que ah&iacute; deber&iacute;amos abandonar, pero no, para eso est&aacute;n los contextos de GPSS-Plus.<br data-start=\"464\" data-end=\"467\">\r\nNo son clases porque en este entorno de eventos discretos no tiene sentido como tal, pero veremos que se les parecen y mucho. Es m&aacute;s un espacio de nombres y aqu&iacute; todo es mucho m&aacute;s simple.<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">Imaginemos un caso algo m&aacute;s trivial, un gr&aacute;fico de un nivel de un dep&oacute;sito.<br data-start=\"709\" data-end=\"712\">\r\nPonemos las l&iacute;neas, los colores y el texto y despu&eacute;s queremos usarlo.<br data-start=\"785\" data-end=\"788\">\r\nAh&iacute; empiezan los problemas, que ese objeto, clase, dibujo, elemento o el nombre que quieras darle tiene tan incrustados los par&aacute;metros que resulta abrumador trabajar con &eacute;l.<br data-start=\"961\" data-end=\"964\">\r\nY no solo eso, sino que si tenemos dos dep&oacute;sitos, el trabajo no es solo el doble sino que hay que ir con mucho cuidado de no pisar la variables y crear tantas <code data-start=\"1126\" data-end=\"1132\">LINE<\/code> como sean necesarias.<\/p>\r\n<p data-start=\"1156\" data-end=\"1197\">Este es el problema que vamos a resolver.<\/p>\r\n<p data-start=\"1199\" data-end=\"1345\">Por analog&iacute;a se parecer&aacute; a decir que es a trav&eacute;s de clases con m&eacute;todos, variables locales e instanciaciones, pero no es as&iacute;. Se resolver&aacute; con una simple <u>variable tipo string<\/u>.<\/p>\r\n<p data-start=\"1347\" data-end=\"1397\">Hasta ahora, para mover un gr&aacute;fico podr&iacute;amos hacer:<\/p>\r\n<pre>\r\nCALL grafico_set,75<\/pre>\r\n<p data-start=\"1432\" data-end=\"1583\">Y si nuestro <code>procedure <\/code>estaba correcto, redibujar&iacute;a cierto gr&aacute;fico para mostrar ese 75% de llenado.<br data-start=\"1533\" data-end=\"1536\">\r\nGr&aacute;ficos que previamente deb&iacute;amos haber creado.<\/p>\r\n<p data-start=\"1585\" data-end=\"1742\">Ahora vamos a hacerlo diferente y de tal forma que todo lo que vamos a ver se podr&aacute; llevar a otro fichero que no nos moleste m&aacute;s y llamarlo con un <code data-start=\"1732\" data-end=\"1741\">INCLUDE<\/code>.<\/p>\r\n<h3 data-start=\"1749\" data-end=\"1789\">Lo primero: crear un m&eacute;todo descriptivo<\/h3>\r\n<p data-start=\"1791\" data-end=\"1856\">Si nuestro objeto es un dep&oacute;sito, crearemos un procedure llamado:<\/p>\r\n<pre>\r\nPROCEDURE deposito.init<\/pre>\r\n<p data-start=\"1895\" data-end=\"2165\">Este procedure va a ser invocado, normalmente, por una <code data-start=\"2024\" data-end=\"2028\">EV<\/code> desde el <code data-start=\"2038\" data-end=\"2047\">PRE_RUN<\/code> \/ <code data-start=\"2050\" data-end=\"2059\">CALL<\/code> y se va a encargar de crear todo el gr&aacute;fico en lugar de con <code data-start=\"2120\" data-end=\"2138\">COMANDOS GRAPHIC<\/code>, con bloques <code data-start=\"2152\" data-end=\"2164\">NEWGRAPHIC<\/code>.<\/p>\r\n<p data-start=\"2167\" data-end=\"2252\">Como todas las llamadas, podr&aacute;n tener sus par&aacute;metros para ser ejecutado, por ejemplo:<\/p>\r\n<pre>\r\ncall deposito.init, 20, 130,120, 100,&quot;Carga&quot;,&quot;#ff0000&quot;<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...donde <code data-start=\"2336\" data-end=\"2353\">deposito.init<\/code>&nbsp;es el procedure&nbsp;y el resto son: ancho, alto, X, Y, nombre y color. Tambi&eacute;n se puede hacer m&aacute;s c&oacute;modamente definiendo previamente un objeto con todas las caracter&iacute;sticas.<\/p>\r\n<p data-start=\"1347\" data-end=\"1397\">Y con esto podemos empezar a pintar nuestro dep&oacute;sito en base a esos par&aacute;metros:<\/p>\r\n<pre>\r\nNEWGRAPHIC { NAME:TextoNivel, GROUP:Deposito, TYPE:TEXT, X:0, Y:0, TEXT:&quot;Nivel: 0% &quot;, COLOR:#000000 }<\/pre>\r\n<p data-start=\"2665\" data-end=\"2717\">Y as&iacute; todo lo que queramos a&ntilde;adir a nuestro gr&aacute;fico.<\/p>\r\n<p data-start=\"2719\" data-end=\"2782\">Despu&eacute;s, solo tenemos que a&ntilde;adir alg&uacute;n m&eacute;todo para gestionarlo:<\/p>\r\n<pre>\r\nPROCEDURE deposito.move\r\n    MOVE {NAME:Deposito, x:P$PARAM_A, y:P$PARAM_B}\r\nENDPROCEDURE<\/pre>\r\n<p><b><span style=\"display: none;\" id=\"1758527154690S\">&nbsp;<\/span><\/b><\/p>\r\n<p><b>Con esto tendremos un gr&aacute;fico... &iexcl;pero solo uno!<\/b><\/p>\r\n<p><span style=\"display: none;\" id=\"1758527154618E\">&nbsp;<\/span><\/p>\r\n<p data-start=\"2936\" data-end=\"2955\">Ese es el problema.<\/p>\r\n<p data-start=\"2957\" data-end=\"3045\">La soluci&oacute;n viene de algo que hemos dicho referente al punto de separaci&oacute;n en el nombre del procedure.<\/p>\r\n<p data-start=\"3047\" data-end=\"3158\">Para llamar a este procedure <code data-start=\"3076\" data-end=\"3082\">init<\/code> o <code data-start=\"3085\" data-end=\"3093\">locate<\/code> podemos llamarlo de tantas formas como instanciaciones queramos:<\/p>\r\n<pre>\r\nCALL aaa.deposito.init, 20, 130,120, 100,&quot;Carga&quot;,&quot;#ff0000&quot;\r\nCALL bbb.deposito.init, 20, 80,420, 100,&quot;Nivel&quot;,&quot;#ffff00&quot;\r\nCALL ccc.deposito.init, 20, 180,320, 100,&quot;Nivel&quot;,&quot;#ff00ff&quot;\r\n...\r\nCALL aaa.deposito.locate,100,200\r\nCALL bbb.deposito.locate,200,100\r\nCALL ccc.deposito.locate,300,200\r\n<\/pre>\r\n<p data-start=\"3047\" data-end=\"3158\">Lo que hacemos es ponerle otro punto de separaci&oacute;n al nombre de la instancia de ese objeto.<\/p>\r\n<h3 data-start=\"3588\" data-end=\"3675\">Y a partir de aqu&iacute; debemos introducir el punto cr&iacute;tico: el valor del <u>string <\/u>de contexto <code data-start=\"3670\" data-end=\"3675\">CX$<\/code><\/h3>\r\n<p data-start=\"3677\" data-end=\"3739\">Llamar a este procedure <code data-start=\"3701\" data-end=\"3724\">&quot;aaa.deposito.locate&quot;<\/code> significa que:<\/p>\r\n<ol data-start=\"3741\" data-end=\"3838\">\r\n    <li data-start=\"3741\" data-end=\"3795\">\r\n    <p data-start=\"3744\" data-end=\"3795\">El nombre real del procedure es <code data-start=\"3776\" data-end=\"3795\">&quot;deposito.locate&quot;<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"3796\" data-end=\"3838\">\r\n    <p data-start=\"3799\" data-end=\"3838\">El contexto (<code data-start=\"3812\" data-end=\"3817\">CX$<\/code>) es el <u>string&nbsp;<code data-start=\"3822\" data-end=\"3838\"><\/code><\/u><code data-start=\"3822\" data-end=\"3838\">&quot;aaa&quot;<\/code><\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"3840\" data-end=\"3883\">Y hay que tenerlo muy claro y muy presente.<\/p>\r\n<p data-start=\"3885\" data-end=\"4042\">Cuando entre una entidad en un <code data-start=\"3930\" data-end=\"3941\">PROCEDURE<\/code> cuya llamada est&eacute; hecha con puntos de separaci&oacute;n, se ejecutar&aacute; la actividad bajo ese valor de <code data-start=\"4036\" data-end=\"4041\">CX$<\/code>.<\/p>\r\n<p data-start=\"4044\" data-end=\"4135\">Y con esto es suficiente para no pisar variables ni recursos entre unas instancias y otras.<\/p>\r\n<p data-start=\"4137\" data-end=\"4149\">Por ejemplo:<\/p>\r\n<pre>\r\nSAVEVALUE CX$_altura , 100<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...realmente habr&aacute; creado:<\/p>\r\n<pre>\r\nSAVEVALUE aaa_altura , 100<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">O:<\/p>\r\n<pre>\r\nNEWGRAPHIC {NAME:CX$_Deposito, TYPE:GROUP, X:0, Y:0}<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...ser&aacute; realmente:<\/p>\r\n<pre>\r\nNEWGRAPHIC {NAME:aaa_Deposito, TYPE:GROUP, X:0, Y:0}<\/pre>\r\n<p data-start=\"4433\" data-end=\"4572\">Por lo tanto, se habr&aacute;n generado tantos <code data-start=\"4473\" data-end=\"4484\">SAVEVALUE<\/code>, <code data-start=\"4486\" data-end=\"4494\">ASSIGN<\/code>,<code data-start=\"4503\" data-end=\"4512\">NEWGRAPHIC<\/code>, <code data-start=\"4514\" data-end=\"4522\">NEWRESTROOM<\/code> como formas de llamar a los <code data-start=\"4551\" data-end=\"4557\">init<\/code> de cada clase.<\/p>\r\n<p data-start=\"4574\" data-end=\"4915\">As&iacute; que solo tendremos que renombrar nuestros <code data-start=\"4620\" data-end=\"4628\">ASSIGN<\/code>s internos y <code data-start=\"4641\" data-end=\"4652\">SAVEVALUE<\/code>s y <code data-start=\"4656\" data-end=\"4662\">NAME<\/code>s.... a&ntilde;adi&eacute;ndoles delante <code data-start=\"4689\" data-end=\"4695\">&quot;CX$_&quot;<\/code><br data-start=\"4779\" data-end=\"4782\">\r\nY cuando queramos acceder a esos mismos par&aacute;metros deberemos hacerlo usando par&eacute;ntesis para seguir evitando un anidamiento indeseado.<\/p>\r\n<p data-start=\"4917\" data-end=\"4929\">Por ejemplo:<\/p>\r\n<pre>\r\nsavevalue CX$_ALTO, 100\r\n<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...se recupera con el <code data-start=\"4990\" data-end=\"4995\">SNA<\/code>:<\/p>\r\n<pre>\r\nX$(CX$_ALTO)<\/pre>\r\n<h3 data-start=\"5029\" data-end=\"5112\">Y con esto, podremos crear y mover tantas instancias de dep&oacute;sitos como queramos<\/h3>\r\n<p data-start=\"5114\" data-end=\"5205\">Todo este material lo introducimos en un fichero separado y lo llamamos desde un <code data-start=\"5195\" data-end=\"5204\">include<\/code>.<\/p>\r\n<h3>&nbsp;<\/h3>\r\n<h3>Resumen de conceptos clave de esta t&eacute;cnica:<\/h3>\r\n<p data-start=\"1347\" data-end=\"1397\">La separaci&oacute;n con puntos en un CALL del tipo aaa.bbb.ccc se interpreta as&iacute;:<br>\r\n<br>\r\n- El contexto (CX$) es &quot;aaa&quot;<br>\r\n<br>\r\n- El nombre real del procedure ejecutado es bbb.ccc<br>\r\n<br>\r\n- CX$ es un SNA clave que representa el contexto de llamada actual y puede usarse para construir nombres din&aacute;micos.<br>\r\n<br>\r\n- Las variables (ASSIGN o SAVEVALUE) se pisan si no est&aacute;n dentro del contexto. Usa siempre:<\/p>\r\n<pre>\r\nASSIGN CX$_nombre, valor\r\n<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...para evitar colisiones entre instancias.<\/p>\r\n<p data-start=\"1347\" data-end=\"1397\">- Si el m&eacute;todo necesita devolver un valor (como un &quot;get&quot;), puedes hacer:<\/p>\r\n<pre>\r\nCALL aaa.bbb.get\r\n<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">...y luego obtener el retorno con:<\/p>\r\n<pre>\r\nP$(get)\r\n<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\">- Todos los <code data-start=\"5950\" data-end=\"5961\">SAVEVALUE<\/code> internos deber&iacute;an usar <code data-start=\"5985\" data-end=\"5990\">CX$_<\/code> para ser privados a cada instancia.<br data-start=\"6026\" data-end=\"6029\">\r\nEjemplo:<\/p>\r\n<pre>\r\nSAVEVALUE CX$_estado, 1\r\n<\/pre>\r\n<p data-start=\"1347\" data-end=\"1397\"><br>\r\n<strong data-start=\"6086\" data-end=\"6100\">IMPORTANTE<\/strong>: Las <code data-start=\"6106\" data-end=\"6110\">EV<\/code> creadas con <code data-start=\"6123\" data-end=\"6131\">SIGNAL<\/code> o <code data-start=\"6134\" data-end=\"6143\">TIMEOUT<\/code> mueren al finalizar el procedure, y sus <code data-start=\"6184\" data-end=\"6192\">ASSIGN<\/code> desaparecen.<br data-start=\"6205\" data-end=\"6208\">\r\nSolo <code data-start=\"6215\" data-end=\"6221\">CALL<\/code>&nbsp;usa una entidad real que mantiene sus variables.<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">- Por coherencia y encapsulamiento, todo lo que se use desde fuera de una &quot;clase&quot; debe hacerse a trav&eacute;s de sus m&eacute;todos:<\/p>\r\n<pre>\r\nCALL instancia.objeto.metodo,...\r\n<\/pre>\r\n<p data-start=\"627\" data-end=\"1154\">...y <strong data-start=\"6448\" data-end=\"6454\">no<\/strong> accediendo directamente a los nombres internos de gr&aacute;ficos, variables o recursos.<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">&nbsp;<\/p>\r\n<h3>En el ejemplo:<\/h3>\r\n<p data-start=\"627\" data-end=\"1154\">Existen dos ficheros, el principal y una librer&iacute;a llamada &quot;<b>.\/library_graphics\/tank.lib<\/b>&quot;.<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">Puedes ver el c&oacute;digo abri&eacute;ndolo en el men&uacute; &quot;OPEN&quot;.<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">&nbsp;<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">Lo m&aacute;s interesante es que se configura la librer&iacute;a desde el programa principal desde PRE_RUN:<\/p>\r\n<pre>\r\n&nbsp; &nbsp; assign config,{title:&quot;deposito&quot;\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ,x:100,y:100&nbsp;\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ,width:50 ,height:180\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ,value:88\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ,&quot;color&quot;:&quot;#ff0000&quot;}\r\n\r\n&nbsp; &nbsp; call aaa.tank.init,V$config<\/pre>\r\n<p data-start=\"627\" data-end=\"1154\">Y se usa sin m&aacute;s desde el programa con <code>CALL<\/code>:<\/p>\r\n<pre>\r\nCALL aaa.tank.set, 10\r\n\r\nCALL aaa.tank.get\r\nMOVE {NAME:Text1, TEXT:&quot;Valor actual aaa: P$(get)&quot;}<\/pre>\r\n<p data-start=\"627\" data-end=\"1154\">&nbsp;<\/p>\r\n<p data-start=\"627\" data-end=\"1154\">&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "358",
                "nombre": "Módulos",
                "texto": "\/*\r\n\r\nContextos y módulos. Módulo simple\r\n\r\n\r\n*\/\r\nSYSTEM {TYPE:OPTIONS, SPEED:5}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nPosition {NAME:POS1,X:147,Y:311}\r\n\r\nSTART 200\r\n\r\ninclude \".\/manual_es\/Season_07_Contextos_y_modulos\/modulo.mod\"\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n\tassign config,{title:\"Planta_A\"\r\n    \t\t,x:350,y:500\r\n            }\r\n\tCALL plantaA.plantaReciclaje.init,V$config\r\n\t\r\n\tassign config,{title:\"Planta B\"\r\n    \t\t,x:350,y:100\r\n            }\r\n\tCALL plantaB.plantaReciclaje.init,V$config\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;------------------------------------------------------\r\n; --- Flujo de entidades ---\r\nGENERATE 6,0 {NAME:GEN1,x:58,y:310} \r\n\r\nadvance 15,0 {to:POS1}\r\n\r\nif (RANDOM > 0.5)\r\nscape plantaReciclaje {cx:\"plantaA\"}\r\nelse\r\nscape plantaReciclaje {cx:\"plantaB\"}\r\nendif\r\n\r\nTERMINATE 1\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Ya hemos visto los contextos <code>CX$<\/code> que resumi&eacute;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 <code>TIMEOUT<\/code>, la EV que ejecute el <code>PROCEDURE<\/code> lo har&aacute; con el mismo valor de <code>CX$<\/code> que el que lo llam&oacute;.<\/p>\r\n<p>Tambi&eacute;n se puede establecer con el BLOQUE <code>CX<\/code>:<\/p>\r\n<pre>\r\nCX &quot;sevilla&quot;<\/pre>\r\n<p>Y a partir de ese punto, el SNA <code>CX$<\/code> tendr&aacute; ese valor.<\/p>\r\n<p>Ahora que sabemos crear elementos gr&aacute;ficos con <code>NEWGRAPHIC<\/code> pasando el contexto, vamos a ver c&oacute;mo crear un m&oacute;dulo completo con el mismo sistema.<\/p>\r\n<p>Un m&oacute;dulo es conjunto de GENERATEs, NEWFACILITYs, PROCEDUREs... que  funcionar&iacute;an por s&iacute; mismos, normalmente bajo un contexto y que suelen  contener al menos un <code>GENERATE<\/code>. Todo esto en un &uacute;nico fichero al que llamaremos con la extensi&oacute;n &quot;.mod&quot;.<\/p>\r\n<p>Lo m&aacute;s particular de estos m&oacute;dulos es que se usan con un <code>INCLUDE<\/code> y los par&aacute;metros afectar&aacute;n a lo que est&eacute; en zona de bloques, pero no a lo que sea un <code>GENERATE<\/code>&nbsp;ya que es el BLOQUE\/COMANDO que no es exactamente un BLOQUE sino que  tambi&eacute;n es un <u>punto de programa<\/u> que crea entidades y donde es seguro  donde enviar una entidad con <code>SCAPE<\/code> o <code>UNLOAD<\/code>.<\/p>\r\n<p>As&iacute; que no tiene sentido hacer algo como:<\/p>\r\n<pre><strike>GENERATE {name: CX$_gen,x:100,y:200}<\/strike><\/pre>\r\n<p>porque una entidad virtual no va a pasar por ella para crear el GENERATE ni mucho menos podr&aacute; realizar un <strike>NEWGENERATE<\/strike>.<\/p>\r\n<p>Este s&iacute; es el aspecto de un <code>GENERATE<\/code> en modulo:<\/p>\r\n<pre>\r\nGENERATE 0,0,0,0 {name:hub,visible:0,x:700,y:100}\r\n&nbsp; &nbsp; advance 10,30 {from: hub , to:&quot;CX$_posicion&quot;}\r\nENDGENERATE\r\n;-------------------------------------------\r\nPROCEDURE hub.init\r\n;... creaci&oacute;n de recursos\r\nENDPROCEDURE<\/pre>\r\n<p>Al ejecutarse el <code>INCLUDE<\/code>, lo &uacute;nico que sucede es que  este fichero pasa a formar parte del c&oacute;digo general. Se concatenan los  textos de c&oacute;digo GPSS-Plus sin m&aacute;s.<\/p>\r\n<p>As&iacute; que el c&oacute;digo pasar&aacute; a tener un nuevo <code>GENERATE <\/code>llamado &quot;hub&quot; que podr&aacute; ser el punto de aterrizaje de cualquier entidad que llegue con <code>SCAPE <\/code>o con <code>UNLOAD<\/code>. Lo importante es que aterricen esas entidades con el <code>CX$<\/code> correcto.<\/p>\r\n<p>Por esto, un m&oacute;dulo sin GENERATE no puede recibir entidades desde fuera,  por lo que no puede funcionar como subsistema aut&oacute;nomo. En este  ejemplo, &quot;hub&quot; es la referencia al punto de programa.<\/p>\r\n<p>En suma, lo que tendremos en un m&oacute;dulo ser&aacute;, normalmente, uno o  varios GENERATEs que procesar&aacute;n entidades provinientes de otros  contextos o m&oacute;dulos o del principal con acceso a recursos bajo ese mismo contexto.<\/p>\r\n<p>En el ejemplo veremos un sistema b&aacute;sico.<\/p>\r\n<p>Del GENERATE principal parten las entidades para ir a parar a un conjuntos de recursos que en bloque son un m&oacute;dulo llamado &quot;plantaReciclaje&quot;.<\/p>\r\n<p>Este m&oacute;dulo tiene, adem&aacute;s de un &quot;.init&quot; similar al de creaci&oacute;n de cualquier librer&iacute;a con NewGraphic, NewFacility... tiene el <code>GENERATE <\/code>para que lleguen a &eacute;l las entidades a trav&eacute;s de <code>SCAPE<\/code>en el principal:<\/p>\r\n<pre>\r\nscape plantaReciclaje {cx:&quot;plantaA&quot;}<\/pre>\r\n<p>Y har&aacute; que cada entidad llegue al generate &quot;plantaReciclaje&quot; con su stack de direcciones vac&iacute;o pero todos los assign intactos y el valor de contexto establecido.<\/p>\r\n<pre>\r\ngenerate 0,0,0,0 {name:plantaReciclaje,visible:0}\r\n    call procesar\r\n    terminate 1\r\nendgenerate\r\n\r\nprocedure procesar\r\n    advance 10,10 {to:CX$_pos_in}\r\n    advance 10,10 {to:CX$_fac1}\r\n    seize CX$_fac1\r\n    advance 10,10\r\n    release CX$_fac1\r\n    advance 10,10 {to:CX$_pos_out}\r\nendprocedure<\/pre>\r\n<p>&nbsp;En cierto modo es algo como la cl&aacute;sica instrucci&oacute;n &quot;GOTO&quot; pero en el que solo se permite el salto a un punto de programa seguro que es el <code>GENERATE<\/code>.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "352",
                "nombre": "Los módulos. Ejemplo completo",
                "texto": "SYSTEM {TYPE:OPTIONS, SPEED:5}\r\nSYSTEM {TYPE:VISUAL,  WIDTH:900,HEIGHT:600}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nSTART 1\r\n\r\ninclude .\/logistica\/namespace.mod\r\ninclude .\/logistica\/punto_distribucion.mod\r\ninclude .\/logistica\/hub_distribucion.mod\r\ninclude .\/logistica\/transportes.mod\r\n\r\nPROCEDURE PRE_RUN\r\n\tCALL crear_puntos_de_distribucion\r\n    CALL hub_distribucion.init\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;------------------------------------------------------\r\nprocedure crear_puntos_de_distribucion\r\n\r\n\r\n    FOREACH ciudad, IN_OBJECT, V$(aCiudades)\r\n        assign datosCiudad, V$(aCiudades.P$ciudad)\r\n        \r\n        assign config,{\r\n    \t\ttitle:\"P$(ciudad)\"\r\n    \t\t,x:P$(datosCiudad.x)\r\n            ,y:P$(datosCiudad.y) \r\n            }\r\n\tcall P$ciudad.punto_distribucion.init,V$config\r\n        \r\n    ENDFOREACH\r\nendprocedure\r\n\r\n",
                "descripcion": "<p>Vamos a ver un ejemplo de un sistema de control log&iacute;stico.<\/p>\r\n<p>Cada m&oacute;dulo se carga en un INCLUDE que se especializa en cada cosa.<\/p>\r\n<p>Un JSON con los datos de varias ciudades ser&aacute; el punto de partida para crear los conjuntos de recursos de manera an&aacute;loga a la que hemos visto.<\/p>\r\n<p>Los paquetes se generan en los &quot;punto_distribucion&quot; almacen&aacute;ndose en un <code>RESTROOM<\/code>&nbsp;particular de ese punto.<\/p>\r\n<p>Una furgoneta recorre los puntos de distribuci&oacute;n cargando en su BACKPACK los paquetes provinientes del <code>RESTROOM<\/code>. Los BACKPACK y los <code>RESTROOM<\/code> funcionan entre ellos. No es posible poner en una mochila las entidades de ning&uacute;n otro recurso.<\/p>\r\n<p>La furgoneta descarga todas la entidades en el punto seguro <code>GENERATE<\/code>&nbsp;&quot;hub_distribucion&quot; que se encargar&aacute; de clasificar los paquetes en diferentes <code>RESTROOM<\/code>s. Tras esto, la furgoneta cargar&aacute; cada saca ya clasificada har&aacute; de nuevo el recorrido entregando los paquetes en su destino.<\/p>\r\n<p>Al final, es un peque&ntilde;o ejemplo de 150 l&iacute;neas de c&oacute;digo para hacer algo aparentemente complejo.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "373",
                "nombre": "La rehidratación",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nPOSITION {NAME:Pos1,X:368,Y:429}\r\nPOSITION {NAME:Pos2,X:634,Y:452}\r\nPOSITION {NAME:Pos3,X:592,Y:164}\r\nPOSITION {NAME:Pos4,X:343,Y:217}\r\n\r\ninitial fsm_camion_logic, {\r\n    STATES: [\r\n        \"F1\", \"F2\", \"F3\", \"F4\"\r\n    ],\r\n\r\n    TRANSITIONS: [\r\n        {FROM:\"F1\", INPUT:\"tick\", TO:\"F2\"},\r\n        {FROM:\"F2\", INPUT:\"tick\", TO:\"F3\"},\r\n        {FROM:\"F3\", INPUT:\"tick\", TO:\"F4\"},\r\n        {FROM:\"F4\", INPUT:\"tick\", TO:\"F1\"}\r\n    ],\r\n\r\n    INITIAL: \"F1\"\r\n}\r\n\r\nFSM {NAME:FSM_CAMION, LOCAL:1, LOGIC:V$(fsm_camion_logic)}\r\nFACILITY {NAME:Fac1,X:633,Y:385}\r\nGraphic {NAME:Text1,Type:TEXT,X:324,Y:100}\r\n\r\nSTART 100\r\n;-------------------------------\r\nPROCEDURE PRE_RUN \r\n  FORWARD_AC1 105\r\n    assign params,{status:\"F2\",tiempoIni:90,tiempo:110,matricula:\"1111\"}\r\n\tnew GEN1,0,V$params\r\n    assign params,{status:\"F3\",tiempoIni:88,tiempo:108,matricula:\"2222\"}\r\n\tnew GEN1,0,V$params\r\n    assign params,{status:\"F2\",tiempoIni:89,tiempo:109,matricula:\"3333\"}\r\n\tnew GEN1,0,V$params\r\n    assign params,{status:\"F2\",tiempoIni:104,tiempo:124,matricula:\"4444\"}\r\n\tnew GEN1,0,V$params\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n\r\n;-------------------------------\r\nPROCEDURE camion_update\r\n\t; PARAM_A -> Numero entidad\r\n\t; PARAM_B -> tiempo final\r\n\t; PARAM_C -> tiempo inicial\r\n\tmove {name:Text1,text:\"camion_update P$(FSM_CAMION,P$PARAM_A) P$PARAM_C\"}\r\n    if (D$(IN_ADVANCE,P$PARAM_A)==1)\r\n    \tupdate P$PARAM_A, P$PARAM_B, P$PARAM_C\r\n    endif\r\n    TERMINATE_VE\r\nendprocedure 1\r\n;-------------------------------\r\n\r\nGENERATE 0,0,0,0 {NAME:GEN1,X:62,Y:396}\r\n    assign params,V$PARAM_A\r\n    assign FSM_CAMION,\"P$(params.status)\"\r\n    mod {subtitle:\"P$(params.matricula)\"}\r\n    ; debe ser una VE y que se ejecute una vez hayan arrancado los advance.\r\n    timeout camion_update,0,D$N,P$(params.tiempo),P$(params.tiempoIni)\r\n\t\r\n   ; move {name:Text1,text:\"Camión inicializado P$PARAM_C R$(FSM_CAMION,STATE) P$FSM_CAMION\"}\r\n\twhile (1==1)\r\n    \tSTATE FSM_CAMION,\"tick\"\r\n        \r\n    \tswitch \"R$(FSM_CAMION,STATE)\"\r\n\t    case ==,\"F1\"\r\n\t        advance 20 {from:Pos4, to:Pos1}\r\n        endcase\r\n\t    case ==,\"F2\"\r\n        \t advance 20 {from:Pos1, to:Pos2}\r\n        endcase\r\n\t    case ==,\"F3\"\r\n            seize Fac1\r\n  \t        advance 40 {from:Pos2, to:Pos3}\r\n            release Fac1\r\n        endcase\r\n\t    case ==,\"F4\"\r\n\t        advance 20 {from:Pos3, to:Pos4}\r\n        endcase\r\n\r\n        endswitch\r\n    endwhile\r\n\r\nendgenerate 1\r\n;-------------------------------\r\n\r\n\r\n",
                "descripcion": "<p>Un concepto avanzado y complejo cuando el sistema crece es <b>la rehidrataci&oacute;n<\/b>.<\/p>\r\n<p>Rehidratar significa <strong>cargar el estado del sistema no desde el inicio, sino desde un punto avanzado en el tiempo<\/strong>, reconstruyendo la simulaci&oacute;n tal y como estaba en ese instante.<br>\r\nEn el ejemplo log&iacute;stico esto es fundamental: no es lo mismo &ldquo;empezar a simular paquetes&rdquo; que <strong>cargar el estado real de un ERP<\/strong> y continuar la simulaci&oacute;n desde ah&iacute;.<\/p>\r\n<p>De esta forma, el modelador se hace responsable de guardar todo aquello que quiera poder restaurar.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>Este proceso implica resolver <strong>tres problemas profundos<\/strong>:<\/p>\r\n<p>&nbsp;<\/p>\r\n<ol>\r\n    <li>El tiempo simulado global (AC1) debe avanzar hasta el instante guardado.<\/li>\r\n    <li>Las entidades deben reprogramar sus tiempos internos, que siempre deben ser posteriores al nuevo AC1.<\/li>\r\n    <li><b>La forma de modelar cambia radicalmente<\/b>: ya no basta con describir procesos; ahora hay que reconstruirlos.<\/li>\r\n<\/ol>\r\n<p>En esta fase, m&aacute;s que modelar, estamos <strong>programando<\/strong>. La rehidrataci&oacute;n es un problema t&eacute;cnico, no declarativo.<\/p>\r\n<p><b>En el ejemplo:<\/b><\/p>\r\n<p>As&iacute; que supongamos que lo que tenemos almacenado es que 4 camiones daban vueltas en un circuito de 4 posiciones. Los datos almacenados por &uacute;ltima vez son:<\/p>\r\n<ul>\r\n    <li>Tiempo del sistema: 105<\/li>\r\n    <li>Cami&oacute;n 1: Pas&oacute; por la fase F3 y llegar&iacute;a a destino en 110<\/li>\r\n    <li>Cami&oacute;n 2: Pas&oacute; por la fase F1 y llegar&iacute;a a destino en 108<\/li>\r\n    <li>Cami&oacute;n 3: Pas&oacute; por la fase F3 y llegar&iacute;a a destino en 109<\/li>\r\n    <li>Cami&oacute;n 4: Pas&oacute; por la fase F2 y llegar&iacute;a a destino en 109<\/li>\r\n<\/ul>\r\n<p>Para reconstruir (rehidratar) este estado necesitamos resolver los tres problemas anteriores.<\/p>\r\n<p><strong><\/strong><\/p>\r\n<p><strong>1. Avanzar el tiempo global<\/strong><\/p>\r\n<p>El bloque <code>FORWARD_AC1<\/code> permite mover el reloj simulado hacia adelante.<br>\r\nLo habitual es colocarlo como <strong>primera instrucci&oacute;n del <code>PRE_RUN<\/code><\/strong>, de modo que el modelo arranque directamente en el instante deseado.<\/p>\r\n<p><strong><\/strong><\/p>\r\n<p><strong>2. Reprogramar los tiempos de las entidades<\/strong><\/p>\r\n<p>Una vez que AC1 ha avanzado, todas las entidades deben tener tiempos <strong>posteriores<\/strong> al nuevo AC1.<br>\r\nEsto se consigue con <code>UPDATE<\/code>, que reprograma el final de un <code>ADVANCE<\/code>. Y en este caso, a&ntilde;adiremos tambi&eacute;n el tiempo de inicio para que la visualizaci&oacute;n comience all&iacute; donde se qued&oacute;.<\/p>\r\n<p>La &uacute;nica condici&oacute;n es cr&iacute;tica:<\/p>\r\n<p><strong>UPDATE debe ejecutarse desde una VE cuando la entidad ya est&eacute; dentro de un ADVANCE.<\/strong><br>\r\nPor eso la rehidrataci&oacute;n requiere una peque&ntilde;a coreograf&iacute;a temporal. Primero la introducimos en la cola correspondiente y despu&eacute;s actualizamos los tiempos a trav&eacute;s de un <code>TIMEOUT<\/code>inmediato.<\/p>\r\n<p><strong><\/strong><\/p>\r\n<p><strong>3. Reconstruir el punto del programa<\/strong><\/p>\r\n<p>Este es el problema m&aacute;s complejo.<br>\r\nSi existiera un <code>GOTO<\/code>, ser&iacute;a trivial: saltar&iacute;amos al punto exacto donde estaba cada entidad.<br>\r\nPero no existe, y no debe existir.<\/p>\r\n<p>La soluci&oacute;n correcta es:<\/p>\r\n<p><strong>Crear una m&aacute;quina de estados finitos (FSM) local para cada entidad.<\/strong><\/p>\r\n<p>El estado de la FSM representa <strong>en qu&eacute; punto del programa estaba la entidad<\/strong> cuando se guard&oacute; el sistema.<br>\r\nLa l&oacute;gica del modelo se reescribe para que cada entidad avance seg&uacute;n su estado.<\/p>\r\n<p>Esto convierte el flujo del programa en un <strong>aut&oacute;mata expl&iacute;cito<\/strong>, perfectamente restaurable.<\/p>\r\n<p>&nbsp;<strong><\/strong><\/p>\r\n<p><strong>4. El detalle clave: toda entidad termina siempre en una cola<\/strong><\/p>\r\n<p>En un motor DES, el &ldquo;estado &uacute;ltimo&rdquo; de una entidad siempre es uno de estos:<\/p>\r\n<ul>\r\n    <li><strong>La cola de eventos<\/strong>, si est&aacute; en un <code>ADVANCE<\/code>.<\/li>\r\n    <li><strong>La cola de un recurso<\/strong>, si est&aacute; en un <code>SEIZE<\/code>.<\/li>\r\n    <li><strong>Una cola expl&iacute;cita<\/strong>, si est&aacute; esperando.<\/li>\r\n<\/ul>\r\n<p>Por tanto:<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><strong>Los estados de la FSM deben corresponderse con estas colas.<\/strong><\/p>\r\n<p>&nbsp;<\/p>\r\n<p>No necesitamos reconstruir colas internas ni manipular estructuras ocultas:<br>\r\nbasta con reinsertar las entidades en el punto correcto del programa, y el motor reconstruye el resto autom&aacute;ticamente.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "236",
        "nombre": "Season 8: Estadísticas",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "234",
                "nombre": "Acumulando datos en tablas",
                "texto": "\/*\r\n\r\n  \t \tAcumulando datos en tablas\r\n \r\n*\/\r\nFACILITY {NAME:VENTANILLA1,X:380,Y:348,capacity:4}\r\nPOSITION {NAME:POS1,X:218,Y:437}\r\nPOSITION {NAME:POS2,X:591,Y:429}\r\nPOSITION {NAME:POS3,X:713,Y:329}\r\n\r\nGraphic {NAME:Line1,Type:LINE,color:#FF0000, X1:218,Y1:500,X2:592,Y2:500}\r\nGraphic {NAME:Text1,Type:TEXT,X:410,Y:527,Text:\"Estadistic section\"}\r\n\r\nTABLE {name: TABLA1,E_BIN_START:50,E_BIN_SIZE:1,E_BIN_COUNT:60,EXPRESSION:(AC1$ - P$TIEMPOINICIO)}\r\n;TABLE {name: TABLA1,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:160,EXPRESSION:(AC1$ - D$M0)}\r\n\r\nSTART 500 \r\n\r\n;***************************************************************\r\n\r\n\r\nGENERATE 8,3 {NAME:GEN1,X:66,Y:350}\r\n\r\n    ADVANCE 40,0 {TO:POS1}\r\n    ASSIGN TIEMPOINICIO,AC1$\r\n    ADVANCE 20,0 {TO:VENTANILLA1}\r\n\r\n    SEIZE VENTANILLA1\r\n    ADVANCE 20,20\r\n    RELEASE VENTANILLA1\r\n\r\n\r\n    ADVANCE 20,0 {TO:POS2}\r\n    TABULATE TABLA1\r\n    ADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE 1\r\n\r\n;***************************************************************\r\n",
                "descripcion": "<p data-start=\"385\" data-end=\"574\">La simulaci&oacute;n y la estad&iacute;stica van de la mano. Poco podremos hacer si no tenemos claros algunos conceptos b&aacute;sicos. En GPSS-Plus, los c&aacute;lculos estad&iacute;sticos se dividen en tres grandes grupos:<\/p>\r\n<ol data-start=\"576\" data-end=\"823\">\r\n    <li data-start=\"576\" data-end=\"658\">\r\n    <p data-start=\"579\" data-end=\"658\"><strong data-start=\"579\" data-end=\"630\">Tiempos que una entidad permanece en un recurso<\/strong> (orientado a la entidad).<\/p>\r\n    <\/li>\r\n    <li data-start=\"659\" data-end=\"741\">\r\n    <p data-start=\"662\" data-end=\"741\"><strong data-start=\"662\" data-end=\"715\">Tiempos que un n&uacute;mero de entidades est&aacute; ocupando un recurso<\/strong> (orientado al recurso).<\/p>\r\n    <\/li>\r\n    <li data-start=\"742\" data-end=\"823\">\r\n    <p data-start=\"745\" data-end=\"823\"><strong data-start=\"745\" data-end=\"782\">Cualquier otro dato personalizado<\/strong> que quieras medir (orientado al modelo).<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"825\" data-end=\"975\">En este primer ejemplo, trabajamos con el <strong data-start=\"1035\" data-end=\"1045\">caso 3<\/strong>, donde queremos medir el <strong data-start=\"1071\" data-end=\"1132\">tiempo que una entidad permanece en un tramo del circuito<\/strong>, incluyendo un recurso (una <code data-start=\"1161\" data-end=\"1171\">FACILITY<\/code>) y su entorno.<\/p>\r\n<h4 data-start=\"977\" data-end=\"1032\">Uso de TABLE para registrar tiempos de permanencia:<\/h4>\r\n<p data-start=\"1034\" data-end=\"1189\">GPSS-Plus automatiza gran parte del trabajo. Solo tienes que&nbsp;crear una <code data-start=\"1152\" data-end=\"1159\">TABLE<\/code> con los par&aacute;metros adecuados:<\/p>\r\n<pre>\r\nTABLE {name: TABLA1, E_BIN_START:0, E_BIN_SIZE:1, E_BIN_COUNT:100, EXPRESSION:(AC1$ - P$TIEMPOINICIO)}<\/pre>\r\n<p data-start=\"1306\" data-end=\"1550\">Esto configura una tabla de 100 celdas de 1 unidad de ancho, que empieza en 0. Cada vez que ejecutemos <code data-start=\"1409\" data-end=\"1426\">TABULATE TABLA1<\/code>, se ejecutar&aacute; <b>EXPRESSION :<\/b><\/p>\r\n<pre><b>(AC1$ - P$TIEMPOINICIO)<\/b><\/pre>\r\n<p data-start=\"1306\" data-end=\"1550\">donde calcular&aacute; la diferencia entre el tiempo actual (<code data-start=\"1479\" data-end=\"1485\">AC1$<\/code>) y un instante anterior (<code data-start=\"1511\" data-end=\"1527\">P$TIEMPOINICIO<\/code>) guardado previamente.<\/p>\r\n<p data-start=\"1552\" data-end=\"1664\">Este valor, que representa la permanencia de la entidad en el conjunto de recursos o zona del modelo, se acumular&aacute; en la celda correspondiente.<\/p>\r\n<h4 data-start=\"1666\" data-end=\"1688\">Un ejemplo b&aacute;sico<\/h4>\r\n<p data-start=\"1690\" data-end=\"1844\">Creamos entidades que avanzan <b>DESDE UN PUNTO<\/b>&nbsp;pasando por una <code data-start=\"1730\" data-end=\"1740\">FACILITY<\/code>&nbsp;Y <b>HASTA OTRO PUNTO<\/b>. despu&eacute;s tabulamos la duraci&oacute;n:<\/p>\r\n<pre>\r\nGENERATE 8,3\r\n    ADVANCE 30,0 {TO:POS1}\r\n    ASSIGN TIEMPOINICIO,AC1$      ; Inicio del c&oacute;mputo\r\n    ADVANCE 20,0 {TO:VENTANILLA1} ; + 20\r\n\r\n    SEIZE VENTANILLA1\r\n    ADVANCE 20,20                 ; +20 a +40 (aleatorio) + Tiempo en cola\r\n    RELEASE VENTANILLA1\r\n\r\n\r\n    ADVANCE 20,0 {TO:POS2}        ; + 20\r\n    TABULATE TABLA1               ; Fin del c&oacute;mputo\r\n    ADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE\r\n\r\n<\/pre>\r\n<h4 data-start=\"2026\" data-end=\"2050\">&iquest;Qu&eacute; mide la tabla?<\/h4>\r\n<p data-start=\"2052\" data-end=\"2282\">Si observas los resultados en el informe (bot&oacute;n superior derecho), ver&aacute;s una <strong data-start=\"2129\" data-end=\"2157\">distribuci&oacute;n rectangular<\/strong> (uniforme) entre 60 y 80, <b>con algunos tiempos superiores de las entidades que han permanecido en la cola<\/b>. Es decir, que todas las entidades han permanecido un tiempo dentro de ese rango, como se esperaba.<\/p>\r\n<p data-start=\"2284\" data-end=\"2397\">Este es un buen punto de partida para empezar a analizar comportamientos internos del modelo con datos objetivos.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "357",
                "nombre": "Tiempo de entidades",
                "texto": "\/*\r\n\r\n  \t \tTiempo de entidades\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:7}\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\nFacility {NAME:ventanilla1,X:377,Y:362,capacity:10,E_BIN_START:18,E_BIN_SIZE:1,E_BIN_COUNT:14}\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 8,0,0,0 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 10 {TO:ventanilla1}\r\n\r\nseize ventanilla1\r\nADVANCE 20,10\r\nrelease ventanilla1\r\n\r\nADVANCE 10 {TO:POS1}\r\nTERMINATE 1\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"214\" data-end=\"355\">Ahora pasamos a las estad&iacute;sticas centradas en el <strong>tiempo de las entidades en el recurso<\/strong>.<\/p>\r\n<p data-start=\"214\" data-end=\"355\">El las estad&iacute;sticas calculadas sobre un &uacute;nico recurso se calculan autom&aacute;ticamente por GPSS-Plus:<\/p>\r\n<pre>\r\nFacility {NAME:ventanilla1,X:377,Y:362,capacity:10\r\n     ,E_BIN_START:18,E_BIN_SIZE:1,E_BIN_COUNT:14\r\n     }<\/pre>\r\n<p data-start=\"357\" data-end=\"556\">Medimos el tiempo que una entidad (E) que permanece en el recurso con los par&aacute;metros <code data-start=\"448\" data-end=\"457\">E_BIN_*<\/code>&nbsp;que tiene el mismo significado que en el bloque <code>TABLE<\/code>.<\/p>\r\n<p data-start=\"722\" data-end=\"948\">En el ejemplo siguiente, se generan entidades a un ritmo constante, y la <code data-start=\"795\" data-end=\"805\">FACILITY<\/code>&nbsp;contiene un ADVANCE 20,10 lo que significa que el resultado estad&iacute;stico ser&aacute;n datos entre 20 y 30.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "237",
                "nombre": "Uso del recurso",
                "texto": "\/*\r\n\r\n  \t \tUso del recurso\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:7}\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\nFacility {NAME:ventanilla1,X:377,Y:362,capacity:10,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:11}\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 10 {TO:ventanilla1}\r\n\r\nseize ventanilla1\r\nADVANCE 20,10\r\nrelease ventanilla1\r\n\r\nADVANCE 10 {TO:POS1}\r\nENDGENERATE 1\r\n\r\n",
                "descripcion": "<p data-start=\"214\" data-end=\"355\">Ahora pasamos a las estad&iacute;sticas centradas en el <strong data-start=\"263\" data-end=\"284\">uso de un recurso<\/strong>, es decir, en cu&aacute;nto tiempo ha estado ocupado por una o m&aacute;s entidades.<\/p>\r\n<p data-start=\"357\" data-end=\"556\">Si antes med&iacute;amos el tiempo que una entidad (E) permanece en el recurso con los par&aacute;metros <code data-start=\"448\" data-end=\"457\">E_BIN_*<\/code>, ahora, para observar <strong data-start=\"480\" data-end=\"537\">c&oacute;mo de ocupado ha estado el recurso en cada instante<\/strong>, usamos <code data-start=\"546\" data-end=\"555\">R_BIN_*<\/code>.<\/p>\r\n<p data-start=\"558\" data-end=\"720\">La tabla creada con <code data-start=\"578\" data-end=\"587\">R_BIN_*<\/code> nos indica, para cada nivel de ocupaci&oacute;n (0, 1, 2, ...), <strong data-start=\"645\" data-end=\"662\">cu&aacute;nto tiempo<\/strong> el recurso ha estado exactamente con esa cantidad de uso.<\/p>\r\n<p data-start=\"722\" data-end=\"948\">En el ejemplo siguiente, se generan entidades a un ritmo constante, y la <code data-start=\"795\" data-end=\"805\">FACILITY<\/code> tiende a tener <strong data-start=\"821\" data-end=\"854\">entre 4 y 6 unidades ocupadas<\/strong>, algo que se ver&aacute; reflejado como un pico en ese rango en la gr&aacute;fica generada autom&aacute;ticamente.<\/p>\r\n<p data-start=\"950\" data-end=\"1122\">Por supuesto, nada impide que uses a la vez <code data-start=\"994\" data-end=\"1003\">E_BIN_*<\/code> y <code data-start=\"1006\" data-end=\"1015\">R_BIN_*<\/code> sobre un mismo recurso, si deseas tener una visi&oacute;n desde ambos &aacute;ngulos: el de la entidad y el del recurso.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "238",
                "nombre": "Las colas",
                "texto": "\/*\r\n\r\n  \t \tLas colas\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:7}\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\n\r\nQUEUER {NAME:Qventanilla1,X:374,Y:424\r\n\t,R_BIN_START:0,R_BIN_SIZE:1,R_BIN_COUNT:20 \r\n    ,E_BIN_START:0,E_BIN_SIZE:1,E_BIN_COUNT:20\r\n    }\r\n\r\nFacility {NAME:ventanilla1,X:373,Y:365,capacity:5}\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 5,3,0,0 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 10 {TO:ventanilla1}\r\n\r\nqueue Qventanilla1\r\nseize ventanilla1\r\ndepart Qventanilla1\r\nADVANCE 20,10\r\nrelease ventanilla1\r\n\r\nADVANCE 10 {TO:POS1}\r\nENDGENERATE 1\r\n",
                "descripcion": "<p data-start=\"228\" data-end=\"449\">Las colas tambi&eacute;n pueden ser objeto de an&aacute;lisis estad&iacute;stico. En GPSS-Plus, funcionan exactamente igual que los recursos, pero se gestionan de forma separada mediante estructuras espec&iacute;ficas llamadas <code data-start=\"427\" data-end=\"435\">QUEUER<\/code>.<\/p>\r\n<p data-start=\"451\" data-end=\"556\">Para activar la recogida de estad&iacute;sticas, basta con envolver el acceso al recurso con <code data-start=\"537\" data-end=\"544\">QUEUE<\/code> y <code data-start=\"547\" data-end=\"555\">DEPART<\/code>:<\/p>\r\n<pre>\r\nqueue Qventanilla1\r\nseize ventanilla1\r\ndepart Qventanilla1<\/pre>\r\n<p data-start=\"628\" data-end=\"667\">Esto habilita el c&aacute;lculo autom&aacute;tico de:<\/p>\r\n<ul data-start=\"669\" data-end=\"807\">\r\n    <li data-start=\"669\" data-end=\"739\">\r\n    <p data-start=\"671\" data-end=\"739\"><strong data-start=\"671\" data-end=\"705\">El n&uacute;mero de entidades en cola<\/strong> a lo largo del tiempo (<code data-start=\"729\" data-end=\"738\">R_BIN_*<\/code>)<\/p>\r\n    <\/li>\r\n    <li data-start=\"740\" data-end=\"807\">\r\n    <p data-start=\"742\" data-end=\"807\"><strong data-start=\"742\" data-end=\"765\">El tiempo de espera<\/strong> que cada entidad pasa en cola (<code data-start=\"797\" data-end=\"806\">E_BIN_*<\/code>)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"809\" data-end=\"989\">En el informe, podr&aacute;s visualizar gr&aacute;ficamente c&oacute;mo se comport&oacute; la cola: cu&aacute;ndo se form&oacute;, cu&aacute;nto tiempo esperaron las entidades, y si el dimensionamiento del recurso fue suficiente.<\/p>\r\n<p data-start=\"991\" data-end=\"1153\">El siguiente ejemplo muestra una <code data-start=\"1024\" data-end=\"1034\">FACILITY<\/code> con colas frecuentes. Al aplicar <code data-start=\"1068\" data-end=\"1075\">QUEUE<\/code>\/<code data-start=\"1076\" data-end=\"1084\">DEPART<\/code>, el sistema captura toda la estad&iacute;stica sin ning&uacute;n c&oacute;digo adicional.<\/p>\r\n<p data-start=\"451\" data-end=\"556\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "239",
                "nombre": "Storages",
                "texto": "\/*\r\n\r\n  \t \tStorages\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:7}\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\nSTORAGE {NAME:almacen1,X:377,Y:362,capacity:14\r\n\t,EQ_BIN_START:18,EQ_BIN_SIZE:1,EQ_BIN_COUNT:14 \r\n    ,RQ_BIN_START:0,RQ_BIN_SIZE:1,RQ_BIN_COUNT:18\r\n    }\r\nSTART 200\r\n\r\n;*****************************************************\r\nGENERATE 8,0,0,0 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 10 {TO:almacen1}\r\n\r\nENTER almacen1,(RANDOM*5)+1\r\nADVANCE 20,10\r\nLEAVE almacen1\r\n\r\nADVANCE 10 {TO:POS1}\r\nENDGENERATE 1\r\n",
                "descripcion": "<p data-start=\"231\" data-end=\"446\">Los <code data-start=\"235\" data-end=\"244\">STORAGE<\/code> son un tipo especial de recurso. A diferencia de las <code data-start=\"298\" data-end=\"308\">FACILITY<\/code>, que gestionan entidades de forma individual, un <code data-start=\"358\" data-end=\"367\">STORAGE<\/code> gestiona <strong data-start=\"377\" data-end=\"401\">cantidades variables<\/strong> que una entidad puede introducir o extraer.<\/p>\r\n<p data-start=\"448\" data-end=\"684\">Por ejemplo, una entidad puede representar un cami&oacute;n que deja 10 unidades en un dep&oacute;sito. Por tanto, el sistema no analiza cu&aacute;ntas entidades han pasado por el almac&eacute;n, sino <strong data-start=\"621\" data-end=\"641\">cu&aacute;ntas unidades<\/strong> se han almacenado y durante cu&aacute;nto tiempo.<\/p>\r\n<h4 data-start=\"686\" data-end=\"704\">&iquest;Qu&eacute; se mide?<\/h4>\r\n<p data-start=\"706\" data-end=\"743\">Se recogen dos tipos de estad&iacute;sticas:<\/p>\r\n<ul data-start=\"745\" data-end=\"934\">\r\n    <li data-start=\"745\" data-end=\"848\">\r\n    <p data-start=\"747\" data-end=\"848\"><strong data-start=\"747\" data-end=\"758\">EQ_BIN_<\/strong>*: Tiempo que cada unidad permanece en el almacenamiento (tiempo de ocupaci&oacute;n por unidad).<\/p>\r\n    <\/li>\r\n    <li data-start=\"849\" data-end=\"934\">\r\n    <p data-start=\"851\" data-end=\"934\"><strong data-start=\"851\" data-end=\"862\">RQ_BIN_<\/strong>*: Nivel de ocupaci&oacute;n del almacenamiento en cada momento (por cantidad).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"936\" data-end=\"1091\">Este comportamiento se activa igual que en otros recursos, pero usando los par&aacute;metros <code data-start=\"1022\" data-end=\"1032\">EQ_BIN_*<\/code> y <code data-start=\"1035\" data-end=\"1045\">RQ_BIN_*<\/code> para configurar los intervalos de las tablas.<\/p>\r\n<h4 data-start=\"1093\" data-end=\"1117\">Un ejemplo pr&aacute;ctico<\/h4>\r\n<p data-start=\"1119\" data-end=\"1418\">El siguiente c&oacute;digo muestra un almacenamiento (<code data-start=\"1166\" data-end=\"1175\">STORAGE<\/code>) con capacidad de 14 unidades. Las entidades entrantes ocupan una cantidad aleatoria (entre 1 y 5 unidades). La estad&iacute;stica resultante indicar&aacute; cu&aacute;nto tiempo estuvieron almacenadas esas unidades y c&oacute;mo vari&oacute; el nivel de ocupaci&oacute;n del almac&eacute;n:<\/p>\r\n<pre>\r\nSTORAGE {NAME:almacen1,X:377,Y:362,capacity:14,\r\n         EQ_BIN_START:18,EQ_BIN_SIZE:1,EQ_BIN_COUNT:14,\r\n         RQ_BIN_START:0, RQ_BIN_SIZE:1, RQ_BIN_COUNT:18}\r\n\r\nGENERATE 8,0,0,0\r\n    ADVANCE 10 {TO:almacen1}\r\n    ENTER almacen1,(RANDOM*5)+1\r\n    ADVANCE 20,10\r\n    LEAVE almacen1\r\n    ADVANCE 10 {TO:pos1}\r\nENDGENERATE 1<\/pre>\r\n<h4 data-start=\"1747\" data-end=\"1766\">&iquest;Qu&eacute; observar?<\/h4>\r\n<p data-start=\"1768\" data-end=\"1811\">En el informe, ver&aacute;s dos tablas diferentes:<\/p>\r\n<ul data-start=\"1813\" data-end=\"2088\">\r\n    <li data-start=\"1813\" data-end=\"1954\">\r\n    <p data-start=\"1815\" data-end=\"1954\">Una con los tramos de <strong data-start=\"1837\" data-end=\"1869\">tiempo por unidad almacenada<\/strong> (<code data-start=\"1871\" data-end=\"1875\">EQ<\/code>), que te dir&aacute; si las unidades permanecieron poco o mucho tiempo en el almac&eacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1955\" data-end=\"2088\">\r\n    <p data-start=\"1957\" data-end=\"2088\">Otra con los niveles de <strong data-start=\"1981\" data-end=\"2006\">ocupaci&oacute;n del almac&eacute;n<\/strong> (<code data-start=\"2008\" data-end=\"2012\">RQ<\/code>), donde podr&aacute;s ver cu&aacute;ntas unidades hab&iacute;a simult&aacute;neamente en cada instante.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2090\" data-end=\"2209\">Este tipo de an&aacute;lisis es esencial en modelos de log&iacute;stica, inventario, o cualquier sistema con almacenamiento variable.<\/p>\r\n<p data-start=\"1119\" data-end=\"1418\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "240",
                "nombre": "Resumen",
                "texto": "",
                "descripcion": "<p data-start=\"182\" data-end=\"366\">En GPSS-Plus, las estad&iacute;sticas se acumulan autom&aacute;ticamente si se definen las tablas con los par&aacute;metros adecuados. Existen varios tipos de estad&iacute;sticas, clasificadas por lo que se mide:<\/p>\r\n<h4 data-start=\"368\" data-end=\"412\">Estad&iacute;sticas por entidad (<code data-start=\"402\" data-end=\"411\">E_BIN_*<\/code>)<\/h4>\r\n<p data-start=\"413\" data-end=\"474\">Mide cu&aacute;nto tiempo ha permanecido cada entidad en un recurso.<\/p>\r\n<ul data-start=\"476\" data-end=\"629\">\r\n    <li data-start=\"476\" data-end=\"552\">\r\n    <p data-start=\"478\" data-end=\"552\"><strong data-start=\"478\" data-end=\"493\">Aplicable a<\/strong>: Todos los recursos (<code data-start=\"515\" data-end=\"525\">FACILITY<\/code>, <code data-start=\"527\" data-end=\"536\">STORAGE<\/code>, <code data-start=\"538\" data-end=\"545\">QUEUE<\/code>, etc.)<\/p>\r\n    <\/li>\r\n    <li data-start=\"553\" data-end=\"629\">\r\n    <p data-start=\"555\" data-end=\"629\"><strong data-start=\"555\" data-end=\"566\">Ejemplo<\/strong>: Una entidad pasa 40 unidades de tiempo &rarr; se suma 1 al bin 40.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h4 data-start=\"631\" data-end=\"675\">Estad&iacute;sticas por recurso (<code data-start=\"665\" data-end=\"674\">R_BIN_*<\/code>)<\/h4>\r\n<p data-start=\"676\" data-end=\"759\">Mide cu&aacute;nto tiempo un recurso ha tenido una cantidad concreta de entidades activas.<\/p>\r\n<ul data-start=\"761\" data-end=\"885\">\r\n    <li data-start=\"761\" data-end=\"798\">\r\n    <p data-start=\"763\" data-end=\"798\"><strong data-start=\"763\" data-end=\"778\">Aplicable a<\/strong>: Todos los recursos<\/p>\r\n    <\/li>\r\n    <li data-start=\"799\" data-end=\"885\">\r\n    <p data-start=\"801\" data-end=\"885\"><strong data-start=\"801\" data-end=\"812\">Ejemplo<\/strong>: El recurso tiene 3 entidades durante 10 unidades &rarr; se suma 10 al bin 3.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h4 data-start=\"887\" data-end=\"942\">Estad&iacute;sticas por unidad almacenada (<code data-start=\"931\" data-end=\"941\">EQ_BIN_*<\/code>)<\/h4>\r\n<p data-start=\"943\" data-end=\"1007\">Mide cu&aacute;nto tiempo permanece cada unidad dentro de un <code data-start=\"997\" data-end=\"1006\">STORAGE<\/code>.<\/p>\r\n<ul data-start=\"1009\" data-end=\"1126\">\r\n    <li data-start=\"1009\" data-end=\"1042\">\r\n    <p data-start=\"1011\" data-end=\"1042\"><strong data-start=\"1011\" data-end=\"1026\">Aplicable a<\/strong>: Solo <code data-start=\"1033\" data-end=\"1042\">STORAGE<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"1043\" data-end=\"1126\">\r\n    <p data-start=\"1045\" data-end=\"1126\"><strong data-start=\"1045\" data-end=\"1056\">Ejemplo<\/strong>: Un cami&oacute;n con 5 unidades permanece 40 tiempos &rarr; se suma 5 al bin 40.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h4 data-start=\"1128\" data-end=\"1192\">Estad&iacute;sticas por cantidad en almacenamiento (<code data-start=\"1181\" data-end=\"1191\">RQ_BIN_*<\/code>)<\/h4>\r\n<p data-start=\"1193\" data-end=\"1268\">Mide cu&aacute;nto tiempo un <code data-start=\"1215\" data-end=\"1224\">STORAGE<\/code> contiene cierta cantidad total de unidades.<\/p>\r\n<ul data-start=\"1270\" data-end=\"1371\">\r\n    <li data-start=\"1270\" data-end=\"1303\">\r\n    <p data-start=\"1272\" data-end=\"1303\"><strong data-start=\"1272\" data-end=\"1287\">Aplicable a<\/strong>: Solo <code data-start=\"1294\" data-end=\"1303\">STORAGE<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"1304\" data-end=\"1371\">\r\n    <p data-start=\"1306\" data-end=\"1371\"><strong data-start=\"1306\" data-end=\"1317\">Ejemplo<\/strong>: 3 unidades durante 10 tiempos &rarr; se suma 30 al bin 3.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p><b><\/b><\/p>\r\n<p><b>Colas (QUEUER)<\/b><\/p>\r\n<p data-start=\"1398\" data-end=\"1493\">Las colas tienen su propia tabla de estad&iacute;sticas si se declara un <code data-start=\"1464\" data-end=\"1472\">QUEUER<\/code>. Para recoger datos:<\/p>\r\n<pre>\r\nqueue Cola1\r\nseize Recurso1\r\ndepart Cola1\r\nADVANCE ...\r\nrelease Recurso1<\/pre>\r\n<p data-start=\"1575\" data-end=\"1590\">Se peude aplicar:<\/p>\r\n<ul data-start=\"1592\" data-end=\"1702\">\r\n    <li data-start=\"1592\" data-end=\"1642\">\r\n    <p data-start=\"1594\" data-end=\"1642\"><code data-start=\"1594\" data-end=\"1603\">E_BIN_*<\/code>: Tiempo que cada entidad pas&oacute; en cola.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1643\" data-end=\"1702\">\r\n    <p data-start=\"1645\" data-end=\"1702\"><code data-start=\"1645\" data-end=\"1654\">R_BIN_*<\/code>: Tiempo que la cola tuvo X entidades esperando.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"1709\" data-end=\"1756\">Personalizaci&oacute;n total: <code data-start=\"1736\" data-end=\"1743\">TABLE<\/code> + <code data-start=\"1746\" data-end=\"1756\">TABULATE<\/code><\/h3>\r\n<p data-start=\"1758\" data-end=\"1791\">Para registrar datos arbitrarios:<\/p>\r\n<pre>\r\nTABLE {name:TABLA1, E_BIN_START:0, E_BIN_SIZE:1, E_BIN_COUNT:100, EXPRESSION:(AC1$ - P$TIEMPOINICIO)}\r\nTABULATE TABLA1<\/pre>\r\n<p data-start=\"1758\" data-end=\"1791\">Esto permite medir cualquier cosa definida por una expresi&oacute;n matem&aacute;tica.<\/p>\r\n<p data-start=\"1758\" data-end=\"1791\">&nbsp;<\/p>\r\n<p data-start=\"1398\" data-end=\"1493\">&nbsp;<\/p>\r\n<p data-start=\"1398\" data-end=\"1493\">&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "218",
        "nombre": "Season 9: Funciones",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "224",
                "nombre": "Definiendo las funciones",
                "texto": "\/*\r\n\r\n  \t \tDefiniendo las funciones\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:16,E_BIN_SIZE:1,E_BIN_COUNT:40,capacity:3}\r\n\r\nFacility {NAME:VENTANILLA2,X:384,Y:186,E_BIN_start:16,E_BIN_SIZE:1,E_BIN_COUNT:40,capacity:3}\r\n\r\n\r\nPOSITION {NAME:POS1,X:278,Y:381}\r\nPOSITION {NAME:POS2,X:482,Y:380}\r\n\r\nFunction {name:funGauss, type:\"GAUSS\", a:1, b:30, sigma1:3.3, sigma2:3.3,intervals:100 }\r\n\r\nFunction {NAME:funValues,type:\"VALUES\",expression:\"RANDOM\",VALUES:\"\r\n0.0001,0.0000\/0.0002,0.0101\/0.0004,0.0202\/0.0006,0.0303\/0.0008,0.0404\/\r\n0.0011,0.0505\/0.0015,0.0606\/0.0020,0.0707\/0.0025,0.0808\/0.0032,0.0909\/\r\n0.0039,0.1010\/0.0049,0.1111\/0.0060,0.1212\/0.0073,0.1313\/0.0088,0.1414\/\r\n0.0106,0.1515\/0.0128,0.1616\/0.0152,0.1717\/0.0180,0.1818\/0.0213,0.1919\/\r\n0.0250,0.2020\/0.0293,0.2121\/0.0341,0.2222\/0.0396,0.2323\/0.0458,0.2424\/\r\n0.0527,0.2525\/0.0603,0.2626\/0.0689,0.2727\/0.0783,0.2828\/0.0887,0.2929\/\r\n0.1000,0.3030\/0.1124,0.3131\/0.1258,0.3232\/0.1403,0.3333\/0.1559,0.3434\/\r\n0.1726,0.3535\/0.1904,0.3636\/0.2093,0.3737\/0.2292,0.3838\/0.2501,0.3939\/\r\n0.2720,0.4040\/0.2948,0.4141\/0.3185,0.4242\/0.3429,0.4343\/0.3680,0.4444\/\r\n0.3937,0.4545\/0.4199,0.4646\/0.4464,0.4747\/0.4731,0.4848\/0.5000,0.4949\/\r\n0.5269,0.5051\/0.5536,0.5152\/0.5801,0.5253\/0.6063,0.5354\/0.6320,0.5455\/\r\n0.6571,0.5556\/0.6815,0.5657\/0.7052,0.5758\/0.7280,0.5859\/0.7499,0.5960\/\r\n0.7708,0.6061\/0.7907,0.6162\/0.8096,0.6263\/0.8274,0.6364\/0.8441,0.6465\/\r\n0.8597,0.6566\/0.8742,0.6667\/0.8876,0.6768\/0.9000,0.6869\/0.9113,0.6970\/\r\n0.9217,0.7071\/0.9311,0.7172\/0.9397,0.7273\/0.9473,0.7374\/0.9542,0.7475\/\r\n0.9604,0.7576\/0.9659,0.7677\/0.9707,0.7778\/0.9750,0.7879\/0.9787,0.7980\/\r\n0.9820,0.8081\/0.9848,0.8182\/0.9872,0.8283\/0.9894,0.8384\/0.9912,0.8485\/\r\n0.9927,0.8586\/0.9940,0.8687\/0.9951,0.8788\/0.9961,0.8889\/0.9968,0.8990\/\r\n0.9975,0.9091\/0.9980,0.9192\/0.9985,0.9293\/0.9989,0.9394\/0.9992,0.9495\/\r\n0.9994,0.9596\/0.9996,0.9697\/0.9998,0.9798\/0.9999,0.9899\/1.0000,1.0000\/\r\n\"}\r\n\r\nSTART 500\r\n\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,0 {NAME:GEN1,X:91,Y:383}\r\n\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE 20  + (FN$funValues * 20) ; definición clásica\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\n\r\nSEIZE VENTANILLA2\r\nADVANCE FN$funGauss ; definición por parámetros\r\n;ADVANCE 2 ; definición por parámetros\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\nENDGENERATE 1\r\n",
                "descripcion": "<h3>&iquest;Qu&eacute; es una funci&oacute;n?<\/h3>\r\n<p data-start=\"487\" data-end=\"732\">Una <strong data-start=\"505\" data-end=\"516\">funci&oacute;n<\/strong> es un componente que produce un <strong data-start=\"549\" data-end=\"567\">valor num&eacute;rico<\/strong> a partir de una entrada. Ese valor puede representar <strong data-start=\"621\" data-end=\"632\">tiempos<\/strong>, <strong data-start=\"634\" data-end=\"648\">cantidades<\/strong>, <strong data-start=\"650\" data-end=\"660\">costes<\/strong>, o cualquier otro par&aacute;metro variable que queramos incorporar al modelo.<\/p>\r\n<p data-start=\"487\" data-end=\"732\">En t&eacute;rminos matem&aacute;ticos, podr&iacute;amos escribir algo como:<\/p>\r\n<pre>\r\nf(x) = 10 + (x * 5)<\/pre>\r\n<p data-start=\"487\" data-end=\"732\">Si <code data-start=\"625\" data-end=\"628\">x<\/code> toma valores entre 0 y 1, entonces <code data-start=\"664\" data-end=\"670\">f(x)<\/code> tomar&aacute; valores entre 10 y 15.<\/p>\r\n<p data-start=\"734\" data-end=\"746\">En GPSS-Plus, cuando usamos:<\/p>\r\n<pre>\r\nADVANCE 10,5<\/pre>\r\n<p data-start=\"245\" data-end=\"533\">estamos usando esa funci&oacute;n impl&iacute;cita, se genera un valor aleatorio entre 0 y 1, lo multiplica y resuelve un valor entre 10 y 15.&nbsp;<\/p>\r\n<p data-start=\"245\" data-end=\"533\">En t&eacute;rminos estad&iacute;sticos, eso es una <strong data-start=\"1021\" data-end=\"1046\">distribuci&oacute;n uniforme<\/strong> entre 10 y 15, es decir: <strong data-start=\"1072\" data-end=\"1139\">todos los valores dentro del rango tienen la misma probabilidad<\/strong> de ocurrir.<\/p>\r\n<h3>Uso de las funciones:<\/h3>\r\n<p data-start=\"1194\" data-end=\"1248\">En muchos casos <strong data-start=\"1217\" data-end=\"1247\">la realidad no es uniforme<\/strong>.<\/p>\r\n<p data-start=\"1250\" data-end=\"1453\">Si medimos cu&aacute;nto tardan 1000 veh&iacute;culos en recorrer 100 km. La mayor&iacute;a tardar&aacute; cerca de 1 hora. Algunos tardar&aacute;n algo menos, otros algo m&aacute;s. Pero casi ninguno lo har&aacute; en 30 minutos o en 2 horas.<\/p>\r\n<p data-start=\"1455\" data-end=\"1565\">Eso <strong data-start=\"1459\" data-end=\"1465\">no<\/strong> es una distribuci&oacute;n uniforme. Es lo que se conoce como una <strong data-start=\"1525\" data-end=\"1548\">distribuci&oacute;n normal<\/strong> o <strong data-start=\"1551\" data-end=\"1564\">gaussiana<\/strong>.<\/p>\r\n<p data-start=\"826\" data-end=\"964\">Por eso, GPSS-Plus permite definir funciones que modelen con mayor precisi&oacute;n comportamientos como estos. Existen dos m&eacute;todos para hacerlo:<\/p>\r\n<h3 data-start=\"971\" data-end=\"1020\"><strong data-start=\"975\" data-end=\"1020\">1. FUNCTION tipo <code data-start=\"1010\" data-end=\"1018\">VALUES<\/code><\/strong><\/h3>\r\n<p data-start=\"1022\" data-end=\"1121\">Con este m&eacute;todo, definimos manualmente una funci&oacute;n basada en una tabla acumulada de probabilidades.<\/p>\r\n<pre>\r\nFunction {\r\n  NAME: funValues,\r\n&nbsp; TYPE: &quot;VALUES&quot;,\r\n&nbsp; EXPRESSION: &quot;RANDOM&quot;,\r\n&nbsp; VALUES: &quot;0.0001,0.0000\/0.0002,0.0101\/...\/1.0000,1.0000\/&quot;\r\n}<\/pre>\r\n<p data-start=\"826\" data-end=\"964\">Esta tabla asocia claves (probabilidades acumuladas entre 0 y 1 <code>EXPRESSION: &quot;RANDOM&quot;<\/code>) con valores resultantes. Si el valor aleatorio generado es 0.33, la funci&oacute;n busca el intervalo que le corresponde (por ejemplo, entre 0.0293 y 0.2121) y devuelve el valor asociado que&nbsp;representa una distribuci&oacute;n de tipo campana (gaussiana), donde los valores extremos tienen menos probabilidad que los valores centrales.<\/p>\r\n<p data-start=\"826\" data-end=\"964\">Entonces, una instrucci&oacute;n como:<\/p>\r\n<pre>\r\nADVANCE 20 + (FN$funValues * 20)<\/pre>\r\n<p data-start=\"826\" data-end=\"964\">Generar&aacute; valores entre 20 y 40, concentrados en torno al 30.<\/p>\r\n<h3 data-start=\"1833\" data-end=\"1881\"><strong data-start=\"1837\" data-end=\"1881\">2. FUNCTION tipo <code data-start=\"1872\" data-end=\"1879\">GAUSS<\/code><\/strong><\/h3>\r\n<p data-start=\"1883\" data-end=\"1991\">Este m&eacute;todo permite definir directamente una distribuci&oacute;n gaussiana mediante sus par&aacute;metros caracter&iacute;sticos:<\/p>\r\n<pre>\r\nFunction {\r\n  NAME: funGauss,\r\n&nbsp; TYPE: &quot;GAUSS&quot;,\r\n&nbsp; A: 1,\r\n&nbsp; B: 30,\r\n&nbsp; SIGMA1: 3.3,\r\n&nbsp; SIGMA2: 3.3,\r\n&nbsp; INTERVALS: 100\r\n}\r\n<\/pre>\r\n<ul>\r\n    <li><code data-start=\"2125\" data-end=\"2128\">A<\/code>: altura de la curva (no afecta al resultado, es visual)<\/li>\r\n    <li><code data-start=\"2187\" data-end=\"2190\">B<\/code>: valor central (media)<\/li>\r\n    <li><code data-start=\"2216\" data-end=\"2224\">SIGMA1<\/code>, <code data-start=\"2226\" data-end=\"2234\">SIGMA2<\/code>: desviaciones est&aacute;ndar izquierda y derecha<\/li>\r\n    <li><code data-start=\"2280\" data-end=\"2291\">INTERVALS<\/code>: cantidad de divisiones para la funci&oacute;n<\/li>\r\n<\/ul>\r\n<p>Con esta funci&oacute;n, simplemente usamos:<\/p>\r\n<pre>\r\nADVANCE FN$funGauss\r\n<\/pre>\r\n<p>Ambas t&eacute;cnicas producir&aacute;n una distribuci&oacute;n de tiempos en torno al valor 30, pero con m&eacute;todos diferentes: una tabla predefinida frente a una curva construida autom&aacute;ticamente.<\/p>\r\n<h3 data-start=\"2590\" data-end=\"2624\"><strong data-start=\"2594\" data-end=\"2624\">Visualizaci&oacute;n y validaci&oacute;n<\/strong><\/h3>\r\n<p data-start=\"2626\" data-end=\"2661\">Las <code data-start=\"2630\" data-end=\"2640\">FACILITY<\/code> con par&aacute;metros como:<\/p>\r\n<pre>\r\nE_BIN_START:16, E_BIN_SIZE:1, E_BIN_COUNT:40<\/pre>\r\n<p data-start=\"2726\" data-end=\"2930\">registran estad&iacute;sticas autom&aacute;ticas sobre cu&aacute;nto tiempo ha permanecido cada entidad en el recurso. Estas estad&iacute;sticas se visualizan en el informe y permiten comprobar c&oacute;mo se comporta realmente la funci&oacute;n.<\/p>\r\n<p data-start=\"2932\" data-end=\"3123\">Veremos que, en ambos casos, la gr&aacute;fica resultante es una <strong data-start=\"2988\" data-end=\"3014\">distribuci&oacute;n gaussiana<\/strong>. Esto permite validar que tanto la tabla manual como la funci&oacute;n generada ofrecen comportamientos coherentes.<\/p>\r\n<p data-start=\"2932\" data-end=\"3123\">&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/informe.png\" width=\"50%'\" alt=\"Imagen informe\"><\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "225",
                "nombre": "Tipos de funciones de distribución",
                "texto": "Function {name:funGauss, type:\"GAUSS\", a:1, b:30, sigma1:3.3, sigma2:3.3,intervals:100 }\r\n\r\nFunction {name:funExp, type:\"EXP\", b:0, lambda:0.15, intervals:100}\r\n\r\nFunction {name:funUni, type:\"UNIFORM\", min:20, max:40, intervals:100}\r\n\r\nFunction {name:funTri, type:\"TRIANGULAR\", min:20, max:40, mode:30, intervals:100}\r\n\r\nFunction {name:funLog, type:\"LOGNORMAL\", mu:3.4, sigma:0.3, intervals:100}\r\nFunction {name:funLog2, type:\"LOGNORMAL\", b:30, spread:5, intervals:100}\r\n\r\nFunction {name:funCus, type:\"FDISTRIBUTION\", function:\"A * Math.sin(B * x)\", a:10, b:0.5, min:0, max:10, intervals:100}\r\n\r\n\r\nFunction {name:funPoi, type:\"POISSON\", lambda:3.5}\r\n\r\nFunction {Name:math1, type: math, EXPRESSION:\"A * 2 + B\"}\r\n",
                "descripcion": "<p data-start=\"237\" data-end=\"606\">El comando <code data-start=\"248\" data-end=\"258\">FUNCTION<\/code> permite definir funciones que devuelven valores num&eacute;ricos, ya sea mediante c&aacute;lculos directos o a trav&eacute;s de tablas de distribuci&oacute;n precalculadas. Estas funciones se utilizan para simular comportamientos variables y realistas en operaciones como <code data-start=\"503\" data-end=\"512\">ADVANCE<\/code>, <code data-start=\"514\" data-end=\"522\">ASSIGN<\/code>, <code data-start=\"524\" data-end=\"534\">TABULATE<\/code>, etc., y representan tiempos, cantidades, costes, probabilidades y m&aacute;s.<\/p>\r\n<p data-start=\"608\" data-end=\"678\">En GPSS-Plus, las funciones pueden clasificarse en dos grandes grupos:<\/p>\r\n<p data-start=\"246\" data-end=\"540\">&nbsp;<\/p>\r\n<h4 data-start=\"680\" data-end=\"765\">1. <strong data-start=\"688\" data-end=\"763\">Funciones basadas en distribuciones estad&iacute;sticas (con tabla de valores)<\/strong><\/h4>\r\n<p data-start=\"766\" data-end=\"1015\">Estas funciones generan previamente una tabla interna con un n&uacute;mero configurable de puntos (intervalos), sobre la que interpolan el resultado. Son ideales para representar comportamientos aleatorios con patrones conocidos. Los tipos disponibles son:<\/p>\r\n<ul data-start=\"1017\" data-end=\"1390\">\r\n    <li data-start=\"1017\" data-end=\"1067\">\r\n    <p data-start=\"1019\" data-end=\"1067\"><code data-start=\"1019\" data-end=\"1026\">GAUSS<\/code>: Distribuci&oacute;n normal (campana de Gauss).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1068\" data-end=\"1114\">\r\n    <p data-start=\"1070\" data-end=\"1114\"><code data-start=\"1070\" data-end=\"1075\">EXP<\/code>: Distribuci&oacute;n exponencial decreciente.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1115\" data-end=\"1164\">\r\n    <p data-start=\"1117\" data-end=\"1164\"><code data-start=\"1117\" data-end=\"1126\">UNIFORM<\/code>: Distribuci&oacute;n uniforme (rectangular).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1165\" data-end=\"1205\">\r\n    <p data-start=\"1167\" data-end=\"1205\"><code data-start=\"1167\" data-end=\"1179\">TRIANGULAR<\/code>: Distribuci&oacute;n triangular.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1206\" data-end=\"1274\">\r\n    <p data-start=\"1208\" data-end=\"1274\"><code data-start=\"1208\" data-end=\"1219\">LOGNORMAL<\/code>: Distribuci&oacute;n log-normal, asim&eacute;trica hacia la derecha.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1275\" data-end=\"1390\">\r\n    <p data-start=\"1277\" data-end=\"1390\"><code data-start=\"1277\" data-end=\"1292\">FDISTRIBUTION<\/code>: Permite definir una funci&oacute;n de distribuci&oacute;n personalizada mediante una f&oacute;rmula matem&aacute;tica.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<h4 data-start=\"1392\" data-end=\"1446\">2. <strong data-start=\"1400\" data-end=\"1444\">Funciones de c&aacute;lculo directo (sin tabla)<\/strong><\/h4>\r\n<p data-start=\"1447\" data-end=\"1645\">Estas funciones eval&uacute;an el resultado directamente, sin generar ni consultar una tabla. Son m&aacute;s flexibles cuando se desea trabajar con f&oacute;rmulas o con modelos de conteo de eventos discretos. Incluyen:<\/p>\r\n<ul data-start=\"1647\" data-end=\"1906\">\r\n    <li data-start=\"1647\" data-end=\"1761\">\r\n    <p data-start=\"1649\" data-end=\"1761\"><code data-start=\"1649\" data-end=\"1658\">POISSON<\/code>: Genera un valor entero aleatorio seg&uacute;n una distribuci&oacute;n de Poisson (n&uacute;mero de eventos por intervalo).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1762\" data-end=\"1906\">\r\n    <p data-start=\"1764\" data-end=\"1906\"><code data-start=\"1764\" data-end=\"1770\">MATH<\/code>: Eval&uacute;a directamente una expresi&oacute;n matem&aacute;tica. Admite m&uacute;ltiples par&aacute;metros que se sustituyen por letras (<code data-start=\"1876\" data-end=\"1879\">A<\/code>, <code data-start=\"1881\" data-end=\"1884\">B<\/code>, <code data-start=\"1886\" data-end=\"1889\">C<\/code>, etc.) en orden.<\/p>\r\n    <\/li>\r\n<\/ul>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "227",
                "nombre": "Función Gauss",
                "texto": "\/*\r\n\r\n  \t \tGauss\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\n\r\nPOSITION {NAME:POS1,X:250,Y:381}\r\nPOSITION {NAME:POS2,X:423,Y:383}\r\nPOSITION {NAME:POS3,X:566,Y:384}\r\n\r\nFunction {name:gfun1, TYPE:GAUSS, a:1, b:50, sigma1:3.3, sigma2:3.3,intervals:100 }\r\nFunction {name:gfun2, TYPE:GAUSS, a:1, b:50, sigma1:1.0, sigma2:10.0,intervals:100 }\r\nFunction {name:gfun3, TYPE:GAUSS, a:1, b:50, sigma1:10.0, sigma2:1.0,intervals:100 }\r\n\r\n\r\nSTART 500\r\n\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,0 {NAME:GEN1,X:91,Y:383}\r\n\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE FN$gfun1\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\n\r\nSEIZE VENTANILLA2\r\nADVANCE FN$gfun2\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\nADVANCE 20,0 {TO:VENTANILLA3}\r\n\r\nSEIZE VENTANILLA3\r\nADVANCE FN$gfun3\r\nRELEASE VENTANILLA3\r\nADVANCE 20,0 {TO:POS3}\r\n\r\n\r\nENDGENERATE 1",
                "descripcion": "<p>La distribuci&oacute;n <strong data-start=\"213\" data-end=\"226\">gaussiana<\/strong> (o normal) representa una <strong data-start=\"253\" data-end=\"282\">curva en forma de campana<\/strong>, donde los valores m&aacute;s probables est&aacute;n cerca de un valor central y los extremos tienen menor probabilidad. Es ideal para modelar fen&oacute;menos naturales como <strong data-start=\"437\" data-end=\"504\">tiempos de atenci&oacute;n, errores de medici&oacute;n o duraci&oacute;n de procesos<\/strong>, donde la mayor&iacute;a de los casos se agrupan alrededor de una media.<\/p>\r\n<p><b>Par&aacute;metros:<\/b><\/p>\r\n<ul>\r\n    <li>a: Altura de la curva (solo para visualizaci&oacute;n, no afecta al c&aacute;lculo).<\/li>\r\n    <li>b: Valor central o media esperada del proceso.<\/li>\r\n    <li>sigma1: Dispersi&oacute;n (desviaci&oacute;n est&aacute;ndar) hacia la izquierda del centro.<\/li>\r\n    <li>sigma2: Dispersi&oacute;n hacia la derecha del centro. Puede ser distinta a sigma1 para generar asimetr&iacute;a.<\/li>\r\n    <li>intervals: N&uacute;mero de tramos en que se divide la curva. A mayor n&uacute;mero, m&aacute;s precisa.<\/li>\r\n<\/ul>\r\n<p>Por construcci&oacute;n, la distribuci&oacute;n tomar&aacute; valores aproximadamente entre <code data-start=\"358\" data-end=\"374\">b - sigma1 * 3<\/code> y <code data-start=\"377\" data-end=\"393\">b + sigma2 * 3<\/code>.<\/p>\r\n<p>Este ejemplo muestra c&oacute;mo tres ventanillas con funciones gaussianas diferentes producen distintas distribuciones de tiempos de servicio:<\/p>\r\n<pre>\r\nADVANCE FN$gfun1 ; sim&eacute;trica\r\nADVANCE FN$gfun2 ; dispersi&oacute;n derecha\r\nADVANCE FN$gfun3 ; dispersi&oacute;n izquierda<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/gfunction.svg\" width=\"50%'\" alt=\"\"><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "228",
                "nombre": "Función Exponencial",
                "texto": "\/*\r\n\r\n  \t \tExponential\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS, Speed:5}\r\n\r\n;=== Facilities con seguimiento estadístico ===\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\n;=== Posiciones finales para visualización ===\r\nPOSITION {NAME:POS1, X:250, Y:381}\r\nPOSITION {NAME:POS2, X:423, Y:383}\r\nPOSITION {NAME:POS3, X:566, Y:384}\r\n\r\n;=== EFunctions con distintas tasas de caída ===\r\nFunction {Name:efun1, TYPE:EXP, a:100, b:20, Lambda:0.3, Intervals:70}\r\nFunction {Name:efun2, TYPE:EXP, a:100, b:20, Lambda:0.6, Intervals:70}\r\nFunction {Name:efun3, TYPE:EXP, a:100, b:20, Lambda:1.0, Intervals:70}\r\n\r\n;=== Inicio de simulación ===\r\nSTART 500\r\n\r\n;=== Flujo de entidades por las tres ventanillas ===\r\nGENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}\r\n\r\n; VENTANILLA 1 – Caída lenta\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE FN$efun1\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\n; VENTANILLA 2 – Caída media\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE FN$efun2\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\n; VENTANILLA 3 – Caída rápida\r\nADVANCE 20,0 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE FN$efun3\r\nRELEASE VENTANILLA3\r\nADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE 1\r\n",
                "descripcion": "<p data-start=\"224\" data-end=\"557\">La distribuci&oacute;n exponencial se utiliza para modelar eventos que ocurren de manera aleatoria pero con una <strong data-start=\"329\" data-end=\"370\">probabilidad decreciente en el tiempo<\/strong>. Es ideal para situaciones como tiempos de espera, fallos de sistemas o llegadas al azar donde lo m&aacute;s probable es que algo ocurra al principio, y sea menos probable cuanto m&aacute;s se demore.<\/p>\r\n<p data-start=\"559\" data-end=\"580\">Un ejemplo cl&aacute;sico:&nbsp;&quot;<i>Si est&aacute;s esperando a ser atendido, es m&aacute;s probable que ocurra pronto; y cada minuto adicional reduce esa probabilidad.<\/i>&quot;<\/p>\r\n<p>Esto <strong data-start=\"710\" data-end=\"735\">no lo representa bien<\/strong> una distribuci&oacute;n uniforme (como <code data-start=\"768\" data-end=\"782\">ADVANCE 10,5<\/code>), pero s&iacute; una funci&oacute;n exponencial, definida con el tipo:<\/p>\r\n<pre>\r\nFunction {name:efun1, type:&quot;EXP&quot;, a:100, b:20, lambda:0.3, intervals:100}<\/pre>\r\n<h3 data-start=\"923\" data-end=\"937\">Par&aacute;metros<\/h3>\r\n<ul data-start=\"939\" data-end=\"1264\">\r\n    <li data-start=\"939\" data-end=\"1015\">\r\n    <p data-start=\"941\" data-end=\"1015\"><strong data-start=\"941\" data-end=\"946\">a<\/strong>: Altura de la curva (solo para visualizaci&oacute;n, no afecta al c&aacute;lculo).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1016\" data-end=\"1095\">\r\n    <p data-start=\"1018\" data-end=\"1095\"><strong data-start=\"1018\" data-end=\"1023\">b<\/strong>: Valor base m&iacute;nimo. Es el punto desde el cual comienza la distribuci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1096\" data-end=\"1172\">\r\n    <p data-start=\"1098\" data-end=\"1172\"><strong data-start=\"1098\" data-end=\"1108\">lambda<\/strong>: Tasa de decrecimiento. A mayor lambda, la ca&iacute;da es m&aacute;s r&aacute;pida.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1173\" data-end=\"1264\">\r\n    <p data-start=\"1175\" data-end=\"1264\"><strong data-start=\"1175\" data-end=\"1188\">intervals<\/strong>: N&uacute;mero de tramos en los que se divide la curva (cuantos m&aacute;s, m&aacute;s precisa).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1268\" data-end=\"1334\">Aproximadamente, el 99.9% de los valores estar&aacute;n dentro del rango:<\/p>\r\n<ul data-start=\"1339\" data-end=\"1427\">\r\n    <li data-start=\"1339\" data-end=\"1366\">\r\n    <p data-start=\"1341\" data-end=\"1366\">&lambda; = 1 &rarr; de b a b + 6.91<\/p>\r\n    <\/li>\r\n    <li data-start=\"1369\" data-end=\"1399\">\r\n    <p data-start=\"1371\" data-end=\"1399\">&lambda; = 0.5 &rarr; de b a b + 13.82<\/p>\r\n    <\/li>\r\n    <li data-start=\"1402\" data-end=\"1427\">\r\n    <p data-start=\"1404\" data-end=\"1427\">&lambda; = 2 &rarr; de b a b + 3.45<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>Ejemplo de uso comparativo:<\/b><\/p>\r\n<pre>\r\nFunction {Name:efun1, type:&quot;EXP&quot;, a:100, b:20, lambda:0.3, intervals:70}\r\nFunction {Name:efun2, type:&quot;EXP&quot;, a:100, b:20, lambda:0.6, intervals:70}\r\nFunction {Name:efun3, type:&quot;EXP&quot;, a:100, b:20, lambda:1.0, intervals:70}\r\n\r\nADVANCE FN$efun1 ; ca&iacute;da lenta\r\nADVANCE FN$efun2 ; ca&iacute;da media\r\nADVANCE FN$efun3 ; ca&iacute;da r&aacute;pida<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/efunction.svg\" width=\"50%'\" alt=\"Graph\"><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "231",
                "nombre": "Función Log-Normal",
                "texto": "\/*\r\n\r\n  \t \tLog-normal\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS, Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:100,capacity:10}\r\n\r\nPOSITION {NAME:POS1, X:250, Y:381}\r\nPOSITION {NAME:POS2, X:423, Y:383}\r\nPOSITION {NAME:POS3, X:566, Y:384}\r\n\r\nFunction {Name:lfunc1, Type: LOGNORMAL, b:30, sigma:0.1}\r\nFunction {Name:lfunc2, Type: LOGNORMAL, b:20, sigma:0.15}\r\nFunction {Name:lfunc3, Type: LOGNORMAL, b:40, sigma:0.2}\r\n\r\n\r\nSTART 500\r\n\r\nGENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}\r\n\r\n; VENTANILLA 1 – Dispersión pequeña\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE FN$lfunc1\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\n; VENTANILLA 2 – Dispersión media\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE FN$lfunc2\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\n; VENTANILLA 3 – Dispersión grande (cola larga)\r\nADVANCE 20,0 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE FN$lfunc3\r\nRELEASE VENTANILLA3\r\nADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE 1\r\n\r\n",
                "descripcion": "<p>&iquest;Qu&eacute; es la distribuci&oacute;n log-normal?<\/p>\r\n<p>Una variable sigue una <strong data-start=\"338\" data-end=\"365\">distribuci&oacute;n log-normal<\/strong> si el logaritmo de esa variable tiene una <strong data-start=\"408\" data-end=\"431\">distribuci&oacute;n normal<\/strong>. En otras palabras:<\/p>\r\n<p><i>&ldquo;Muchos eventos peque&ntilde;os ocurren frecuentemente, pero tambi&eacute;n es posible que aparezcan algunos eventos con valores muy grandes, aunque con baja probabilidad.&rdquo;<\/i><\/p>\r\n<h3 data-start=\"620\" data-end=\"641\">Casos t&iacute;picos:<\/h3>\r\n<ul data-start=\"642\" data-end=\"788\">\r\n    <li data-start=\"642\" data-end=\"680\">\r\n    <p data-start=\"644\" data-end=\"680\">Tiempo de reparaci&oacute;n de una m&aacute;quina.<\/p>\r\n    <\/li>\r\n    <li data-start=\"681\" data-end=\"718\">\r\n    <p data-start=\"683\" data-end=\"718\">Duraci&oacute;n de una llamada telef&oacute;nica.<\/p>\r\n    <\/li>\r\n    <li data-start=\"719\" data-end=\"788\">\r\n    <p data-start=\"721\" data-end=\"788\">Ingresos econ&oacute;micos (la mayor&iacute;a gana poco, unos pocos ganan mucho).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<pre>\r\nFunction {Name:lfunc1, Type:LOGNORMAL, b:30, sigma:0.1}<\/pre>\r\n<p><b>Par&aacute;metros:<\/b><\/p>\r\n<ul>\r\n    <li><code data-start=\"898\" data-end=\"901\">b<\/code>: Valor <strong data-start=\"909\" data-end=\"925\">m&aacute;s probable<\/strong> de la funci&oacute;n (el pico de la curva).<\/li>\r\n    <li><code data-start=\"965\" data-end=\"972\">sigma<\/code>: Controla la <strong data-start=\"986\" data-end=\"1000\">dispersi&oacute;n<\/strong> de los valores hacia la derecha. A mayor sigma, m&aacute;s amplia es la &ldquo;cola&rdquo; de la distribuci&oacute;n.<\/li>\r\n<\/ul>\r\n<p>GPSS-Plus convierte autom&aacute;ticamente estos valores a los par&aacute;metros matem&aacute;ticos <code data-start=\"1175\" data-end=\"1178\">&mu;<\/code> (media logar&iacute;tmica) y <code data-start=\"1201\" data-end=\"1204\">&sigma;<\/code> (desviaci&oacute;n est&aacute;ndar) para construir la curva log-normal.<\/p>\r\n<p>A diferencia de la gaussiana, la log-normal <strong data-start=\"1333\" data-end=\"1352\">no es sim&eacute;trica<\/strong>. Tiene una &ldquo;cola&rdquo; larga hacia valores m&aacute;s altos. Este comportamiento refleja bien los fen&oacute;menos donde los valores extremos son raros, pero posibles.<\/p>\r\n<p>Este ejemplo muestra tres funciones log-normales con el mismo tipo de curva, pero distinta dispersi&oacute;n.<\/p>\r\n<pre>\r\nFunction {Name:lfunc1, Type:LOGNORMAL, b:30, sigma:0.1}   ; curva estrecha\r\nFunction {Name:lfunc2, Type:LOGNORMAL, b:20, sigma:0.15}  ; dispersi&oacute;n media\r\nFunction {Name:lfunc3, Type:LOGNORMAL, b:40, sigma:0.2}   ; cola larga<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/lfunction.svg\" width=\"50%'\" alt=\"Graph\"><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "229",
                "nombre": "Función Uniforme",
                "texto": "\/*\r\n\r\n  \t \tUniforme\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS, Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nPOSITION {NAME:POS1, X:250, Y:381}\r\nPOSITION {NAME:POS2, X:423, Y:383}\r\nPOSITION {NAME:POS3, X:566, Y:384}\r\n\r\nFunction {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}\r\nFunction {Name:ufun2, TYPE:UNIFORM, Min:20, Max:60}\r\nFunction {Name:ufun3, TYPE:UNIFORM, Min:30, Max:50}\r\n\r\nSTART 500\r\n\r\nGENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}\r\n\r\n; VENTANILLA 1 – Uniforme de 20 a 40\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE FN$ufun1\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\n; VENTANILLA 2 – Uniforme de 20 a 60\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE FN$ufun2\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\n; VENTANILLA 3 – Uniforme de 30 a 50\r\nADVANCE 20,0 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE FN$ufun3\r\nRELEASE VENTANILLA3\r\nADVANCE 20,0 {TO:POS3}\r\n\r\nTERMINATE 1\r\n\r\n\r\n",
                "descripcion": "<p>La <strong data-start=\"229\" data-end=\"254\">distribuci&oacute;n uniforme<\/strong> representa la m&aacute;xima incertidumbre: <strong data-start=\"291\" data-end=\"372\">todos los valores dentro de un rango tienen exactamente la misma probabilidad<\/strong>.<\/p>\r\n<p><i>&quot;Es como lanzar un dado perfecto: ning&uacute;n n&uacute;mero tiene m&aacute;s posibilidades que otro.&quot;<\/i><\/p>\r\n<p>Este tipo de distribuci&oacute;n es &uacute;til cuando <strong data-start=\"528\" data-end=\"604\">no hay razones para pensar que unos valores sean m&aacute;s probables que otros<\/strong>.<img src=\"..\/imagenes\/ufunction.svg\" width=\"50%'\" alt=\"\"><\/p>\r\n<p>Par&aacute;metros<\/p>\r\n<pre>\r\nFunction {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}<\/pre>\r\n<ul data-start=\"865\" data-end=\"964\">\r\n    <li data-start=\"865\" data-end=\"914\">\r\n    <p data-start=\"867\" data-end=\"914\"><code data-start=\"867\" data-end=\"872\">Min<\/code>: valor m&iacute;nimo que puede tomar la funci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"915\" data-end=\"964\">\r\n    <p data-start=\"917\" data-end=\"964\"><code data-start=\"917\" data-end=\"922\">Max<\/code>: valor m&aacute;ximo que puede tomar la funci&oacute;n.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"966\" data-end=\"1029\">Todos los valores entre Min y Max tienen la misma probabilidad.<\/p>\r\n<p>En el gr&aacute;fico del informe, la curva tiene forma de <strong data-start=\"1108\" data-end=\"1128\">rect&aacute;ngulo plano<\/strong>. Cuanto <strong data-start=\"1137\" data-end=\"1159\">mayor sea el rango<\/strong>, m&aacute;s dispersa ser&aacute; la distribuci&oacute;n.<\/p>\r\n<p>Este ejemplo compara tres distribuciones uniformes con distintos rangos. Todas tienen forma rectangular, pero difieren en amplitud y posici&oacute;n:<\/p>\r\n<pre>\r\nFunction {Name:ufun1, TYPE:UNIFORM, Min:20, Max:40}   ; Rango peque&ntilde;o\r\nFunction {Name:ufun2, TYPE:UNIFORM, Min:20, Max:60}   ; Rango amplio\r\nFunction {Name:ufun3, TYPE:UNIFORM, Min:30, Max:50}   ; Rango desplazado<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/ufunction.svg\" width=\"50%'\" alt=\"Graph\"><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "230",
                "nombre": "Función Triangular",
                "texto": "\/*\r\n\r\n  \t \tTriangular\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS, Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:165,Y:184,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA2,X:343,Y:185,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nFacility {NAME:VENTANILLA3,X:535,Y:183,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:10}\r\n\r\nPOSITION {NAME:POS1, X:250, Y:381}\r\nPOSITION {NAME:POS2, X:423, Y:383}\r\nPOSITION {NAME:POS3, X:566, Y:384}\r\n\r\nFunction {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30}\r\nFunction {Name:tfun2, TYPE:TRIANGULAR, Min:20, Max:40, Mode:25}\r\nFunction {Name:tfun3, TYPE:TRIANGULAR, Min:20, Max:40, Mode:35}\r\n\r\nSTART 500\r\n\r\nGENERATE 10,0,0,0 {NAME:GEN1, X:91, Y:383}\r\n\r\n; VENTANILLA 1 – Triángulo centrado\r\nADVANCE 20,4 {TO:VENTANILLA1}\r\nSEIZE VENTANILLA1\r\nADVANCE FN$tfun1\r\nRELEASE VENTANILLA1\r\nADVANCE 20,0 {TO:POS1}\r\n\r\n; VENTANILLA 2 – Triángulo con pico a la izquierda\r\nADVANCE 20,0 {TO:VENTANILLA2}\r\nSEIZE VENTANILLA2\r\nADVANCE FN$tfun2\r\nRELEASE VENTANILLA2\r\nADVANCE 20,0 {TO:POS2}\r\n\r\n; VENTANILLA 3 – Triángulo con pico a la derecha\r\nADVANCE 20,0 {TO:VENTANILLA3}\r\nSEIZE VENTANILLA3\r\nADVANCE FN$tfun3\r\nRELEASE VENTANILLA3\r\nADVANCE 20,0 {TO:POS3}\r\n\r\nENDGENERATE 1\r\n\r\n\r\n\r\n",
                "descripcion": "<p>La <strong data-start=\"281\" data-end=\"308\">distribuci&oacute;n triangular<\/strong> permite definir una estimaci&oacute;n <strong data-start=\"340\" data-end=\"373\">m&iacute;nima, m&aacute;xima y m&aacute;s probable<\/strong> (modo) de un valor. Tiene forma de tri&aacute;ngulo, donde el valor del <strong data-start=\"439\" data-end=\"447\">modo<\/strong> es el m&aacute;s frecuente y los extremos son menos probables.<\/p>\r\n<p>Es &uacute;til cuando tienes una <strong data-start=\"533\" data-end=\"552\">idea aproximada<\/strong> del comportamiento de un proceso, pero no datos suficientes para definir una distribuci&oacute;n m&aacute;s compleja.<\/p>\r\n<p><i>&ldquo;Una entrega normalmente tarda 3 d&iacute;as, pero podr&iacute;a hacerlo en 2 o en 5.&rdquo;<\/i><\/p>\r\n<p>Ese tipo de incertidumbre es ideal para una <strong data-start=\"806\" data-end=\"833\">distribuci&oacute;n triangular<\/strong>.<\/p>\r\n<p><b>Par&aacute;metros <\/b><\/p>\r\n<pre>\r\nFunction {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30}\r\n<\/pre>\r\n<p>&nbsp;<\/p>\r\n<ul>\r\n    <li><code data-start=\"952\" data-end=\"957\">Min<\/code>: Valor m&iacute;nimo posible.<\/li>\r\n    <li><code data-start=\"983\" data-end=\"988\">Max<\/code>: Valor m&aacute;ximo posible.<\/li>\r\n    <li><code data-start=\"1014\" data-end=\"1020\">Mode<\/code>: Valor m&aacute;s probable (pico de la distribuci&oacute;n).<\/li>\r\n<\/ul>\r\n<p>Este ejemplo compara tres funciones triangulares con diferentes posiciones del pico. Todas con el mismo rango, pero con distintas distribuciones:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\nFunction {Name:tfun1, TYPE:TRIANGULAR, Min:20, Max:40, Mode:30} ; Pico centrado\r\nFunction {Name:tfun2, TYPE:TRIANGULAR, Min:20, Max:40, Mode:25} ; Pico sesgado a la izquierda\r\nFunction {Name:tfun3, TYPE:TRIANGULAR, Min:20, Max:40, Mode:35} ; Pico sesgado a la derecha<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/tfunction.svg\" width=\"50%'\" alt=\"Graph\"><\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "232",
                "nombre": "Función Poisson",
                "texto": "\/*\r\n\r\n  \t \tPoisson\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nGraphic {NAME:pText1,Type:TEXT,X:309,Y:281,Text:\"Media1\"}\r\nGraphic {NAME:pText2,Type:TEXT,X:309,Y:238,Text:\"Media2\"}\r\nGraphic {NAME:pText3,Type:TEXT,X:308,Y:198,Text:\"Media3\"}\r\n\r\nGraphic {NAME:eText1,Type:TEXT,X:506,Y:280,Text:\"Exp1\"}\r\nGraphic {NAME:eText2,Type:TEXT,X:506,Y:238,Text:\"Exp2\"}\r\nGraphic {NAME:eText3,Type:TEXT,X:504,Y:196,Text:\"Exp3\"}\r\n\r\n\r\n\r\nPOSITION {NAME:POS1,X:598,Y:395}\r\nINITIAL contador,0\r\n\r\nINITIAL LAMBDA1,1\/4\r\nINITIAL iLAMBDA1,round(1\/X$LAMBDA1,3)\r\nINITIAL pTotal1,0\r\nINITIAL eTotal1,0\r\n\r\nINITIAL LAMBDA2,3\/4\r\nINITIAL iLAMBDA2,round(1\/X$LAMBDA2,3)\r\nINITIAL pTotal2,0\r\nINITIAL eTotal2,0\r\n\r\nINITIAL LAMBDA3,3\r\nINITIAL iLAMBDA3,round(1\/X$LAMBDA3,3)\r\nINITIAL pTotal3,0\r\nINITIAL eTotal3,0\r\n\r\n\r\n\r\nFunction {Name:fPoisson1, TYPE:POISSON, LAMBDA:X$LAMBDA1}\r\nFunction {Name:fPoisson2, TYPE:POISSON, LAMBDA:X$LAMBDA2}\r\nFunction {Name:fPoisson3, TYPE:POISSON, LAMBDA:X$LAMBDA3}\r\n\r\nFunction {Name:fexp1, TYPE:EXP, LAMBDA:X$LAMBDA1, b:0}\r\nFunction {Name:fexp2, TYPE:EXP, LAMBDA:X$LAMBDA2, b:0}\r\nFunction {Name:fexp3, TYPE:EXP, LAMBDA:X$LAMBDA3, b:0}\r\n\r\nSTART 2000\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,0 {NAME:GEN1,X:218,Y:395}\r\n\r\nSAVEVALUE contador,X$contador + 1\r\n\r\n; --- Acumulation Poisson ---\r\nSAVEVALUE pTotal1,X$pTotal1 + FN$fPoisson1\r\nSAVEVALUE pMedia1, X$pTotal1 \/ X$contador\r\nSAVEVALUE pMediaRound1, round(X$pMedia1,3)\r\n\r\nSAVEVALUE pTotal2,X$pTotal2 + FN$fPoisson2\r\nSAVEVALUE pMedia2, X$pTotal2 \/ X$contador\r\nSAVEVALUE pMediaRound2, round(X$pMedia2,3)\r\n\r\nSAVEVALUE pTotal3,X$pTotal3 + FN$fPoisson3\r\nSAVEVALUE pMedia3, X$pTotal3 \/ X$contador\r\nSAVEVALUE pMediaRound3, round(X$pMedia3,3)\r\n\r\n; --- Acumulation Exponentian ---\r\n\r\nSAVEVALUE eTotal1,X$eTotal1 + FN$fexp1\r\nSAVEVALUE eMedia1, X$eTotal1 \/ X$contador\r\nSAVEVALUE eMediaRound1, round(X$eMedia1,3)\r\n\r\nSAVEVALUE eTotal2,X$eTotal2 + FN$fexp2\r\nSAVEVALUE eMedia2, X$eTotal2 \/ X$contador\r\nSAVEVALUE eMediaRound2, round(X$eMedia2,3)\r\n\r\nSAVEVALUE eTotal3,X$eTotal3 + FN$fexp3\r\nSAVEVALUE eMedia3, X$eTotal3 \/ X$contador\r\nSAVEVALUE eMediaRound3, round(X$eMedia3,3)\r\n\r\n\r\nmove {name:pText1,text:\"Tendencia a Lambda = X$LAMBDA1  -->  X$pMediaRound1\"}\r\nmove {name:pText2,text:\"Tendencia a Lambda = X$LAMBDA2  -->  X$pMediaRound2\"}\r\nmove {name:pText3,text:\"Tendencia a Lambda = X$LAMBDA3  -->  X$pMediaRound3\"}\r\n\r\nmove {name:eText1,text:\"Exp = 1\/X$LAMBDA1 = X$iLAMBDA1 -->  X$eMediaRound1\"}\r\nmove {name:eText2,text:\"Exp = 1\/X$LAMBDA2 = X$iLAMBDA2 -->  X$eMediaRound2\"}\r\nmove {name:eText3,text:\"Exp = 1\/X$LAMBDA3 = X$iLAMBDA3 -->  X$eMediaRound3\"}\r\n\r\n\r\nADVANCE 30,10 {TO:POS1}\r\n\r\n\r\nENDGENERATE 1\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"315\" data-end=\"433\">La distribuci&oacute;n <strong data-start=\"331\" data-end=\"342\">Poisson<\/strong> modela la <strong data-start=\"353\" data-end=\"376\">cantidad de eventos<\/strong> que ocurren en un intervalo de tiempo fijo, siempre que:<\/p>\r\n<ul data-start=\"435\" data-end=\"532\">\r\n    <li data-start=\"435\" data-end=\"480\">\r\n    <p data-start=\"437\" data-end=\"480\">Los eventos ocurren <strong data-start=\"457\" data-end=\"479\">independientemente<\/strong>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"481\" data-end=\"532\">\r\n    <p data-start=\"483\" data-end=\"532\">La tasa media de ocurrencia (<strong data-start=\"512\" data-end=\"517\">&lambda;<\/strong>) es constante.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p><b>&iquest;Cu&aacute;ndo usar Poisson?<\/b><\/p>\r\n<p>&nbsp;<\/p>\r\n<ul data-start=\"569\" data-end=\"690\">\r\n    <li data-start=\"569\" data-end=\"619\">\r\n    <p data-start=\"571\" data-end=\"619\">N&uacute;mero de llamadas por minuto en un call center.<\/p>\r\n    <\/li>\r\n    <li data-start=\"620\" data-end=\"656\">\r\n    <p data-start=\"622\" data-end=\"656\">Llegadas de clientes a una tienda.<\/p>\r\n    <\/li>\r\n    <li data-start=\"657\" data-end=\"690\">\r\n    <p data-start=\"659\" data-end=\"690\">Errores de fabricaci&oacute;n por d&iacute;a.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p><b>Par&aacute;metro:<\/b><\/p>\r\n<p><code data-start=\"718\" data-end=\"721\">&lambda;<\/code> (lambda): n&uacute;mero medio esperado de eventos por intervalo.<\/p>\r\n<p>Por ejemplo, si &lambda; = 3, en promedio habr&aacute; <strong data-start=\"822\" data-end=\"856\">3 eventos por unidad de tiempo<\/strong>, aunque puede haber 2, 4, 0, etc. La variabilidad est&aacute; incluida en la distribuci&oacute;n.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><b>Relaci&oacute;n con la distribuci&oacute;n exponencial<\/b><\/p>\r\n<ul data-start=\"996\" data-end=\"1093\">\r\n    <li data-start=\"996\" data-end=\"1041\">\r\n    <p data-start=\"998\" data-end=\"1041\"><strong data-start=\"998\" data-end=\"1009\">Poisson<\/strong> cuenta cu&aacute;ntos eventos ocurren.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1042\" data-end=\"1093\">\r\n    <p data-start=\"1044\" data-end=\"1093\"><strong data-start=\"1044\" data-end=\"1059\">Exponencial<\/strong> mide el <strong data-start=\"1068\" data-end=\"1092\">tiempo entre eventos<\/strong>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>Ambas est&aacute;n controladas por el mismo par&aacute;metro <strong data-start=\"1142\" data-end=\"1147\">&lambda;<\/strong>, y son <strong data-start=\"1155\" data-end=\"1174\">complementarias<\/strong>:<\/p>\r\n<p>Poisson(&lambda;):&nbsp; &nbsp; &nbsp; N&uacute;mero de eventos por intervalo&nbsp; &nbsp; &lambda;<br>\r\nExponencial(&lambda;):&nbsp; Tiempo entre eventos consecutivos&nbsp; &nbsp; 1 \/ &lambda;<\/p>\r\n<h3 data-start=\"1484\" data-end=\"1517\">&iquest;Qu&eacute; muestra este ejemplo?<\/h3>\r\n<p data-start=\"1519\" data-end=\"1592\">Comparaci&oacute;n visual de ambas distribuciones para 3 valores distintos de &lambda;:<\/p>\r\n<pre>\r\nFunction {Name:fPoisson1, TYPE:POISSON, LAMBDA:X$LAMBDA1}\r\nFunction {Name:fPoisson2, TYPE:POISSON, LAMBDA:X$LAMBDA2}\r\nFunction {Name:fPoisson3, TYPE:POISSON, LAMBDA:X$LAMBDA3}\r\n\r\nFunction {Name:fexp1, TYPE:EXP, LAMBDA:X$LAMBDA1, b:0}\r\nFunction {Name:fexp2, TYPE:EXP, LAMBDA:X$LAMBDA2, b:0}\r\nFunction {Name:fexp3, TYPE:EXP, LAMBDA:X$LAMBDA3, b:0}<\/pre>\r\n<p data-start=\"1519\" data-end=\"1592\">Los resultados se visualizan en texto din&aacute;mico con la <strong data-start=\"1999\" data-end=\"2018\">media acumulada<\/strong>:<\/p>\r\n<pre>\r\nTendencia a &lambda; = 3.0 &rarr; Media &asymp; 3.0\r\nTendencia a &lambda; = 0.25 &rarr; Media &asymp; 0.25\r\n\r\nExp = 1 \/ &lambda; = 0.333 &rarr; Media &asymp; 0.333<\/pre>\r\n<p data-start=\"2161\" data-end=\"2217\">Esta simulaci&oacute;n sirve para entender visualmente c&oacute;mo:<\/p>\r\n<ul data-start=\"2219\" data-end=\"2339\">\r\n    <li data-start=\"2219\" data-end=\"2273\">\r\n    <p data-start=\"2221\" data-end=\"2273\"><strong data-start=\"2221\" data-end=\"2232\">Poisson<\/strong> estabiliza el n&uacute;mero de eventos hacia &lambda;.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2274\" data-end=\"2339\">\r\n    <p data-start=\"2276\" data-end=\"2339\"><strong data-start=\"2276\" data-end=\"2291\">Exponencial<\/strong> estabiliza el tiempo entre eventos hacia 1 \/ &lambda;.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1519\" data-end=\"1592\">&nbsp;<\/p>\r\n<p><img src=\"..\/imgfront\/pfunction.svg\" width=\"50%'\" alt=\"Graph\"><\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "314",
                "nombre": "Funciones tipo FDISTRIBUTION",
                "texto": "\/*\r\n\r\n  \t \tFDistribution\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nFacility {NAME:VENTANILLA1,X:244,Y:190,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:20}\r\n\r\nFacility {NAME:VENTANILLA2,X:412,Y:188,E_BIN_start:5,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:20}\r\n\r\nPOSITION {NAME:POS1,X:200,Y:387}\r\nPOSITION {NAME:POS2,X:341,Y:381}\r\nPOSITION {NAME:POS3,X:507,Y:380}\r\n\r\n;GAUSSIANA\r\nFunction {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}\r\n\r\n; TRIANGULAR B: centro del rango C: ancho total del rango\r\nFunction {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} \r\n\r\nSTART 1000\r\n\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,0 {NAME:GEN1,X:56,Y:319}\r\n\r\nADVANCE 20,0 {TO:POS1}\r\n\r\nADVANCE 30,10 {TO:VENTANILLA1}\r\n\r\nSEIZE VENTANILLA1\r\nADVANCE FN$tName1\r\nRELEASE VENTANILLA1\r\n\r\nADVANCE 20,0 {TO:POS2}\r\n\r\nADVANCE 20,10 {TO:VENTANILLA2}\r\n\r\nSEIZE VENTANILLA2\r\nADVANCE FN$tName2\r\nRELEASE VENTANILLA2\r\n\r\nADVANCE 20,0 {TO:POS3}\r\n\r\n\r\nENDGENERATE 1\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<h3>Distribuciones personalizadas<\/h3>\r\n<p data-start=\"422\" data-end=\"633\">El tipo <code data-start=\"430\" data-end=\"445\">FDISTRIBUTION<\/code> permite definir funciones de distribuci&oacute;n <strong data-start=\"488\" data-end=\"520\">completamente personalizadas<\/strong> mediante una <strong data-start=\"534\" data-end=\"558\">expresi&oacute;n matem&aacute;tica<\/strong>, sin limitarse a f&oacute;rmulas predefinidas como <code data-start=\"603\" data-end=\"610\">GAUSS<\/code>, <code data-start=\"612\" data-end=\"617\">EXP<\/code> o <code data-start=\"620\" data-end=\"632\">TRIANGULAR<\/code>.<\/p>\r\n<p data-start=\"635\" data-end=\"844\">Estas funciones <strong data-start=\"651\" data-end=\"701\">generan una tabla de distribuci&oacute;n internamente<\/strong>, evaluando una expresi&oacute;n matem&aacute;tica en un rango determinado. Es ideal para representar distribuciones no est&aacute;ndar o variantes de las cl&aacute;sicas.<\/p>\r\n<p data-start=\"187\" data-end=\"477\">&nbsp;<\/p>\r\n<p data-start=\"479\" data-end=\"643\">&iquest;C&oacute;mo funciona?<\/p>\r\n<p data-start=\"872\" data-end=\"928\">GPSS-Plus construye una tabla de distribuci&oacute;n basada en:<\/p>\r\n<ul data-start=\"930\" data-end=\"1247\">\r\n    <li data-start=\"930\" data-end=\"1033\">\r\n    <p data-start=\"932\" data-end=\"1033\">Una <strong data-start=\"936\" data-end=\"960\">expresi&oacute;n matem&aacute;tica<\/strong> que define la forma de la funci&oacute;n (como una campana, un tri&aacute;ngulo, etc.)<\/p>\r\n    <\/li>\r\n    <li data-start=\"1034\" data-end=\"1100\">\r\n    <p data-start=\"1036\" data-end=\"1100\">Un rango de valores (<code data-start=\"1057\" data-end=\"1062\">min<\/code>, <code data-start=\"1064\" data-end=\"1069\">max<\/code>) donde se eval&uacute;a dicha funci&oacute;n<\/p>\r\n    <\/li>\r\n    <li data-start=\"1101\" data-end=\"1167\">\r\n    <p data-start=\"1103\" data-end=\"1167\">Un n&uacute;mero de <code data-start=\"1116\" data-end=\"1127\">intervals<\/code> que determina la resoluci&oacute;n de la tabla<\/p>\r\n    <\/li>\r\n    <li data-start=\"1168\" data-end=\"1247\">\r\n    <p data-start=\"1170\" data-end=\"1247\">Par&aacute;metros opcionales (<code data-start=\"1193\" data-end=\"1196\">A<\/code>, <code data-start=\"1198\" data-end=\"1201\">B<\/code>, <code data-start=\"1203\" data-end=\"1206\">C<\/code>...) que puedes usar dentro de la f&oacute;rmula<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1249\" data-end=\"1421\">Una vez construida la tabla, la funci&oacute;n act&uacute;a como cualquier otra distribuci&oacute;n: se toma un valor aleatorio, se consulta la tabla y se devuelve el resultado correspondiente.<\/p>\r\n<p data-start=\"1249\" data-end=\"1421\"><b>Sintaxis:<\/b><\/p>\r\n<pre>\r\nFunction {\r\n  Name: nombre,\r\n  Type: FDISTRIBUTION,\r\n  Expression: &quot;f&oacute;rmula&quot;,\r\n  min: valorMinimo,\r\n  max: valorMaximo,\r\n  intervals: cantidad,\r\n  A: valor, B: valor, ...\r\n}<\/pre>\r\n<p><b>Ejemplo 1: Distribuci&oacute;n gaussiana personalizada<\/b><\/p>\r\n<pre>\r\nFunction {\r\n  Name:gauss1,\r\n  Type: FDISTRIBUTION,\r\n&nbsp; EXPRESSION: &quot;(1 \/ (A * SQRT(2 * PI))) * EXP(-0.5 * ((X - B) \/ A)^2)&quot;,\r\n&nbsp; A: 2.3,\r\n&nbsp; B: 30,\r\n&nbsp; min: B - (3 * A),\r\n&nbsp; max: B + (3 * A),\r\n&nbsp; intervals: 100\r\n}<\/pre>\r\n<p>Este ejemplo crea una funci&oacute;n tipo campana centrada en <strong data-start=\"1965\" data-end=\"1971\">30<\/strong>, con dispersi&oacute;n <strong data-start=\"1988\" data-end=\"1995\">2.3<\/strong>. Es equivalente a una gaussiana cl&aacute;sica, pero totalmente configurable.<\/p>\r\n<p><b>Ejemplo 2: Distribuci&oacute;n triangular definida por f&oacute;rmula<\/b><\/p>\r\n<pre>\r\nFunction {\r\n  Name:tri1,\r\n  Type: FDISTRIBUTION,\r\n&nbsp; EXPRESSION: &quot;(1 \/ C) * (1 - ABS((X - B) \/ (C \/ 2)))&quot;,\r\n&nbsp; B: 25, C: 30,\r\n&nbsp; min: B - C \/ 2,\r\n&nbsp; max: B + C \/ 2,\r\n&nbsp; intervals: 100\r\n}<\/pre>\r\n<p>Esta funci&oacute;n genera una distribuci&oacute;n triangular con pico en <strong data-start=\"2386\" data-end=\"2392\">25<\/strong>, anchura total de <strong data-start=\"2411\" data-end=\"2417\">30<\/strong>, y base de <strong data-start=\"2429\" data-end=\"2441\">[10, 40]<\/strong>.<\/p>\r\n<h3 data-start=\"2449\" data-end=\"2471\">Detalles t&eacute;cnicos:<\/h3>\r\n<ul data-start=\"2473\" data-end=\"2736\">\r\n    <li data-start=\"2473\" data-end=\"2522\">\r\n    <p data-start=\"2475\" data-end=\"2522\">La variable <code data-start=\"2487\" data-end=\"2490\">X<\/code> es el eje horizontal (dominio).<\/p>\r\n    <\/li>\r\n    <li data-start=\"2523\" data-end=\"2584\">\r\n    <p data-start=\"2525\" data-end=\"2584\">La funci&oacute;n se eval&uacute;a <code data-start=\"2546\" data-end=\"2557\">intervals<\/code> veces entre <code data-start=\"2570\" data-end=\"2575\">min<\/code> y <code data-start=\"2578\" data-end=\"2583\">max<\/code>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2585\" data-end=\"2667\">\r\n    <p data-start=\"2587\" data-end=\"2667\">La integral se normaliza autom&aacute;ticamente para comportarse como una distribuci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2668\" data-end=\"2736\">\r\n    <p data-start=\"2670\" data-end=\"2736\">Todos los par&aacute;metros (<code data-start=\"2692\" data-end=\"2695\">A<\/code>, <code data-start=\"2697\" data-end=\"2700\">B<\/code>, <code data-start=\"2702\" data-end=\"2705\">C<\/code>, etc.) pueden ser expresiones.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "313",
                "nombre": "Funciones tipo MATH",
                "texto": "\/*\r\n\r\n  \t \tMath\r\n \r\n*\/\r\nGraphic {NAME:Text1,Type:TEXT,X:370,Y:312,Text:\"Hello\"}\r\n\r\nFunction {Name:math1, type: MATH, EXPRESSION:\"A * 2 + B\"}\r\n\r\nSTART 1\r\n;*****************************************************\r\nGENERATE 1,0,0,1 {NAME:GEN1,X:56,Y:319}\r\n\r\nassign AAA,4\r\nassign BBB,6\r\n\r\nmove {name:Text1,text:\"Result: FN$(math1,P$AAA,P$BBB)\"}\r\n\r\nENDGENERATE 1\r\n\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<h3>F&oacute;rmulas matem&aacute;ticas personalizadas<\/h3>\r\n<p data-start=\"231\" data-end=\"521\">El tipo <code data-start=\"239\" data-end=\"245\">MATH<\/code> permite definir funciones que se eval&uacute;an como <strong data-start=\"292\" data-end=\"325\">f&oacute;rmulas matem&aacute;ticas directas<\/strong>, sin utilizar distribuciones ni tablas de valores precalculadas. Son &uacute;tiles cuando el valor que necesitamos <strong data-start=\"434\" data-end=\"520\">depende de variables de forma determinista o con una expresi&oacute;n algebraica<\/strong>.<\/p>\r\n<p data-start=\"523\" data-end=\"678\">Este tipo de funci&oacute;n <strong data-start=\"544\" data-end=\"570\">no genera aleatoriedad<\/strong>. Simplemente toma los par&aacute;metros indicados en orden, los sustituye en la expresi&oacute;n y devuelve el resultado.<\/p>\r\n<p>Sintaxis:<\/p>\r\n<pre>\r\nFunction {Name:nombre, Type:MATH, Expression:&quot;A + B * C&quot;}<\/pre>\r\n<p>&nbsp;<\/p>\r\n<ul>\r\n    <li><code data-start=\"771\" data-end=\"777\">Name<\/code>: nombre identificador de la funci&oacute;n.<\/li>\r\n    <li><code data-start=\"817\" data-end=\"823\">Type<\/code>: debe ser <code data-start=\"834\" data-end=\"840\">MATH<\/code>.<\/li>\r\n    <li><code data-start=\"844\" data-end=\"856\">Expression<\/code>: f&oacute;rmula a evaluar, usando letras may&uacute;sculas como variables (A, B, C...). Estas letras representan los par&aacute;metros pasados en orden.<\/li>\r\n<\/ul>\r\n<p><b>&iquest;C&oacute;mo se llama una funci&oacute;n <code data-start=\"1032\" data-end=\"1038\">MATH<\/code>?<\/b><\/p>\r\n<p>Se utiliza como cualquier funci&oacute;n <code data-start=\"1075\" data-end=\"1080\">FN$<\/code>, pero con par&aacute;metros entre par&eacute;ntesis:<\/p>\r\n<pre>\r\nFN$(nombre, valor_A, valor_B, ...)<\/pre>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "264",
        "nombre": "Season 10: Sistemas continuos",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "267",
                "nombre": "Sistema continuo dentro del discreto",
                "texto": "\/*\r\n\r\n Sistema continuo dentro del discreto\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}\r\n\r\nGraphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:\"aviso\"}\r\n\r\nFunction {Name:fcCaudal, Type:Math, Expression:\"0.6 + SIN(A) * 4\"}\r\n\r\nINITIAL nivel, 0\r\nINITIAL caudal, 1.2\r\nINITIAL fase, 0\r\nINITIAL constante, 0.6\r\n\r\ninclude .\/library_graphics\/speedometer.lib\r\n\r\nSTART 100\r\n\r\nPROCEDURE PRE_RUN\r\n\tassign config,{title:\"Depósito\"\r\n    \t\t,x:100,y:60 \r\n            ,width:100 ,height:180\r\n\t       \t,min_value: 0\r\n    \t\t,max_value: 100\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\tcall deposito.speedometer.init,V$config\r\n\r\n\tassign config,{title:\"Caudal\"\r\n    \t\t,x:300,y:60 \r\n            ,width:100 ,height:180\r\n\t       \t,min_value: -10\r\n    \t\t,max_value: 10\r\n            ,\"color\":\"orange\"}\r\n            \r\n\tcall caudal.speedometer.init,V$config\r\n\r\nTERMINATE_VE \r\nENDPROCEDURE\r\n\r\nPROCEDURE llenado\r\n    ; Oscilar caudal como una función del tiempo simulado\r\n;    SAVEVALUE caudal, X$constante + SIN(X$fase) * 4\r\n    SAVEVALUE caudal, FN$(fcCaudal,X$fase)\r\n\r\n\r\n    ; Aumentamos la fase suavemente\r\n    SAVEVALUE fase, X$fase + 0.1\r\n\r\n    ; Sumamos el caudal al nivel, según el intervalo de tiempo\r\n    SAVEVALUE nivel, X$nivel + X$caudal * 0.1\r\n\r\n    ; Actualizamos visualmente la línea del depósito\r\n\r\n\r\n    ; Valores redondeados para mostrar\r\n    ASSIGN tCaudal, round(X$caudal, 2)\r\n    ASSIGN tNivel, round(X$nivel, 2)\r\n\tCALL deposito.speedometer.set, P$tNivel\r\n\tCALL caudal.speedometer.set, P$tCaudal\r\n    \r\n    ; Visualización del estado\r\n    IF (X$nivel>=100)\r\n        MOVE {name:aviso, text:\"¡DEPÓSITO LLENO!\"}\r\n        STOP\r\n    ELSE\r\n        MOVE {name:aviso, text:\"Nivel: P$tNivel - Caudal: P$tCaudal\"}\r\n    ENDIF\r\nTERMINATE\r\nENDPROCEDURE 1\r\n",
                "descripcion": "<p>Hasta ahora hemos visto c&oacute;mo <strong data-start=\"245\" data-end=\"258\">GPSS-Plus<\/strong> es, en su esencia, un motor de simulaci&oacute;n de eventos <strong data-start=\"312\" data-end=\"325\">discretos<\/strong>. Pero tambi&eacute;n es capaz de simular sistemas <strong data-start=\"369\" data-end=\"382\">continuos<\/strong>, es decir, aquellos en los que los cambios ocurren de forma gradual a lo largo del tiempo.<\/p>\r\n<h3 data-start=\"475\" data-end=\"510\">&iquest;Qu&eacute; es un sistema continuo?<\/h3>\r\n<p data-start=\"512\" data-end=\"603\">Un sistema continuo es aquel en el que los valores cambian sin interrupciones. Por ejemplo:<\/p>\r\n<ul data-start=\"605\" data-end=\"746\">\r\n    <li data-start=\"605\" data-end=\"650\">\r\n    <p data-start=\"607\" data-end=\"650\">El nivel de un dep&oacute;sito que se va llenando.<\/p>\r\n    <\/li>\r\n    <li data-start=\"651\" data-end=\"696\">\r\n    <p data-start=\"653\" data-end=\"696\">La temperatura de un horno que se calienta.<\/p>\r\n    <\/li>\r\n    <li data-start=\"697\" data-end=\"746\">\r\n    <p data-start=\"699\" data-end=\"746\">La velocidad de un coche acelerando suavemente.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"748\" data-end=\"878\">En estos casos no hay &ldquo;saltos&rdquo; puntuales como los de una cola o un cliente que entra o sale. El cambio ocurre de forma progresiva.<\/p>\r\n<h3 data-start=\"880\" data-end=\"930\">&iquest;Y c&oacute;mo simular eso en un entorno discreto?<\/h3>\r\n<p data-start=\"932\" data-end=\"1266\">La respuesta es: <strong data-start=\"949\" data-end=\"995\">haciendo trampa, pero de forma inteligente<\/strong>. En lugar de simular un cambio constante y fluido, lo dividimos en <strong>peque&ntilde;os&nbsp;<\/strong><strong data-start=\"1063\" data-end=\"1096\">pasos temporales<\/strong>, lo bastante r&aacute;pidos como para que <strong data-start=\"1132\" data-end=\"1152\">parezca continuo<\/strong>.<br data-start=\"1153\" data-end=\"1156\">\r\nEs como el cine: cada fotograma es una imagen est&aacute;tica, pero al reproducirse 24 por segundo, vemos movimiento.<\/p>\r\n<h3 data-start=\"1268\" data-end=\"1308\">&iquest;C&oacute;mo se hace esto en GPSS-Plus?<\/h3>\r\n<p data-start=\"1310\" data-end=\"1332\">Con un ajustes clave:<\/p>\r\n<pre>\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}\r\n<\/pre>\r\n<p data-start=\"1310\" data-end=\"1332\">&nbsp;<\/p>\r\n<ul>\r\n    <li><code data-start=\"1404\" data-end=\"1421\">TIME_DECIMALS:1<\/code> indica que el tiempo se mide con un decimal (0.1).<\/li>\r\n    <li><code data-start=\"1479\" data-end=\"1490\">SPEED:5<\/code> hace que el sistema se actualice con una interpretaci&oacute;n media. Del 0 que indica pausa a 10 que es el m&aacute;ximo de velocidad. <b>La interpretaci&oacute;n media (5) es que 10 instantes AC1 tandan aproximadamente 1 segundo. Con <code>SPEED:2<\/code>, 1 segundo equivale aproximadamente a 1 AC1.<\/b><\/li>\r\n<\/ul>\r\n<p>El motor sigue siendo discreto. El tiempo <em data-start=\"1659\" data-end=\"1682\">no avanza por s&iacute; solo<\/em>; avanza cuando se atiende una entidad o se dispara un evento. Lo que hacemos es generar muchos eventos peque&ntilde;os, muy seguidos.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p data-start=\"748\" data-end=\"878\"><b>Ejemplo: Llenado de un dep&oacute;sito<\/b><\/p>\r\n<p data-start=\"1855\" data-end=\"1977\">Vamos a llenar un dep&oacute;sito con un <strong data-start=\"1889\" data-end=\"1908\">caudal variable<\/strong> (por ejemplo, siguiendo una funci&oacute;n seno para hacerlo m&aacute;s din&aacute;mico).<\/p>\r\n<p data-start=\"1979\" data-end=\"2124\">Para ello, usamos un <strong data-start=\"2000\" data-end=\"2009\">timer<\/strong>, que lanza un bloque de c&oacute;digo cada 0.1 unidades de tiempo. No genera transacciones ni estad&iacute;sticas: solo ejecuta.<\/p>\r\n<pre>\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}<\/pre>\r\n<p data-start=\"2199\" data-end=\"2251\">Cada vez que se activa el <code data-start=\"2225\" data-end=\"2236\">PROCEDURE<\/code>, haremos esto:<\/p>\r\n<ol data-start=\"2253\" data-end=\"2474\">\r\n    <li data-start=\"2253\" data-end=\"2303\">\r\n    <p data-start=\"2256\" data-end=\"2303\">Calcular un caudal&nbsp;seg&uacute;n una funci&oacute;n oscilante, definida previamente con <code data-start=\"522\" data-end=\"542\">FUNCTION TYPE:MATH<\/code>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2304\" data-end=\"2354\">\r\n    <p data-start=\"2307\" data-end=\"2354\">A&ntilde;adir ese caudal al nivel actual del dep&oacute;sito.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2355\" data-end=\"2390\">\r\n    <p data-start=\"2358\" data-end=\"2390\">Actualizar visualmente el nivel.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2391\" data-end=\"2423\">\r\n    <p data-start=\"2394\" data-end=\"2423\">Mostrar el valor en pantalla.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2424\" data-end=\"2474\">\r\n    <p data-start=\"2427\" data-end=\"2474\">Si el dep&oacute;sito se llena, detener la simulaci&oacute;n.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"748\" data-end=\"878\">&nbsp;<\/p>\r\n<p data-start=\"748\" data-end=\"878\">En ese caso, cada ciclo calculaba el <strong data-start=\"1020\" data-end=\"1030\">caudal<\/strong> y lo sumaba al <strong data-start=\"1046\" data-end=\"1055\">nivel<\/strong> con una f&oacute;rmula sencilla:<\/p>\r\n<pre>\r\nSAVEVALUE nivel, X$nivel + X$caudal * 0.1\r\n<\/pre>\r\n<p data-start=\"1138\" data-end=\"1459\">Este m&eacute;todo se llama <strong data-start=\"1159\" data-end=\"1178\">m&eacute;todo de Euler<\/strong>, y es la forma m&aacute;s simple de <b>integraci&oacute;n <\/b>num&eacute;rica.&nbsp;<\/p>\r\n<p data-start=\"1138\" data-end=\"1459\">Estima que el intervalo de tiempo entre AC1$ y AC1$ + 0.1 el dep&oacute;sito se va a llenar con la cantidad de caudal en el momento AC1$ multiplicado por el intervalo 0.1<\/p>\r\n<p data-start=\"1138\" data-end=\"1459\">En el siguiente paso, volver&aacute; a hacer el mismo c&aacute;lculo, usando el nuevo valor del caudal en ese instante. Y as&iacute; sucesivamente, sumando peque&ntilde;as cantidades como si fuera una suma de rect&aacute;ngulos: eso es, en esencia, una <strong data-start=\"924\" data-end=\"936\">integral<\/strong>.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "266",
                "nombre": "Recolección de datos PLOT",
                "texto": "\/*\r\n\r\n Recolección de datos PLOT\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}\r\n\r\nPLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal, X:TIME_AC1}\r\n\r\nGraphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:\"aviso\"}\r\n\r\n\r\nFunction {Name:fcCaudal, Type:Math, Expression:\"0.6 + SIN(A) * 4\"}\r\n\r\nINITIAL nivel, 0\r\nINITIAL caudal, 1.2\r\nINITIAL fase, 0\r\n\r\ninclude .\/library_graphics\/speedometer.lib\r\n\r\nSTART 100\r\n\r\nPROCEDURE PRE_RUN\r\n\tassign config,{title:\"Depósito\"\r\n    \t\t,x:100,y:60 \r\n            ,width:100 ,height:180\r\n\t       \t,min_value: 0\r\n    \t\t,max_value: 100\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\tcall deposito.speedometer.init,V$config\r\n\r\n\tassign config,{title:\"Caudal\"\r\n    \t\t,x:300,y:60 \r\n            ,width:100 ,height:180\r\n\t       \t,min_value: -10\r\n    \t\t,max_value: 10\r\n            ,\"color\":\"orange\"}\r\n            \r\n\tcall caudal.speedometer.init,V$config\r\n\r\nTERMINATE_VE \r\nENDPROCEDURE\r\n\r\nPROCEDURE llenado\r\n    ; Oscilar caudal como una función del tiempo simulado\r\n    SAVEVALUE caudal, FN$(fcCaudal,X$fase)\r\n\r\n    ; Aumentamos la fase\r\n    SAVEVALUE fase, X$fase + 0.1\r\n\r\n    ; Sumar caudal al nivel\r\n    SAVEVALUE nivel, X$nivel + X$caudal * 0.1\r\n\r\n    ; Registrar los datos en el ploter\r\n    PLOT thePlot, X$nivel, X$caudal\r\n\r\n    ; Mostrar valores redondeados\r\n    ASSIGN tCaudal, round(X$caudal, 2)\r\n    ASSIGN tNivel, round(X$nivel, 2)\r\n\r\n    ; Actualizar visualmente el nivel en pantalla\r\n\tCALL deposito.speedometer.set, P$tNivel\r\n\tCALL caudal.speedometer.set, P$tCaudal\r\n\r\n\tIF (X$nivel>=100)\r\n        MOVE {name:aviso, text:\"¡DEPÓSITO LLENO!\"}\r\n        STOP\r\n    ELSE\r\n        MOVE {name:aviso, text:\"Nivel: P$tNivel - Caudal: P$tCaudal\"}\r\n    ENDIF\r\nTERMINATE\r\nENDPROCEDURE 1\r\n\r\n",
                "descripcion": "<p data-start=\"331\" data-end=\"589\">En el cap&iacute;tulo anterior vimos c&oacute;mo GPSS-Plus puede simular comportamientos <em data-start=\"406\" data-end=\"431\">aparentemente continuos<\/em> mediante el uso de <code data-start=\"451\" data-end=\"459\">TIMERs<\/code> y pasos de tiempo muy peque&ntilde;os. Observamos, por ejemplo, c&oacute;mo se llenaba un dep&oacute;sito en pantalla, simulando un proceso constante.<\/p>\r\n<p data-start=\"591\" data-end=\"742\">Ahora vamos un paso m&aacute;s all&aacute;:<br data-start=\"620\" data-end=\"623\">\r\nNo solo vamos a <strong data-start=\"639\" data-end=\"646\">ver<\/strong> lo que ocurre&hellip; vamos a <strong data-start=\"670\" data-end=\"714\">registrarlo y graficarlo autom&aacute;ticamente<\/strong> para su an&aacute;lisis posterior.<\/p>\r\n<p data-start=\"744\" data-end=\"951\">Porque, en muchos casos, <strong data-start=\"769\" data-end=\"813\">no basta con observar c&oacute;mo se mueve algo<\/strong>: necesitamos tener una curva, un gr&aacute;fico, un historial del comportamiento.<br data-start=\"888\" data-end=\"891\">\r\nPara eso existe en GPSS-Plus el sistema de trazado de datos:<\/p>\r\n<h3 data-start=\"953\" data-end=\"975\"><code data-start=\"957\" data-end=\"966\">PLOTTER<\/code> y <code data-start=\"969\" data-end=\"975\">PLOT<\/code><\/h3>\r\n<ul data-start=\"977\" data-end=\"1141\">\r\n    <li data-start=\"977\" data-end=\"1051\">\r\n    <p data-start=\"979\" data-end=\"1051\"><code data-start=\"979\" data-end=\"991\">PLOTTER {}<\/code>: declara una tabla gr&aacute;fica donde se ir&aacute;n guardando valores.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1052\" data-end=\"1141\">\r\n    <p data-start=\"1054\" data-end=\"1141\"><code data-start=\"1054\" data-end=\"1060\">PLOT<\/code>: a&ntilde;ade un punto a esa tabla, usando el tiempo actual u otra variable como eje X.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1143\" data-end=\"1291\">Cada punto a&ntilde;adido es como un &ldquo;fotograma&rdquo; del sistema, y al terminar la simulaci&oacute;n, GPSS-Plus dibuja una <strong data-start=\"1248\" data-end=\"1257\">curva<\/strong> con todos los puntos registrados.<\/p>\r\n<h3 data-start=\"1293\" data-end=\"1311\">Ejemplo de uso<\/h3>\r\n<p data-start=\"1313\" data-end=\"1347\">En nuestro caso vamos a registrar:<\/p>\r\n<pre>\r\nPLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal, X:TIME_AC1}\r\n<\/pre>\r\n<p data-start=\"1418\" data-end=\"1433\">Esto significa:<\/p>\r\n<ul data-start=\"1434\" data-end=\"1564\">\r\n    <li data-start=\"1434\" data-end=\"1495\">\r\n    <p data-start=\"1436\" data-end=\"1495\">Vamos a graficar dos series de valores: <code data-start=\"1476\" data-end=\"1483\">nivel<\/code> y <code data-start=\"1486\" data-end=\"1494\">caudal<\/code>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1496\" data-end=\"1564\">\r\n    <p data-start=\"1498\" data-end=\"1564\">El eje horizontal (X) ser&aacute; el tiempo (<code data-start=\"1536\" data-end=\"1542\">AC1$<\/code>), llamado <code data-start=\"1553\" data-end=\"1563\">TIME_AC1<\/code>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1566\" data-end=\"1602\">Para a&ntilde;adir puntos en cada instante:<\/p>\r\n<pre>\r\nPLOT thePlot, X$nivel, X$caudal\r\n<\/pre>\r\n<p data-start=\"1313\" data-end=\"1347\">Esto se ejecuta dentro del <code data-start=\"1674\" data-end=\"1685\">PROCEDURE<\/code>, en cada llamada, y va dibujando la historia del proceso.<\/p>\r\n<p data-start=\"1313\" data-end=\"1347\"><em data-start=\"849\" data-end=\"935\">&quot;NIVEL se obtiene acumulando el CAUDAL. El sistema simula una integraci&oacute;n continua.&quot;<\/em><br data-start=\"935\" data-end=\"938\">\r\n<em data-start=\"940\" data-end=\"1019\">&quot;Cada punto corresponde a un instante generado autom&aacute;ticamente por el TIMER.&quot;<\/em><\/p>\r\n<p data-start=\"1143\" data-end=\"1291\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "265",
                "nombre": "Mejora de resultados con INTEGRATE",
                "texto": "\/*\r\n\r\n Mejora de resultados con INTEGRATE\r\n \r\n*\/\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:llenado, INTERVAL: 0.1}\r\n\r\nPLOTTER {NAME:thePlot, Y_0:nivel, Y_1:caudal,X:TIME_AC1}\r\nPLOTTER {NAME:thePlot_RK4, Y_0:nivel, Y_1:caudal,X:TIME_AC1}\r\nPLOTTER {NAME:thePlot_ALL, Y_0:nivel, Y_1:caudal, Y_2:nivel_RK4, Y_3:caudal_RK4,X:TIME_AC1}\r\n\r\nGraphic {NAME:aviso,Type:TEXT,X:291,Y:342,Text:\"aviso\"}\r\nGraphic {NAME:aviso_RK4,Type:TEXT,X:291,Y:302,Text:\"Aviso2\"}\r\n\r\nGraphic {NAME:Cubo1,Type:LINE,POINTS:\"[100,100],[100,500],[200,500],[200,100]\"\r\n\t, Close:1\r\n    , color:#00FFFF\r\n    , Fcolor:#FFFFFF}\r\nGraphic {NAME:Line1,Type:LINE,fcolor:#FF6666, X1:102,Y1:100,X2:198,Y2:100}\r\n\r\nGraphic {NAME:Line_RK4,Type:LINE,color:#333300, X1:98,Y1:100,X2:202,Y2:100}\r\n\r\n\r\n\r\nINITIAL nivel, 0\r\nINITIAL caudal, 1.2\r\nINITIAL nivel_RK4, 0\r\nINITIAL caudal_RK4, 1.2\r\nINITIAL fase, 0\r\nINITIAL constante, 0.6\r\n\r\nSTART 100\r\n\r\n;*****************************************************\r\nPROCEDURE llenado\r\n    ; Oscilar caudal como una función del tiempo simulado\r\n    SAVEVALUE caudal, X$constante + SIN(X$fase) * 4\r\n    ; Aumentamos la fase suavemente\r\n    SAVEVALUE fase, X$fase + 0.1\r\n    ; Integración continua del nivel\r\n   \r\n    INTEGRATE  { EXPRESSION: \"X$constante + SIN(T) * 4\", METHOD: RK4, DT: 0.1, SAVEVALUE: caudal_RK4 }\r\n    \r\n\r\n    SAVEVALUE nivel, X$nivel + X$caudal * 0.1\r\n    SAVEVALUE nivel_RK4, X$nivel_RK4 + X$caudal_RK4 * 0.1\r\n\r\n\tPLOT thePlot,X$nivel,X$caudal\r\n\tPLOT thePlot_RK4,X$nivel_RK4,X$caudal_RK4\r\n\tPLOT thePlot_ALL,X$nivel,X$caudal,X$nivel_RK4,X$caudal_RK4\r\n\r\n\tMOVE {NAME:Line1, X1:98,Y1:(100+X$nivel*4) ,X2:202,Y2:(100+X$nivel*4)}\r\n\tMOVE {NAME:Line_RK4, X1:98,Y1:(100+X$nivel_RK4*4) ,X2:202,Y2:(100+X$nivel_RK4*4)}\r\n\r\n    ASSIGN tCaudal, round(X$caudal, 2)\r\n    ASSIGN tNivel, round(X$nivel, 2)\r\n\r\n\tASSIGN tCaudal_RK4, round(X$caudal_RK4, 2)\r\n    ASSIGN tNivel_RK4, round(X$nivel_RK4, 2)\r\n    \r\n    ; Visualización del estado\r\n    IF (X$nivel>=100)\r\n        MOVE {name:aviso, text:\"¡DEPÓSITO LLENO!\"}\r\n        stop\r\n    ELSE\r\n        MOVE {name:aviso, text:\"Nivel: P$tNivel - Caudal: P$tCaudal\"}\r\n        MOVE {name:aviso_RK4, text:\"Nivel: P$tNivel_RK4 - Caudal: P$tCaudal_RK4\"}\r\n    ENDIF\r\nTERMINATE\r\nENDPROCEDURE 1\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"206\" data-end=\"473\">En los cap&iacute;tulos anteriores vimos c&oacute;mo GPSS-Plus permite simular sistemas con comportamiento aparentemente continuo gracias a la ejecuci&oacute;n frecuente de procedimientos temporizados (<code data-start=\"387\" data-end=\"394\">TIMER<\/code>) y el uso de <code data-start=\"408\" data-end=\"419\">SAVEVALUE<\/code> para acumular valores como el llenado de un dep&oacute;sito.<\/p>\r\n<p data-start=\"475\" data-end=\"539\">Hasta ahora us&aacute;bamos una f&oacute;rmula simple para simular el llenado:<\/p>\r\n<pre>\r\nSAVEVALUE nivel, X$nivel + X$caudal * 0.1<\/pre>\r\n<p data-start=\"475\" data-end=\"539\">Este m&eacute;todo es efectivo, pero b&aacute;sico. Asume que el caudal es constante durante cada intervalo de tiempo, lo cual no es del todo cierto si el caudal var&iacute;a r&aacute;pidamente, por ejemplo, con una funci&oacute;n seno como es el caso.<\/p>\r\n<p data-start=\"475\" data-end=\"539\">Y cuando hablamos de intervalo, en este caso hablamos de ese &quot;0.1&quot;. No es lo mismo SIN(0) que SIN(0.1).Y si queremos una estimaci&oacute;n <strong data-start=\"725\" data-end=\"756\">m&aacute;s precisa del valor medio<\/strong>, probablemente ser&iacute;a mejor usar <code data-start=\"789\" data-end=\"800\">SIN(0.05)<\/code>, es decir, el punto <strong data-start=\"821\" data-end=\"832\">central<\/strong> del intervalo.<\/p>\r\n<h3 data-start=\"820\" data-end=\"883\">&iquest;Qu&eacute; ocurre si el caudal var&iacute;a durante el paso de tiempo?<\/h3>\r\n<p data-start=\"885\" data-end=\"1048\">Cuando el valor que est&aacute;s acumulando cambia dentro del mismo intervalo, el sistema no es perfectamente exacto: est&aacute;s sumando un valor medio aproximado, no el real.<\/p>\r\n<p data-start=\"1050\" data-end=\"1251\">Para mejorar esto, GPSS-Plus incorpora el bloque <code data-start=\"1099\" data-end=\"1110\">INTEGRATE<\/code>, que utiliza un m&eacute;todo de integraci&oacute;n num&eacute;rica llamado <strong data-start=\"1166\" data-end=\"1203\">Runge-Kutta de cuarto orden (RK4)<\/strong> para calcular una estimaci&oacute;n mucho m&aacute;s precisa.<\/p>\r\n<h3>&iquest;Qu&eacute; hace INTEGRATE?<\/h3>\r\n<pre>\r\nINTEGRATE { EXPRESSION: &quot;SIN(T) + 2&quot;, DT: 0.1, SAVEVALUE: caudal_RK4 }<\/pre>\r\n<p data-start=\"1371\" data-end=\"1383\">Este bloque:<\/p>\r\n<p data-start=\"1371\" data-end=\"1383\">&nbsp;<\/p>\r\n<p data-start=\"1371\" data-end=\"1383\">&nbsp;<\/p>\r\n<ul>\r\n    <li>\r\n    <p data-start=\"826\" data-end=\"967\">Eval&uacute;a la expresi&oacute;n en cuatro puntos clave del intervalo: al inicio (<code data-start=\"895\" data-end=\"898\">X<\/code>), dos veces en el punto medio (<code data-start=\"930\" data-end=\"940\">X + DT\/2<\/code>), y al final (<code data-start=\"955\" data-end=\"963\">X + DT<\/code>).<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-start=\"970\" data-end=\"1140\">Aplica el m&eacute;todo de Runge-Kutta de cuarto orden (RK4), combinando esos valores con pesos espec&iacute;ficos para estimar con precisi&oacute;n la variaci&oacute;n de la funci&oacute;n en ese tramo.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-start=\"1143\" data-end=\"1262\">Guarda el resultado en el <code data-start=\"1169\" data-end=\"1180\">SAVEVALUE<\/code> indicado, como mejor estimaci&oacute;n del valor medio de la expresi&oacute;n en ese intervalo.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>El valor resultante ser&aacute; una <strong data-start=\"1639\" data-end=\"1676\">mejor estimaci&oacute;n del caudal medio<\/strong> en ese intervalo de tiempo, y lo podemos usar para llenar el dep&oacute;sito con m&aacute;s precisi&oacute;n.<\/p>\r\n<p>&nbsp;<\/p>\r\n<h3>Comparando el m&eacute;todo tradicional (Euler) y RK4<\/h3>\r\n<p data-start=\"1826\" data-end=\"1897\">Vamos a construir un ejemplo que utiliza <strong data-start=\"1867\" data-end=\"1896\">ambos m&eacute;todos en paralelo<\/strong>:<\/p>\r\n<ul data-start=\"1899\" data-end=\"2068\">\r\n    <li data-start=\"1899\" data-end=\"1934\">\r\n    <p data-start=\"1901\" data-end=\"1934\"><code data-start=\"1901\" data-end=\"1909\">caudal<\/code>: usando el m&eacute;todo simple<\/p>\r\n    <\/li>\r\n    <li data-start=\"1935\" data-end=\"1982\">\r\n    <p data-start=\"1937\" data-end=\"1982\"><code data-start=\"1937\" data-end=\"1949\">caudal_RK4<\/code>: usando integraci&oacute;n num&eacute;rica RK4<\/p>\r\n    <\/li>\r\n    <li data-start=\"1983\" data-end=\"2025\">\r\n    <p data-start=\"1985\" data-end=\"2025\"><code data-start=\"1985\" data-end=\"1992\">nivel<\/code>: acumulado con el caudal cl&aacute;sico<\/p>\r\n    <\/li>\r\n    <li data-start=\"2026\" data-end=\"2068\">\r\n    <p data-start=\"2028\" data-end=\"2068\"><code data-start=\"2028\" data-end=\"2039\">nivel_RK4<\/code>: acumulado con el caudal RK4<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2070\" data-end=\"2162\">Adem&aacute;s, graficaremos las 4 curvas usando <code data-start=\"2111\" data-end=\"2120\">PLOTTER<\/code> para comparar visualmente los resultados.<\/p>\r\n<h3 data-start=\"2169\" data-end=\"2201\">&iquest;Qu&eacute; ver&aacute;s en la gr&aacute;fica?<\/h3>\r\n<ul data-start=\"2203\" data-end=\"2530\">\r\n    <li data-start=\"2203\" data-end=\"2339\">\r\n    <p data-start=\"2205\" data-end=\"2339\"><code data-start=\"2205\" data-end=\"2216\">nivel_RK4<\/code> sube ligeramente m&aacute;s suavemente y m&aacute;s r&aacute;pido que <code data-start=\"2266\" data-end=\"2273\">nivel<\/code>, porque tiene en cuenta que el caudal crece dentro del intervalo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2340\" data-end=\"2432\">\r\n    <p data-start=\"2342\" data-end=\"2432\"><code data-start=\"2342\" data-end=\"2354\">caudal_RK4<\/code> se ajusta mejor a los picos y valles que <code data-start=\"2396\" data-end=\"2404\">caudal<\/code>, que solo calcula un punto.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2433\" data-end=\"2530\">\r\n    <p data-start=\"2435\" data-end=\"2530\">La l&iacute;nea blanca en pantalla (nivel RK4) va un poco por delante de la l&iacute;nea azul (nivel simple).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"2537\" data-end=\"2583\">&iquest;Cu&aacute;ndo es v&aacute;lido usar RK4?<\/h3>\r\n<p data-start=\"2585\" data-end=\"2781\">El m&eacute;todo <strong data-start=\"2595\" data-end=\"2632\">Runge-Kutta de cuarto orden (RK4)<\/strong>, que implementa el bloque <code data-start=\"2659\" data-end=\"2670\">INTEGRATE<\/code> en GPSS-Plus, es un algoritmo num&eacute;rico dise&ntilde;ado para resolver ecuaciones diferenciales ordinarias de la forma:<\/p>\r\n<pre>\r\ndy\/dt = f(t)<\/pre>\r\n<p data-start=\"475\" data-end=\"539\">Y lo hace suponiendo que la funci&oacute;n <code data-start=\"2845\" data-end=\"2851\">f(t)<\/code> es continua y suave en el intervalo que se est&aacute; integrando.<\/p>\r\n<h3 data-start=\"2918\" data-end=\"2974\">Pero&hellip; GPSS-Plus es un sistema de eventos discretos<\/h3>\r\n<p data-start=\"2976\" data-end=\"3134\">Esto significa que hay muchas variables (<code data-start=\"3017\" data-end=\"3026\">X$nivel<\/code>, <code data-start=\"3028\" data-end=\"3047\">X$entidadesEnCola<\/code>, <code data-start=\"3049\" data-end=\"3062\">X$facilidad<\/code>, etc.) que pueden cambiar <strong data-start=\"3089\" data-end=\"3104\">bruscamente<\/strong>, sin continuidad ni suavidad.<\/p>\r\n<p data-start=\"3136\" data-end=\"3246\">Esos cambios dependen de eventos que se disparan cuando otras entidades llegan, bloquean recursos, o terminan.<\/p>\r\n<p data-start=\"3248\" data-end=\"3258\">Por tanto:<\/p>\r\n<p data-start=\"3260\" data-end=\"3356\"><strong data-start=\"3263\" data-end=\"3356\">No se debe usar INTEGRATE con expresiones que dependan de elementos del sistema discreto.<\/strong><\/p>\r\n<p data-start=\"3358\" data-end=\"3479\"><strong data-start=\"3360\" data-end=\"3479\">INTEGRATE con RK4 es una herramienta potente y precisa, siempre que se utilice con funciones puramente matem&aacute;ticas.&nbsp;<\/strong>No es una simulaci&oacute;n m&aacute;gica del futuro: es un estimador inteligente de una funci&oacute;n bien definida.<\/p>\r\n<p data-start=\"3248\" data-end=\"3258\">&nbsp;<\/p>\r\n<p data-start=\"475\" data-end=\"539\">&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "315",
                "nombre": "Movimiento con aceleración constante",
                "texto": "\/*\r\n\r\n Movimiento con aceleración constante\r\n \r\n*\/\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:5}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:moverCoche, INTERVAL: 0.01}\r\n\r\n; --- Variables iniciales ---\r\nINITIAL velocidad, 0.0            ; Velocidad angular\r\nINITIAL angulo, 0               ; Posición angular (en radianes)\r\nINITIAL aceleracion, 0.01       ; Aceleración angular constante\r\nINITIAL radio, 200              ; Radio del circuito circular\r\n\r\n; --- Funciones gráficas ---\r\nGraphic {NAME:txtVel, Type:TEXT, X:510, Y:120, TEXT:\"Velocidad:\"}\r\nGraphic {NAME:coche, Type:LINE, X1:-10, Y1:-10,  X2:-10, Y2:10,  X3:10, Y3:10,  X4:10, Y4:-10,  COLOR:#FF0000, close:1}\r\nGraphic {NAME:pista, Type:ARC, X:300, Y:300, RADIUS:X$radio, CLOSE:0, COLOR:#999999}\r\n\r\n; --- Gráfica de evolución ---\r\nPLOTTER {NAME:curva, Y_0:aceleracion,  Y_1:velocidad,X:TIME}\r\n\r\nSTART 500\r\n\r\n\r\n; --- Procedimiento de movimiento ---\r\nPROCEDURE moverCoche\r\n    IF (X$velocidad >= 4)\r\n        SAVEVALUE aceleracion, -0.1  ; Empieza a frenar\r\n    endif\r\n    if (X$velocidad <= 0.2)\r\n        SAVEVALUE aceleracion, 0.1   ; Vuelve a acelerar\r\n    ENDIF\r\n\r\n    ; Calcular incremento de velocidad\r\n    INTEGRATE {EXPRESSION: X$aceleracion, DT: 0.01, SAVEVALUE: deltaVel}\r\n\r\n    ; Acumular en velocidad total\r\n    SAVEVALUE velocidad, X$velocidad + X$deltaVel\r\n\r\n    ; Sumar velocidad al ángulo\r\n    SAVEVALUE angulo, X$angulo + X$velocidad * 0.05\r\n\r\n    ; Calcular posición\r\n    ASSIGN rad, X$angulo\r\n    ASSIGN posX, 300 + X$radio * COS(P$rad)\r\n    ASSIGN posY, 300 + X$radio * SIN(P$rad)\r\n\r\n    MOVE {NAME:coche, X:P$posX, Y:P$posY}\r\n\r\n    ASSIGN tVel, round(X$velocidad, 2)\r\n    MOVE {NAME:txtVel, TEXT: \"Velocidad: P$tVel rad\/s\"}\r\n\r\n\tPLOT curva, X$aceleracion, X$velocidad\r\nTERMINATE\r\nENDPROCEDURE\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Este ejemplo muestra c&oacute;mo simular el movimiento de un veh&iacute;culo en un circuito circular utilizando una aceleraci&oacute;n constante. Es el primer paso hacia la construcci&oacute;n de modelos f&iacute;sicos simples, como los que se emplean en videojuegos o simulaciones b&aacute;sicas de sistemas mec&aacute;nicos.<\/p>\r\n<h3 data-start=\"653\" data-end=\"672\">&iquest;Qu&eacute; se simula?<\/h3>\r\n<ul data-start=\"674\" data-end=\"1022\">\r\n    <li data-start=\"674\" data-end=\"717\">\r\n    <p data-start=\"676\" data-end=\"717\">El coche comienza detenido (velocidad 0).<\/p>\r\n    <\/li>\r\n    <li data-start=\"718\" data-end=\"843\">\r\n    <p data-start=\"720\" data-end=\"843\">Se le aplica una aceleraci&oacute;n constante positiva hasta alcanzar una velocidad m&aacute;xima (en este caso, 4 radianes por segundo).<\/p>\r\n    <\/li>\r\n    <li data-start=\"844\" data-end=\"971\">\r\n    <p data-start=\"846\" data-end=\"971\">Al llegar a ese l&iacute;mite, se invierte la aceleraci&oacute;n para que el coche frene suavemente hasta una velocidad m&iacute;nima (0.2 rad\/s).<\/p>\r\n    <\/li>\r\n    <li data-start=\"972\" data-end=\"1022\">\r\n    <p data-start=\"974\" data-end=\"1022\">Luego vuelve a acelerar... y el ciclo se repite.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"1024\" data-end=\"1045\">&iquest;C&oacute;mo se calcula?<\/h3>\r\n<p data-start=\"1047\" data-end=\"1128\">Se emplea el bloque <code data-start=\"1067\" data-end=\"1078\">INTEGRATE<\/code> para sumar la aceleraci&oacute;n en cada paso de tiempo:<\/p>\r\n<pre>\r\nINTEGRATE {EXPRESSION: X$aceleracion, DT: 0.1, SAVEVALUE: deltaVel}\r\n<\/pre>\r\n<p>En este caso, al ser una aceleraci&oacute;n constante:<\/p>\r\n<pre>\r\ndeltaVel = X$aceleracion * 0.1\r\ndeltaVel = &plusmn;0.01 &times; 0.1 = &plusmn;0.001<\/pre>\r\n<p>Esto nos da el incremento de velocidad en ese intervalo, y luego simplemente se acumula:<\/p>\r\n<pre>\r\nSAVEVALUE velocidad, X$velocidad + X$deltaVel<\/pre>\r\n<h3 data-start=\"1370\" data-end=\"1400\">Resultado visual y gr&aacute;fico<\/h3>\r\n<ul data-start=\"1402\" data-end=\"1699\">\r\n    <li data-start=\"1402\" data-end=\"1485\">\r\n    <p data-start=\"1404\" data-end=\"1485\">El coche se mueve suavemente en la pista circular, ganando y perdiendo velocidad.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1486\" data-end=\"1699\">\r\n    <p data-start=\"1488\" data-end=\"1541\">El gr&aacute;fico resultante (<code data-start=\"1511\" data-end=\"1520\">PLOTTER<\/code>) muestra dos curvas:<\/p>\r\n    <ul data-start=\"1544\" data-end=\"1699\">\r\n        <li data-start=\"1544\" data-end=\"1613\">\r\n        <p data-start=\"1546\" data-end=\"1613\"><strong data-start=\"1546\" data-end=\"1561\">Aceleraci&oacute;n<\/strong>: una onda cuadrada que alterna entre +0.01 y -0.01.<\/p>\r\n        <\/li>\r\n        <li data-start=\"1616\" data-end=\"1699\">\r\n        <p data-start=\"1618\" data-end=\"1699\"><strong data-start=\"1618\" data-end=\"1631\">Velocidad<\/strong>: una onda triangular que sube y baja en respuesta a la aceleraci&oacute;n.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"1701\" data-end=\"1905\">Este comportamiento es el t&iacute;pico de un sistema con control b&aacute;sico de velocidad, y es una base para la aceleraci&oacute;n variable, resistencia, o control adaptativo.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "316",
                "nombre": "Movimiento con aceleración variable",
                "texto": "\/*\r\n\r\n Movimiento con aceleración variable\r\n \r\n*\/\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:6}\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:moverCoche, INTERVAL: 0.1}\r\n\r\n; --- Variables iniciales ---\r\nINITIAL velocidad, 0.0 ; Velocidad angular\r\nINITIAL angulo, 0 ; Posición angular (en radianes)\r\nINITIAL aceleracion, 0.01 ; Aceleración angular\r\nINITIAL radio, 200 ; Radio del circuito circular\r\nINITIAL deltaA, 0.0002\r\n; --- Funciones gráficas ---\r\nGraphic {NAME:txtVel, Type:TEXT, X:510, Y:120, TEXT:\"Velocidad:\"}\r\nGraphic {NAME:coche, Type:LINE, X1:-10, Y1:-10, X2:-10, Y2:10, X3:10, Y3:10, X4:10, Y4:-10, COLOR:green, close:1}\r\nGraphic {NAME:pista, Type:ARC, X:300, Y:300, RADIUS:X$radio, CLOSE:0, COLOR:#999999}\r\n\r\n; --- Gráfica de evolución ---\r\nPLOTTER {NAME:curva, Y_0:aceleracion, Y_1:velocidad,X:TIME}\r\n\r\nSTART 500\r\n\r\n\r\n\r\nPROCEDURE moverCoche\r\n ;-------------------------------\r\n ; AJUSTE SUAVE DE LA ACELERACIÓN\r\n ;-------------------------------\r\n\r\n ; Control: si velocidad muy alta → reducir aceleración poco a poco\r\n IF (X$aceleracion > 0.04)\r\n SAVEVALUE deltaA, -0.003\r\n SAVEVALUE aceleracion, 0.04\r\n MOVE {name: coche,color:red}\r\n ENDIF\r\n\r\n IF (X$velocidad <= 0.0)\r\n SAVEVALUE deltaA, 0.0002\r\n SAVEVALUE aceleracion, 0\r\n SAVEVALUE velocidad, 0.0\r\n MOVE {name: coche,color:green}\r\n ENDIF\r\n\r\n\r\n ;---------------------------------\r\n ; ACTUALIZAR VELOCIDAD Y POSICIÓN\r\n ;---------------------------------\r\n \r\n ; Modificar aceleración suavemente según deltaA\r\n SAVEVALUE aceleracion, X$aceleracion + X$deltaA\r\n INTEGRATE {EXPRESSION: X$aceleracion, DT: 0.1, SAVEVALUE: deltaVel}\r\n SAVEVALUE velocidad, X$velocidad + X$deltaVel\r\n\r\n ; Actualizar ángulo (posición) según velocidad angular\r\n SAVEVALUE angulo, X$angulo + X$velocidad * 0.1\r\n\r\n ;--------------------------------\r\n ; CALCULAR POSICIÓN Y MOVER COCHE\r\n ;--------------------------------\r\n ASSIGN rad, X$angulo\r\n ASSIGN posX, 300 + X$radio * COS(P$rad)\r\n ASSIGN posY, 300 + X$radio * SIN(P$rad)\r\n MOVE {NAME:coche, X:P$posX, Y:P$posY}\r\n\r\n ;--------------------------\r\n ; MOSTRAR ESTADO Y PLOTTERS\r\n ;--------------------------\r\n ASSIGN tVel, round(X$velocidad, 2)\r\n ASSIGN tAcel, round(X$aceleracion, 4)\r\n MOVE {NAME:txtVel, TEXT: \"Velocidad: P$tVel rad\/s \\n Aceleración: P$tAcel\"}\r\n\r\n ; Gráficas para análisis\r\n PLOT curva, X$aceleracion, X$velocidad\r\nTERMINATE\r\nENDPROCEDURE\r\n\r\n\r\n\r\n\r\n\r\n",
                "descripcion": "<p data-start=\"232\" data-end=\"381\">En este ejemplo damos un paso m&aacute;s en la simulaci&oacute;n de movimiento circular, haciendo que la <strong data-start=\"323\" data-end=\"380\">aceleraci&oacute;n del veh&iacute;culo evolucione de forma din&aacute;mica<\/strong>.<\/p>\r\n<p data-start=\"383\" data-end=\"678\">Hasta ahora hab&iacute;amos utilizado una aceleraci&oacute;n constante o un cambio inmediato entre aceleraci&oacute;n positiva y negativa. Sin embargo, en el mundo real, un coche <strong data-start=\"541\" data-end=\"564\">acelera poco a poco<\/strong>, y <strong data-start=\"568\" data-end=\"592\">frena con m&aacute;s fuerza<\/strong>, sobre todo si deja de acelerar y retiene motor. Eso es lo que queremos modelar aqu&iacute;.<\/p>\r\n<h3 data-start=\"685\" data-end=\"704\">&iquest;Qu&eacute; modelamos?<\/h3>\r\n<p data-start=\"706\" data-end=\"732\">Simulamos un veh&iacute;culo que:<\/p>\r\n<ul data-start=\"734\" data-end=\"1020\">\r\n    <li data-start=\"734\" data-end=\"793\">\r\n    <p data-start=\"736\" data-end=\"793\"><strong data-start=\"736\" data-end=\"758\">Acelera lentamente<\/strong> cuando parte desde velocidad cero.<\/p>\r\n    <\/li>\r\n    <li data-start=\"794\" data-end=\"856\">\r\n    <p data-start=\"796\" data-end=\"856\"><strong data-start=\"796\" data-end=\"821\">Frena m&aacute;s bruscamente<\/strong> cuando alcanza una velocidad alta.<\/p>\r\n    <\/li>\r\n    <li data-start=\"857\" data-end=\"890\">\r\n    <p data-start=\"859\" data-end=\"890\">Circula por una pista circular.<\/p>\r\n    <\/li>\r\n    <li data-start=\"891\" data-end=\"946\">\r\n    <p data-start=\"893\" data-end=\"946\">Muestra gr&aacute;ficamente su posici&oacute;n y velocidad angular.<\/p>\r\n    <\/li>\r\n    <li data-start=\"947\" data-end=\"1020\">\r\n    <p data-start=\"949\" data-end=\"1020\">Registra la evoluci&oacute;n en dos gr&aacute;ficas: <strong data-start=\"988\" data-end=\"1003\">aceleraci&oacute;n<\/strong> y <strong data-start=\"1006\" data-end=\"1019\">velocidad<\/strong>.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"1027\" data-end=\"1058\">&iquest;Qu&eacute; elementos intervienen?<\/h3>\r\n<ul data-start=\"1060\" data-end=\"1550\">\r\n    <li data-start=\"1060\" data-end=\"1113\">\r\n    <p data-start=\"1062\" data-end=\"1113\"><code data-start=\"1062\" data-end=\"1075\">aceleracion<\/code>: valor actual de aceleraci&oacute;n angular.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1114\" data-end=\"1192\">\r\n    <p data-start=\"1116\" data-end=\"1192\"><code data-start=\"1116\" data-end=\"1124\">deltaA<\/code>: ritmo de cambio de la aceleraci&oacute;n (puede ser positivo o negativo).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1193\" data-end=\"1269\">\r\n    <p data-start=\"1195\" data-end=\"1269\"><code data-start=\"1195\" data-end=\"1206\">INTEGRATE<\/code>: calcula cu&aacute;nto cambia la velocidad con la aceleraci&oacute;n actual.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1270\" data-end=\"1332\">\r\n    <p data-start=\"1272\" data-end=\"1332\"><code data-start=\"1272\" data-end=\"1283\">velocidad<\/code>: cambia suavemente en funci&oacute;n de la aceleraci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1333\" data-end=\"1403\">\r\n    <p data-start=\"1335\" data-end=\"1403\"><code data-start=\"1335\" data-end=\"1343\">angulo<\/code>: se incrementa seg&uacute;n la velocidad, generando el movimiento.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1404\" data-end=\"1461\">\r\n    <p data-start=\"1406\" data-end=\"1461\"><code data-start=\"1406\" data-end=\"1413\">coche<\/code>: se mueve sobre la pista en funci&oacute;n del &aacute;ngulo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1462\" data-end=\"1550\">\r\n    <p data-start=\"1464\" data-end=\"1550\"><code data-start=\"1464\" data-end=\"1473\">PLOTTER<\/code>: representa c&oacute;mo var&iacute;an la aceleraci&oacute;n y la velocidad a lo largo del tiempo.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"1557\" data-end=\"1594\">&iquest;C&oacute;mo se gestiona la aceleraci&oacute;n?<\/h3>\r\n<ol data-start=\"1596\" data-end=\"1961\">\r\n    <li data-start=\"1596\" data-end=\"1711\">\r\n    <p data-start=\"1599\" data-end=\"1711\">Mientras la velocidad es baja, el sistema <strong data-start=\"1641\" data-end=\"1667\">incrementa poco a poco<\/strong> la aceleraci&oacute;n (<code data-start=\"1684\" data-end=\"1692\">deltaA<\/code> positivo y suave).<\/p>\r\n    <\/li>\r\n    <li data-start=\"1712\" data-end=\"1835\">\r\n    <p data-start=\"1715\" data-end=\"1835\">Cuando la aceleraci&oacute;n supera un umbral (por ejemplo, 0.04), se <strong data-start=\"1778\" data-end=\"1811\">invierte el signo de <code data-start=\"1801\" data-end=\"1809\">deltaA<\/code><\/strong> y se frena r&aacute;pidamente.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1836\" data-end=\"1961\">\r\n    <p data-start=\"1839\" data-end=\"1961\">Al llegar a velocidad cero, se reinicia el ciclo: se corta la aceleraci&oacute;n, se resetea <code data-start=\"1925\" data-end=\"1936\">velocidad<\/code>, y se vuelve a acelerar.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"1963\" data-end=\"2100\">De esta forma se obtiene un patr&oacute;n c&iacute;clico muy natural, parecido al de un motor t&eacute;rmico: <strong data-start=\"2052\" data-end=\"2099\">aceleraci&oacute;n progresiva y frenado m&aacute;s brusco<\/strong>.<\/p>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"2107\" data-end=\"2130\">Resultados gr&aacute;ficos<\/h3>\r\n<ul data-start=\"2132\" data-end=\"2391\">\r\n    <li data-start=\"2132\" data-end=\"2226\">\r\n    <p data-start=\"2134\" data-end=\"2226\">La <strong data-start=\"2137\" data-end=\"2152\">aceleraci&oacute;n<\/strong> forma una curva en dientes de sierra: sube lentamente, y cae r&aacute;pidamente.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2227\" data-end=\"2318\">\r\n    <p data-start=\"2229\" data-end=\"2318\">La <strong data-start=\"2232\" data-end=\"2245\">velocidad<\/strong> sigue una forma suave y ondulada: acelera poco a poco y baja m&aacute;s r&aacute;pido.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2319\" data-end=\"2391\">\r\n    <p data-start=\"2321\" data-end=\"2391\">El <strong data-start=\"2324\" data-end=\"2349\">coche cambia de color<\/strong>: verde cuando acelera, rojo cuando frena.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"383\" data-end=\"678\">&nbsp;<\/p>\r\n<p data-start=\"383\" data-end=\"678\">&nbsp;<\/p>\r\n<p data-start=\"383\" data-end=\"678\">&nbsp;<\/p>\r\n<p data-start=\"383\" data-end=\"678\">&nbsp;<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            },
            {
                "id": "268",
                "nombre": "Simulación híbrida",
                "texto": "\/*\r\n\r\n Simulación híbrida\r\n \r\n*\/\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:0, SPEED:5}         ; Tiempo con dos decimales y simulación visual lenta\r\nSYSTEM {TYPE:ON_TIMER, TRIGGER:cicloSolar, INTERVAL: 1}   ; Ejecutar procedimiento 'cicloSolar' cada 1 unidad de tiempo\r\n\r\n;--- Gráficas de resultados ---\r\nPLOTTER {NAME:bateriaPlot, Y_0:nivelBateria, Y_1:caudalSolar, X:TIME_AC1}    ; Curvas: nivel batería y caudal solar\r\n\r\n;--- Función aleatoria de decisión para usuarios ---\r\nFunction {Name:decisionCarga, TYPE:POISSON, LAMBDA:1\/4}                 ; Probabilidad de decidir cargar batería\r\n\r\n;--- Posiciones gráficas ---\r\nPosition {NAME:Pos1,X:431,Y:511}                           ; Entrada al punto de carga\r\nPosition {NAME:Pos2,X:690,Y:510}                           ; Salida\r\nPosition {NAME:PosRecarga,X:578,Y:173}                     ; Punto de recarga (visual)\r\n\r\n;--- Recurso de recarga de usuarios ---\r\nRestroom {NAME:Cargador,X:555,Y:360,R_BIN_SIZE:1,R_BIN_COUNT:40}  ; Permite 40 unidades de carga concurrentes\r\n\r\n;--- Indicadores visuales ---\r\nGraphic {NAME:tNivelBateria,Type:TEXT,X:329,Y:305,Text:\"tNivel\"}   ; Texto: nivel batería\r\nGraphic {NAME:gNivelCaudal,TYPE:ARC,X:411,Y:173,\r\nfCOLOR:#FFFF99,RADIUS:50,START_ANGLE:0,END_ANGLE:45,CLOSE:1} ; Semicírculo: intensidad solar\r\nGraphic {NAME:tCaudal,Type:TEXT,X:411,Y:173,Text:\"tCaudal\"}        ; Texto: valor de caudal\r\nGraphic {NAME:Cubo1,Type:LINE\r\n,POINTS:\"[100,100],[100,500],[ 200,500],[200,100]\"\r\n,Close:1,fcolor:#666666} ; Depósito batería\r\nGraphic {NAME:Line1,Type:LINE,color:#00FFFF, X1:98,Y1:100,X2:202,Y2:100}        ; Línea de nivel de batería\r\n\r\n;--- Variables iniciales ---\r\nINITIAL nivelBateria, 50\r\nINITIAL caudalSolar, 0\r\nINITIAL usoPorCarga, 15\r\n\r\nSTART 2000 \r\n\r\n;==============================\r\n; PROCEDIMIENTO CICLO SOLAR\r\n;==============================\r\nPROCEDURE cicloSolar\r\n    ; Caudal solar: función sinusoidal normalizada entre 0 y 1.5\r\n    INTEGRATE { EXPRESSION: \"0.75 + 0.75 * SIN((T \/ 1440) * 6.2832)\", DT: 1, SAVEVALUE: caudalSolar }\r\n\r\n    ; Incrementar batería (con límite 100)\r\n    SAVEVALUE nivelBateria, MIN(100, X$nivelBateria + X$caudalSolar)\r\n\r\n    ; Recorrer entidades conectadas al cargador\r\n    FOREACH NUMERO,IN_RESOURCE,Cargador\r\n        if (X$nivelBateria>1)\r\n            ; Aumentar batería interna del usuario\r\n            assign actual,P$(bateria,P$NUMERO) + 0.5\r\n            assign bateria,P$actual,P$NUMERO\r\n\r\n            ; Restar batería del sistema\r\n            savevalue nivelBateria,X$nivelBateria - 0.5\r\n\r\n            ; Si ya tiene batería suficiente, liberar al usuario\r\n            if (P$actual>X$usoPorCarga)\r\n                wake Cargador,0,P$NUMERO\r\n            endif\r\n        endif\r\n    ENDFOREACH\r\n\r\n    ; Gráfico solar: arco proporcional al caudal\r\n    ASSIGN anguloSol, round((X$caudalSolar \/ 1.5) * 360, 1)\r\n    MOVE {NAME: gNivelCaudal, END_ANGLE: P$anguloSol}\r\n\r\n    ; Actualizar línea visual del nivel de batería\r\n    MOVE {NAME:Line1, X1:98,Y1:(100+X$nivelBateria*4) ,X2:202,Y2:(100+X$nivelBateria*4)}\r\n\r\n    ; Mostrar valores redondeados\r\n    assign tnivelBateriaRound,round(X$nivelBateria,2)\r\n    assign tCaudalRound,round(X$caudalSolar,2)\r\n    move {name:tNivelBateria,text:\"Nivel actual: P$tNivelBateriaRound\"}\r\n    move {name:tCaudal,text:\"Caudal: P$tCaudalRound \/ 1.5\"}\r\n\r\n    ; Guardar datos para informe\r\n    PLOT bateriaPlot, X$nivelBateria, X$caudalSolar\r\nTERMINATE\r\nENDPROCEDURE 1\r\n\r\n;==============================\r\n; USUARIOS\r\n;==============================\r\nGENERATE 5,5 {NAME:userGen, X:294, Y:514, ECOLOR:#000000} ; Llega un usuario cada ~5 unidades\r\n\r\nassign bateria,X$usoPorCarga                 ; Define su necesidad de carga\r\n\r\nASSIGN decision, FN$decisionCarga           ; Decide si necesita recargar (Poisson)\r\nADVANCE 10 {to:Pos1}                        ; Tiempo de conexión\r\n\r\nIF (P$decision>=1)\r\n    assign bateria,0                        ; Inicializa contador de carga\r\n    ADVANCE 10 {to:Cargador}                ; Se mueve al punto de carga\r\n    rest Cargador                           ; Espera a estar cargado\r\nENDIF\r\n\r\nADVANCE 10 {to:Pos2}                        ; Sale\r\nTERMINATE 1\r\n\r\n\r\n",
                "descripcion": "<p>En los cap&iacute;tulos anteriores vimos c&oacute;mo simular el llenado de un dep&oacute;sito con funciones continuas, c&oacute;mo registrar su evoluci&oacute;n con gr&aacute;ficos (PLOTTER) y c&oacute;mo mejorar la precisi&oacute;n usando integraci&oacute;n num&eacute;rica (INTEGRATE con RK4). Ahora es el momento de integrar todo ese conocimiento en un sistema <strong data-start=\"727\" data-end=\"750\">realista y complejo<\/strong>, en el que lo continuo y lo discreto conviven.<\/p>\r\n<h4 data-start=\"799\" data-end=\"851\">Ejemplo: estaci&oacute;n solar con usuarios conectados<\/h4>\r\n<p data-start=\"853\" data-end=\"910\">Este ejemplo representa un <strong data-start=\"880\" data-end=\"899\">sistema h&iacute;brido<\/strong> en el que:<\/p>\r\n<ul data-start=\"912\" data-end=\"1053\">\r\n    <li data-start=\"912\" data-end=\"967\">\r\n    <p data-start=\"914\" data-end=\"967\">Hay una <strong data-start=\"922\" data-end=\"949\">fuente de energ&iacute;a solar<\/strong> (modelo continuo)<\/p>\r\n    <\/li>\r\n    <li data-start=\"968\" data-end=\"1053\">\r\n    <p data-start=\"970\" data-end=\"1053\">Hay <strong data-start=\"974\" data-end=\"996\">usuarios discretos<\/strong> que llegan, deciden cargar y se conectan si lo necesitan<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"1128\" data-end=\"1164\">Parte continua: generaci&oacute;n solar<\/h3>\r\n<p data-start=\"1166\" data-end=\"1413\">La energ&iacute;a solar se modela como una funci&oacute;n senoidal que simula el paso del d&iacute;a: una oscilaci&oacute;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&ntilde;ade energ&iacute;a a la bater&iacute;a.<\/p>\r\n<pre>\r\nINTEGRATE { \r\n&nbsp; EXPRESSION: &quot;0.75 + 0.75 * SIN((T \/ 1440) * 6.2832)&quot;,&nbsp;\r\n&nbsp; DT: 1,&nbsp;\r\n&nbsp; SAVEVALUE: caudalSolar&nbsp;\r\n}<\/pre>\r\n<p data-start=\"1536\" data-end=\"1694\">Esta expresi&oacute;n genera una onda suave con un m&aacute;ximo de 1.5 y un m&iacute;nimo de 0, centrada en 0.75. El resultado se suma al nivel de bater&iacute;a hasta un m&aacute;ximo de 100.<\/p>\r\n<p data-start=\"1696\" data-end=\"1750\">Adem&aacute;s, un <strong data-start=\"1707\" data-end=\"1718\">PLOTTER<\/strong> recoge los datos en cada ciclo:<\/p>\r\n<pre>\r\nPLOTTER {NAME:bateriaPlot, Y_0:nivelBateria, Y_1:caudalSolar, X:TIME_AC1}<\/pre>\r\n<p data-start=\"1696\" data-end=\"1750\">As&iacute; se podr&aacute; ver la evoluci&oacute;n de la bater&iacute;a y el caudal solar durante todo el d&iacute;a.<\/p>\r\n<h3 data-start=\"1928\" data-end=\"1976\">Parte discreta: usuarios que solicitan carga<\/h3>\r\n<p data-start=\"1978\" data-end=\"2163\">Mientras tanto, los usuarios llegan de forma aleatoria usando <code data-start=\"2040\" data-end=\"2050\">GENERATE<\/code>. Cada uno decide si necesita cargar su bater&iacute;a, usando una <strong data-start=\"2110\" data-end=\"2137\">funci&oacute;n de tipo Poisson<\/strong> para modelar la decisi&oacute;n:<\/p>\r\n<pre>\r\nFunction {Name:decisionCarga, TYPE:POISSON, LAMBDA:1\/3}\r\n<\/pre>\r\n<p data-start=\"2234\" data-end=\"2252\">Si deciden cargar:<\/p>\r\n<ol data-start=\"2254\" data-end=\"2599\">\r\n    <li data-start=\"2254\" data-end=\"2291\">\r\n    <p data-start=\"2257\" data-end=\"2291\">Se mueven hasta el punto de carga.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2292\" data-end=\"2379\">\r\n    <p data-start=\"2295\" data-end=\"2379\">Se conectan al recurso <code data-start=\"2318\" data-end=\"2335\">RESTROOM cargador<\/code>, que simula un sistema de carga simult&aacute;nea.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2380\" data-end=\"2486\">\r\n    <p data-start=\"2383\" data-end=\"2486\">Permanecen dentro del <code>RESTROOM<\/code>, mientras la bater&iacute;a central les transfiere energ&iacute;a (si tiene suficiente).<\/p>\r\n    <\/li>\r\n    <li data-start=\"2487\" data-end=\"2599\">\r\n    <p data-start=\"2490\" data-end=\"2599\">Cuando alcanzan su nivel de carga requerido (<code data-start=\"2535\" data-end=\"2548\">usoPorCarga<\/code>), son liberados autom&aacute;ticamente mediante <code data-start=\"2590\" data-end=\"2598\">WAKE<\/code>.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<h3 data-start=\"2606\" data-end=\"2638\">Coordinaci&oacute;n de ambos mundos<\/h3>\r\n<p data-start=\"2640\" data-end=\"2697\">Cada vez que se ejecuta el ciclo de carga (<code data-start=\"2683\" data-end=\"2695\">cicloSolar<\/code>):<\/p>\r\n<ul data-start=\"2699\" data-end=\"2966\">\r\n    <li data-start=\"2699\" data-end=\"2734\">\r\n    <p data-start=\"2701\" data-end=\"2734\">Se calcula el nuevo caudal solar.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2735\" data-end=\"2783\">\r\n    <p data-start=\"2737\" data-end=\"2783\">Se aumenta el nivel de bater&iacute;a con ese caudal.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2784\" data-end=\"2893\">\r\n    <p data-start=\"2786\" data-end=\"2893\">Se recorre la lista de usuarios conectados (<code data-start=\"2830\" data-end=\"2839\">FOREACH<\/code>) y se les transfiere carga si hay energ&iacute;a disponible.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2894\" data-end=\"2966\">\r\n    <p data-start=\"2896\" data-end=\"2966\">Si un usuario ya ha sido cargado completamente, se libera del recurso.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2968\" data-end=\"3009\">Visualmente, todo esto se representa con:<\/p>\r\n<ul data-start=\"3011\" data-end=\"3195\">\r\n    <li data-start=\"3011\" data-end=\"3071\">\r\n    <p data-start=\"3013\" data-end=\"3071\">Un <strong data-start=\"3016\" data-end=\"3028\">dep&oacute;sito<\/strong> que sube y baja seg&uacute;n el nivel de bater&iacute;a.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3072\" data-end=\"3128\">\r\n    <p data-start=\"3074\" data-end=\"3128\">Un <strong data-start=\"3077\" data-end=\"3085\">arco<\/strong> que representa la intensidad solar actual.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3129\" data-end=\"3195\">\r\n    <p data-start=\"3131\" data-end=\"3195\">Un gr&aacute;fico de evoluci&oacute;n con los datos recolectados en cada paso.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"3202\" data-end=\"3235\">Visualizaci&oacute;n e informe final<\/h3>\r\n<p data-start=\"3237\" data-end=\"3314\">Al finalizar la simulaci&oacute;n, el gr&aacute;fico <code data-start=\"3276\" data-end=\"3289\">bateriaPlot<\/code> muestra la evoluci&oacute;n de:<\/p>\r\n<ul data-start=\"3316\" data-end=\"3381\">\r\n    <li data-start=\"3316\" data-end=\"3350\">\r\n    <p data-start=\"3318\" data-end=\"3350\"><strong data-start=\"3318\" data-end=\"3338\">Nivel de bater&iacute;a<\/strong> (serie Y_0)<\/p>\r\n    <\/li>\r\n    <li data-start=\"3351\" data-end=\"3381\">\r\n    <p data-start=\"3353\" data-end=\"3381\"><strong data-start=\"3353\" data-end=\"3369\">Caudal solar<\/strong> (serie Y_1)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3383\" data-end=\"3406\">Se puede observar c&oacute;mo:<\/p>\r\n<ul data-start=\"3408\" data-end=\"3714\">\r\n    <li data-start=\"3408\" data-end=\"3475\">\r\n    <p data-start=\"3410\" data-end=\"3475\">La bater&iacute;a se recarga durante el d&iacute;a y se vac&iacute;a durante la noche.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3476\" data-end=\"3574\">\r\n    <p data-start=\"3478\" data-end=\"3574\">Si hay muchos usuarios conectados, la bater&iacute;a puede agotarse antes de que vuelva a salir el sol.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3575\" data-end=\"3714\">\r\n    <p data-start=\"3577\" data-end=\"3714\">El n&uacute;mero de usuarios simult&aacute;neos conectados se puede monitorizar con un contador cl&aacute;sico (por ejemplo, <code data-start=\"3681\" data-end=\"3700\">TABULATE cargador<\/code> si se desea).<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<h3 data-start=\"3721\" data-end=\"3735\">Conclusi&oacute;n<\/h3>\r\n<p data-start=\"3737\" data-end=\"3832\">Este tipo de simulaci&oacute;n es lo que se denomina un <strong data-start=\"3786\" data-end=\"3822\">modelo h&iacute;brido continuo\/discreto<\/strong>. Combina:<\/p>\r\n<ul data-start=\"3834\" data-end=\"3964\">\r\n    <li data-start=\"3834\" data-end=\"3884\">\r\n    <p data-start=\"3836\" data-end=\"3884\">Una evoluci&oacute;n continua del entorno (como el sol)<\/p>\r\n    <\/li>\r\n    <li data-start=\"3885\" data-end=\"3964\">\r\n    <p data-start=\"3887\" data-end=\"3964\">Una l&oacute;gica discreta de eventos, decisiones y ocupaci&oacute;n de recursos (usuarios)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3966\" data-end=\"4110\">Este tipo de modelado es ideal para simular:<\/p>\r\n<ul data-start=\"4158\" data-end=\"4313\">\r\n    <li data-start=\"4158\" data-end=\"4186\">\r\n    <p data-start=\"4160\" data-end=\"4186\">Energ&iacute;a y redes el&eacute;ctricas<\/p>\r\n    <\/li>\r\n    <li data-start=\"4187\" data-end=\"4231\">\r\n    <p data-start=\"4189\" data-end=\"4231\">Producci&oacute;n intermitente y consumo variable<\/p>\r\n    <\/li>\r\n    <li data-start=\"4232\" data-end=\"4264\">\r\n    <p data-start=\"4234\" data-end=\"4264\">Procesos biol&oacute;gicos o qu&iacute;micos<\/p>\r\n    <\/li>\r\n    <li data-start=\"4265\" data-end=\"4313\">\r\n    <p data-start=\"4267\" data-end=\"4313\">Infraestructuras compartidas con restricciones<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"4315\" data-end=\"4474\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": "",
                "parametros_json": "",
                "ejemplo": "",
                "hijos": []
            }
        ]
    },
    {
        "id": "377",
        "nombre": "Season 11: Sistemas No Lineales",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "317",
                "nombre": "Sistemas No Lineales",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "318",
                        "nombre": "Sistemas No Lineales",
                        "texto": "\/*\r\n\r\n Sistemas No Lineales\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     \r\n\r\nPLOTTER {NAME:presiones, Y_0:tank1,  Y_1:tank2,  X:TIME_AC1}   \r\nPLOTTER {NAME:flujo, Y_0:flujo, X:TIME_AC1}   \r\n\r\nGraphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:\"tTexto\"}   ; Texto: nivel batería\r\n\r\nINITIAL TANK1_PRESION, 10\r\nINITIAL TANK2_PRESION, 1\r\nINITIAL K, 0.1\r\n\r\nINITIAL dynamic_config, {\r\n    EXPRESSIONS: [\r\n        \"PIPE_FLUJO + PIPE_K * (TANK1_PRESION - TANK2_PRESION)\",\r\n        \"TANK1_PRESION - TANK1_PRESION_PREV - PIPE_FLUJO * DT\",\r\n        \"TANK2_PRESION - TANK2_PRESION_PREV + PIPE_FLUJO * DT\"\r\n      ],\r\n      STATES: [    \"PIPE_FLUJO\",    \"TANK1_PRESION\",    \"TANK2_PRESION\"  ],\r\n      VARIABLES: [    \"PIPE_K\",    \"TANK1_PRESION_PREV\",    \"TANK2_PRESION_PREV\" ]\r\n    }  \r\n\r\nINITIAL dynamic_values, { PIPE_K: X$K\r\n\t\t\t, TANK1_PRESION_PREV:X$TANK1_PRESION\r\n            , TANK2_PRESION_PREV:X$TANK2_PRESION \r\n            }\r\n\r\nDYNAMIC {name:sys\r\n\t,CONFIG:V$dynamic_config\r\n    ,VALUES:V$dynamic_values\r\n\t,X:300,Y:300\r\n    , TOLERANCE: 1e-6,  MAX_ITER: 10\r\n}\r\n\r\nSTART 1000 \r\n\r\ninclude .\/library_graphics\/tank.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n\r\ntimeout cambiar_deposito,10\r\n    advance 0.1\r\nwhile (1==1)\r\nmove {name:tTexto, text:\"flujo: P$rFlujo Tot: T1: P$rH1  T2: P$rH2 \"}\r\n\r\nSOLVE {  name:\"sys\",  DT: 0.1,  SAVEVALUE: \"resultado\"}\r\n\r\n    SAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)\r\n    SAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)\r\n    SAVEVALUE PIPE_FLUJO,  X$(resultado.PIPE_FLUJO)\r\n\r\n    PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION\r\n    PLOT flujo, X$PIPE_FLUJO\r\n\r\n\tassign rFlujo,round(X$PIPE_FLUJO,3)\r\n\tassign rH1,round(X$TANK1_PRESION,3)\r\n\tassign rH2,round(X$TANK2_PRESION,3)\r\n    \r\n    move {name:tTexto, text:\"flujo: P$rFlujo Tot: T1: P$rH1  T2: P$rH2 \"}\r\n\r\n\tCALL tank1.tank.set, P$rH1\r\n\tCALL tank2.tank.set, P$rH2\r\n\r\n\r\n    IF (ABS(X$PIPE_FLUJO) < 0.001)\r\n        stop\r\n    ENDIF\r\n\r\n\r\n\r\nadvance 0.1,0\r\nendwhile\r\n\r\nstop\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    call crear_depositos\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_depositos\r\n\r\n\tassign config,{title:\"TANK 1\"\r\n    \t\t,x:100,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\ttimeout tank1.tank.init,0,V$config\r\n\r\n\tassign config,{title:\"TANK 2\"\r\n    \t\t,x:400,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\ttimeout tank2.tank.init,0,V$config\r\n\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\nPROCEDURE cambiar_deposito\r\n\r\n    assign nuevosParams,{TANK2_PRESION_PREV:10}\r\n    dynamic_set sys,V$nuevosParams\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n",
                        "descripcion": "<h3><span>&iquest;Qu&eacute; es un sistema no lineal?<\/span><\/h3>\r\n<p><span>Los sistemas lineales son aquellos que se resuelven con ecuaciones proporcionales y directas: si duplicas la entrada, se duplica la salida. Un ejemplo cl&aacute;sico es:<\/span><\/p>\r\n<p><i><span>El padre tiene el doble de edad que su hijo, y el hijo es 20 a&ntilde;os menor que el padre.<\/span><\/i><\/p>\r\n<p><span>Este tipo de sistemas se resuelven paso a paso, de forma secuencial o lineal.<\/span><\/p>\r\n<p><span>Pero en la realidad, muchos sistemas son <\/span><span><strong>no lineales<\/strong><\/span><span>, es decir, las variables est&aacute;n tan interrelacionadas que no pueden resolverse por separado. Requieren ser resueltas <\/span><span><strong>simult&aacute;neamente<\/strong><\/span><span>.<\/span><\/p>\r\n<p><b>Ejemplo: Dep&oacute;sitos de agua unidos por un tubo<\/b><\/p>\r\n<p data-pm-slice=\"1 1 []\"><span>Supongamos dos dep&oacute;sitos de agua, uno con 10 litros y otro con 5 litros, unidos por un tubo.<\/span><\/p>\r\n<p><span>Podr&iacute;amos escribir:<\/span><\/p>\r\n<pre>\r\nvolumen_A_Nuevo = volumen_A - flujo * DT ; DT es el incremento de tiempo\r\nvolumen_B_Nuevo = volumen_B + flujo * DT\r\nflujo = K * (volumen_A - volumen_B)      ; K es una constante sobre la capacidad de fluir del tubo<\/pre>\r\n<p data-pm-slice=\"1 3 []\"><span>Donde:<\/span><\/p>\r\n<ul>\r\n    <li><code><span>K<\/span><\/code><span> es una constante que define la permeabilidad del tubo<\/span><\/li>\r\n    <li><code><span>DT<\/span><\/code><span> es el paso de tiempo (delta t)<\/span><\/li>\r\n<\/ul>\r\n<p data-pm-slice=\"1 1 []\"><span>Esto puede funcionar bien para dos dep&oacute;sitos. Pero si conectamos un tercer dep&oacute;sito en serie, el sistema ya no responde correctamente. A veces, el dep&oacute;sito central parece no fluir hacia el tercero, a pesar de tener diferencia de presi&oacute;n. Si intentamos ver la cantidad de flujo en el instante 0 entre el segundo y tercer dep&oacute;sito ser&iacute;a 0 mientras que en realidad, s&iacute; hay flujo.<\/span><\/p>\r\n<p data-pm-slice=\"1 1 []\"><span>Eso se debe a que las ecuaciones est&aacute;n <\/span><span><strong>acopladas<\/strong><\/span><span>. Resolverlas por separado no funciona.<\/span><\/p>\r\n<p>&nbsp;<\/p>\r\n<h3><span>Resolver sistemas no lineales<\/span><\/h3>\r\n<p><span>La soluci&oacute;n es tratarlos como un conjunto de ecuaciones no lineales y resolverlas todas juntas.<\/span><\/p>\r\n<p><span>GPSS-Plus automatiza esto mediante el recurso <code>DINAMIC <\/code>que en cierto modo se parece a una Facility pero en lugar de gestionar entidades y colas, gestiona valores matem&aacute;ticos.<\/span><\/p>\r\n<p><span>Este recurso implementa:<\/span><\/p>\r\n<ul>\r\n    <li><span><strong>Matrices Jacobianas<\/strong><\/span><span> (para linealizar el sistema en cada iteraci&oacute;n)<\/span><\/li>\r\n    <li><span><strong>M&eacute;todo de Newton-Raphson<\/strong><\/span><span> (para encontrar la soluci&oacute;n iterativamente)<\/span><\/li>\r\n<\/ul>\r\n<h3 data-pm-slice=\"1 1 []\"><span>Declaraci&oacute;n t&iacute;pica:<\/span><\/h3>\r\n<pre>\r\nINITIAL dynamic_config, {\r\n  EXPRESSIONS: [\r\n&nbsp; &nbsp; &quot;PIPE_FLUJO + PIPE_K * (TANK1_PRESION - TANK2_PRESION)&quot;,\r\n&nbsp; &nbsp; &quot;TANK1_PRESION - TANK1_PRESION_PREV - PIPE_FLUJO * DT&quot;,\r\n&nbsp; &nbsp; &quot;TANK2_PRESION - TANK2_PRESION_PREV + PIPE_FLUJO * DT&quot;\r\n&nbsp; ],\r\n&nbsp; STATES: [&quot;PIPE_FLUJO&quot;, &quot;TANK1_PRESION&quot;, &quot;TANK2_PRESION&quot;],\r\n&nbsp; VARIABLES: [&quot;PIPE_K&quot;, &quot;TANK1_PRESION_PREV&quot;, &quot;TANK2_PRESION_PREV&quot;]\r\n}&nbsp;&nbsp;\r\n\r\nINITIAL dynamic_values, {\r\n&nbsp; PIPE_K: 0.1,\r\n&nbsp; TANK1_PRESION_PREV: 10,\r\n&nbsp; TANK2_PRESION_PREV: 1\r\n}\r\n\r\nDYNAMIC {\r\n&nbsp; name: sys,\r\n&nbsp; CONFIG: V$dynamic_config,\r\n&nbsp; VALUES: V$dynamic_values,\r\n&nbsp; X: 300,\r\n&nbsp; Y: 300,\r\n&nbsp; TOLERANCE: 1e-6, ; valor por defecto\r\n&nbsp; MAX_ITER: 10     ; valor por defecto\r\n}<\/pre>\r\n<p>El bloque <code data-start=\"2737\" data-end=\"2746\">DYNAMIC<\/code> permite declarar:<\/p>\r\n<ul>\r\n    <li><code data-start=\"2769\" data-end=\"2782\">EXPRESSIONS<\/code>: las ecuaciones del sistema<\/li>\r\n    <li><code data-start=\"2815\" data-end=\"2823\">STATES<\/code>: variables a resolver (como presi&oacute;n, velocidad, temperatura)<\/li>\r\n    <li><code data-start=\"2889\" data-end=\"2900\">VARIABLES<\/code>: par&aacute;metros auxiliares (constantes o valores anteriores)<\/li>\r\n<\/ul>\r\n<p><b><span>El sufijo <\/span><code><span>_PREV<\/span><\/code><span>&nbsp;en las variables se gestiona autom&aacute;ticamente: GPSS-Plus actualiza su valor en cada iteraci&oacute;n.<\/span><\/b><\/p>\r\n<h3 data-pm-slice=\"1 1 []\"><span>Resoluci&oacute;n con <\/span><code><span>SOLVE:<\/span><\/code><code><\/code><\/h3>\r\n<pre>\r\nSOLVE {\r\n&nbsp; name: &quot;sys&quot;,\r\n&nbsp; DT: 0.1,\r\n&nbsp; SAVEVALUE: &quot;resultado&quot;\r\n}\r\n<\/pre>\r\n<h2 data-pm-slice=\"1 1 []\"><span>Ejecuci&oacute;n t&iacute;pica:<\/span><\/h2>\r\n<pre>\r\nPROCEDURE agente.init\r\n\r\n  timeout cambiar_deposito, 10 ; si se desea cambiar alg&uacute;n par&aacute;metro en tiempo de ejecuci&oacute;n\r\n\r\n  while (1==1)\r\n\r\n    SOLVE { name: &quot;sys&quot;, DT: 0.1, SAVEVALUE: &quot;resultado&quot; }\r\n\r\n    SAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)\r\n    SAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)\r\n    SAVEVALUE PIPE_FLUJO,    X$(resultado.PIPE_FLUJO)\r\n\r\n    PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION\r\n    PLOT flujo, X$PIPE_FLUJO\r\n\r\n    IF (ABS(X$PIPE_FLUJO) &lt; 0.001)\r\n      stop\r\n    ENDIF\r\n\r\n    advance 0.1, 0\r\n  endwhile\r\n\r\n  stop\r\nENDPROCEDURE<\/pre>\r\n<p data-start=\"2532\" data-end=\"2609\">Eventualmente, se pueden alterar los valores de <code>VARIABLES <\/code>con el uso de <code>DYNAMIC_SET<\/code>:<\/p>\r\n<pre>\r\n    assign nuevosParams,{TANK2_PRESION_PREV:10}\r\n    dynamic_set sys,V$nuevosParams\r\n<\/pre>\r\n<p>En la secci&oacute;n del depuraci&oacute;n se peude observar c&oacute;mo cambian los valores de <code>STATES <\/code>y la configuraci&oacute;n completa del <code>DYNAMIC<\/code>.<\/p>\r\n<p>El resultado del <code>PLOTTER <\/code>es esclarecedor y muestra claramente c&oacute;mo se equilibran los niveles de los dep&oacute;sitos con el paso del tiempo.&nbsp;<\/p>\r\n<h2 data-pm-slice=\"1 3 []\">&nbsp;<\/h2>\r\n<h2 data-pm-slice=\"1 3 []\"><span>En resumen<\/span><\/h2>\r\n<ul>\r\n    <li><span>En sistemas acoplados, como el flujo entre m&uacute;ltiples dep&oacute;sitos, necesitamos resolver ecuaciones <\/span><span><strong>simult&aacute;neamente<\/strong><\/span><span>.<\/span><\/li>\r\n    <li><code><span>DYNAMIC<\/span><\/code><span> permite modelar esos sistemas f&iacute;sicos con expresiones simples.<\/span><\/li>\r\n    <li><span>GPSS-Plus automatiza la actualizaci&oacute;n de estados, la integraci&oacute;n en el tiempo, y el control de convergencia.<\/span><\/li>\r\n<\/ul>\r\n<p><span>Con esto, puedes modelar f&aacute;cilmente fen&oacute;menos como:<\/span><\/p>\r\n<ul>\r\n    <li><span>Equilibrio hidr&aacute;ulico<\/span><\/li>\r\n    <li><span>Transferencia de calor<\/span><\/li>\r\n    <li><span>Corriente el&eacute;ctrica<\/span><\/li>\r\n    <li><span>Sistemas acoplados de cualquier tipo<\/span><\/li>\r\n<\/ul>\r\n<p data-start=\"2532\" data-end=\"2609\">&nbsp;<\/p>\r\n<p data-start=\"2532\" data-end=\"2609\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                        "parametros": "",
                        "parametros_json": "",
                        "ejemplo": "",
                        "hijos": []
                    },
                    {
                        "id": "323",
                        "nombre": "Clásico masa-muelle-amortiguador",
                        "texto": "\/*\r\n\r\n Masa-muelle-amortiguador\r\n \r\n*\/\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:30, CAMERA:0}\r\nUI {TYPE: BUTTON, id:botonA,TEXT: \"Pulsar\", LABEL: \"Pulsar\", TRIGGER: Pulsar}\r\nUI {\r\n  TYPE: SLIDER, ID: unSlider, LABEL: \"Velocidad\",\r\n  VALUE: 15, MIN: 1, MAX: 50, STEP: 1,\r\n  TRIGGER: capturarVelocidad\r\n}\r\n\r\nPLOTTER {NAME:V_X, Y_0:V,  Y_1:X,  X:TIME_AC1}   \r\n\r\nGraphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}\r\nGraphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}\r\nGraphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}\r\n\r\nGraphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:\"tTexto\"} \r\nGRAPHIC {NAME:muelle, TYPE:OBJECT, src:SPRING, X:0, Y:15, Z:0, DEPTH:10, width:10, height:10, opacity:0.6}\r\n\r\n\r\nGRAPHIC {NAME:masa, TYPE:SPHERE, X:0, Y:3, Z:0, radius:3, color:red}\r\n\r\nGRAPHIC {NAME:amortiguador, TYPE:BOX, X:0, Y:0, Z:0, WIDTH:2, HEIGHT:20, DEPTH:2, color:blue}\r\nGRAPHIC {NAME:amortiguadorB, TYPE:BOX, X:0, Y:10, Z:0, WIDTH:1.6, HEIGHT:20, DEPTH:1.6, color:cyan}\r\n; FIJACIÓN inferior (visual)\r\nGRAPHIC {NAME:soporte, TYPE:BOX, X:0, Y:-1, Z:0, WIDTH:20, HEIGHT:2, DEPTH:20, color:yellow}\r\n\r\n\r\n\r\nINITIAL mech_config, {\r\n  EXPRESSIONS: [\r\n    \"F_spring + F_damper + F_inertial\",       ; suma de fuerzas = 0 (equilibrio dinámico)\r\n\r\n    \"F_spring - K * X\",                       ; resorte\r\n    \"F_damper - C * V\",                       ; amortiguador\r\n    \"F_inertial - M * (V - V_PREV) \/ DT\",     ; fuerza de inercia (aceleración)\r\n    \r\n    \"X - X_PREV - DT * V\"                     ; integración explícita: posición\r\n  ],\r\n  STATES: [\r\n    \"X\",           ; posición\r\n    \"V\",           ; velocidad\r\n    \"F_spring\",    ; fuerza del muelle\r\n    \"F_damper\",    ; fuerza de amortiguamiento\r\n    \"F_inertial\"   ; fuerza de inercia\r\n  ],\r\n  VARIABLES: [\r\n    \"K\", \"C\", \"M\", \"X_PREV\", \"V_PREV\"\r\n  ]\r\n}\r\n\r\nINITIAL mech_values, {\r\n  K: 20,          ; constante del muelle\r\n  C: 0.005,         ; coef. de amortiguamiento\r\n  M: 1,           ; masa\r\n  X_PREV: 0,      ; posición inicial\r\n  V_PREV: 30       ; velocidad inicial\r\n}\r\n\r\nDYNAMIC {\r\n  name: sys,\r\n  CONFIG: V$mech_config,\r\n  VALUES: V$mech_values,\r\n  X: 300,\r\n  Y: 300,\r\n  TOLERANCE: 1e-6,\r\n  MAX_ITER: 10\r\n}\r\n\r\n\r\nSTART 1000 \r\n\r\ninclude .\/library_graphics\/speedometer.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n    \r\nwhile (1==1)\r\n\r\n    SOLVE {  name:\"sys\",  DT: 0.05,  SAVEVALUE: \"resultado\"}\r\n\r\n    SAVEVALUE V, X$(resultado.V)\r\n    SAVEVALUE X, X$(resultado.X)\r\n    SAVEVALUE I_cap ,  X$(resultado.I_cap)\r\n\r\n    assign x_masa,30 + X$X\r\n    ; === Posición de la masa\r\n    MOVE {name:masa, Y:P$x_masa}\r\n\r\n    ; === Estirar muelle desde base [0,0,0] hasta la masa\r\n    MOVE {name:muelle, STRETCH_BETWEEN:\"[0,0,0],[0,P$x_masa,0]\",rotate_y:P$x_masa*200}\r\n\r\n    ; === Estirar amortiguador desde base también\r\n    MOVE {name:amortiguador, MOVE_BETWEEN:\"[0,0,0],[0,P$x_masa,0]\",y:P$x_masa -10}\r\n\r\n\r\n    PLOT V_X, X$V, X$X\r\n\r\n\tassign rX,round(X$X,3)\r\n\tassign rV,round(X$V,3)\r\n    \r\n\tCALL tank1.speedometer.set, P$rV\r\n\tCALL tank2.speedometer.set, P$rX\r\n\r\n\r\n    IF (ABS(X$V) < 0.001)\r\n        stop\r\n    ENDIF\r\n\r\nadvance 0.05,0\r\n\r\nendwhile\r\n\r\nstop\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    CALL crear_indicadores\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_indicadores\r\n\r\n\tassign config,{title:\"V\"\r\n    \t\t,x:20,y:15 \r\n            ,width:5 ,height:18\r\n            ,value:0\r\n            ,min_value:-30\r\n            ,max_value:30\r\n            ,\"color\":\"#ff0000\"\r\n            ,font:\"4px\"}\r\n            \r\n\tcall tank1.speedometer.init,V$config\r\n\r\n\tassign config,{title:\"X\"\r\n    \t\t,x:50,y:15 \r\n            ,width:5 ,height:18\r\n            ,value:0\r\n            ,min_value:-10\r\n            ,max_value:10\r\n            ,\"color\":\"blue\"\r\n            ,font:\"4px\"}\r\n            \r\n\tcall tank2.speedometer.init,V$config\r\n\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\nPROCEDURE Pulsar\r\n\tsavevalue velocidad_ui,max(X$velocidad_ui,15)\r\n    assign nuevosParams,{V_PREV:-X$velocidad_ui}\r\n    dynamic_set sys,V$nuevosParams\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nPROCEDURE capturarVelocidad\r\n\r\n    savevalue velocidad_ui,P$PARAM_B\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n\r\n",
                        "descripcion": "<p>Vamos a ver c&oacute;mo se resuelve este cl&aacute;sico que solo requiere de las ecuaciones y posicionar correctamente la masa con respecto a los componentes.<\/p>\r\n<p>El ejemplo muestra c&oacute;mo usar los gr&aacute;ficos 3D.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "374",
                "nombre": "No Lineales Componentes",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "319",
                        "nombre": "Por componentes: COMPOSITOR",
                        "texto": "\/*\r\n\r\n Por componentes: COMPOSITOR\r\n \r\n*\/\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:2, SPEED:5}     \r\nPLOTTER {NAME:presiones, Y_0:tank1,  Y_1:tank2,  Y_2:tank3,  X:TIME_AC1}   \r\nPLOTTER {NAME:flujo, Y_0:flujo, X:TIME_AC1}   \r\n\r\nGraphic {NAME:tTexto,Type:TEXT,X:376,Y:579,Text:\"tTexto\"}   ; Texto: nivel batería\r\n\r\ninclude .\/library_componets_liquid\/liquid.lib\r\n\r\nINITIAL PIPE12_VALUES, { K: 0.1 }\r\nINITIAL PIPE23_VALUES, { K: 0.1 }\r\nINITIAL TANK1_VALUES, { Presion_PREV: 10 }\r\nINITIAL TANK2_VALUES, { PresionA_PREV: 0 }\r\nINITIAL TANK3_VALUES, { Presion_PREV: 3 }\r\n\r\n\r\nDYNAMIC {name:tank1,  config:V$TANK_CONFIG,         VALUES:V$TANK1_VALUES, X:113, Y:491}\r\nDYNAMIC {name:tank2,  config:V$TANK_2_PORTS_CONFIG, VALUES:V$TANK2_VALUES, X:396, Y:389}\r\nDYNAMIC {name:tank3,  config:V$TANK_CONFIG,         VALUES:V$TANK3_VALUES, X:696, Y:342}\r\nDYNAMIC {name:pipe12,  config:V$PIPE_CONFIG,        VALUES:V$PIPE12_VALUES, X:189, Y:333}\r\nDYNAMIC {name:pipe23,  config:V$PIPE_CONFIG,        VALUES:V$PIPE23_VALUES, X:603, Y:512}\r\n\r\n\r\nINITIAL LIQUID_SYSTEM, {\r\n  COMPONENTS: [\"pipe12\", \"pipe23\", \"tank1\", \"tank2\", \"tank3\"],\r\n  CONNECTIONS: [\r\n    { NODE: \"n1\", CONNECTIONS: [\"tank1.A\",\"pipe12.A\"] },\r\n    { NODE: \"n2\", CONNECTIONS: [\"pipe12.B\", \"tank2.A\"] },\r\n    { NODE: \"n3\", CONNECTIONS: [\"pipe23.A\", \"tank2.B\"] },\r\n    { NODE: \"n4\", CONNECTIONS: [\"pipe23.B\", \"tank3.A\"] }\r\n  ]\r\n}\r\n\r\nDYNAMIC {name:sys, compositor:V$LIQUID_SYSTEM, X:712, Y:54}\r\n\r\nSTART 1000 \r\n\r\ninclude .\/library_graphics\/tank.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n\r\ntimeout cambiar_deposito,2\r\n    advance 0.1\r\nwhile (1==1)\r\n\r\n    SOLVE {  name:\"sys\",  DT: 0.01,  SAVEVALUE: \"resultado\"}\r\n\r\n   \r\n    SAVEVALUE TANK1_PRESION, X$(resultado.tank1_Presion)\r\n    SAVEVALUE TANK2_PRESION, X$(resultado.tank2_PresionA)\r\n    SAVEVALUE TANK3_PRESION, X$(resultado.tank3_Presion)\r\n    SAVEVALUE PIPE12,  X$(resultado.pipe12_QA)\r\n    SAVEVALUE PIPE23,  X$(resultado.pipe23_QA)\r\n\r\n    PLOT presiones, X$TANK1_PRESION, X$TANK2_PRESION, X$TANK3_PRESION\r\n    PLOT flujo, X$PIPE_FLUJO\r\n\r\n\tassign rFlujo12,round(X$PIPE12,3)\r\n\tassign rFlujo23,round(X$PIPE23,3)\r\n\tassign rH1,round(X$TANK1_PRESION,3)\r\n\tassign rH2,round(X$TANK2_PRESION,3)\r\n\tassign rH3,round(X$TANK3_PRESION,3)\r\n    \r\n    move {name:tTexto, text:\"AC1$ flujos: P$rFlujo12 ----- P$rFlujo23 \"}\r\n\r\n\tCALL tank1.tank.set, P$rH1\r\n\tCALL tank2.tank.set, P$rH2\r\n\tCALL tank3.tank.set, P$rH3\r\n\r\n\r\n    IF (ABS(X$PIPE_FLUJO) < 0.001)\r\n        ;stop\r\n    ENDIF\r\n\r\nadvance 0.01,0\r\n\r\nendwhile\r\n\r\nstop\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    CALL crear_depositos\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_depositos\r\n\r\n\tassign config,{title:\"TANK 1\"\r\n    \t\t,x:100,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\ttimeout tank1.tank.init,0,V$config\r\n\r\n\tassign config,{title:\"TANK 2\"\r\n    \t\t,x:300,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\ttimeout tank2.tank.init,0,V$config\r\n\r\n\tassign config,{title:\"TANK 3\"\r\n    \t\t,x:500,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\ttimeout tank3.tank.init,0,V$config\r\n\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\nPROCEDURE cambiar_deposito\r\n\r\n    assign nuevosParams,{tank3_Presion_PREV:10}\r\n    dynamic_set sys,V$nuevosParams\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n",
                        "descripcion": "<p data-start=\"259\" data-end=\"591\">En simulaciones complejas, a menudo necesitamos modelar sistemas formados por m&uacute;ltiples componentes que interact&uacute;an entre s&iacute;, como dep&oacute;sitos conectados por tuber&iacute;as, circuitos el&eacute;ctricos o sistemas mec&aacute;nicos acoplados. Para ello, GPSS-Plus utiliza el concepto de <strong data-start=\"522\" data-end=\"549\">composici&oacute;n de sistemas<\/strong> mediante la COMPOSICI&Oacute;N.<\/p>\r\n<h3 data-start=\"670\" data-end=\"699\">&iquest;Qu&eacute; hace la composici&oacute;n?<\/h3>\r\n<p data-start=\"701\" data-end=\"946\">La composici&oacute;n permite <strong data-start=\"724\" data-end=\"767\">fusionar m&uacute;ltiples DYNAMIC individuales<\/strong> en un solo sistema din&aacute;mico. Esta fusi&oacute;n se realiza conectando sus variables seg&uacute;n un esquema com&uacute;n de nodos, y resolviendo <strong data-start=\"892\" data-end=\"915\">de forma simult&aacute;nea<\/strong> todas sus ecuaciones internas.<\/p>\r\n<p data-start=\"593\" data-end=\"796\">&nbsp;<\/p>\r\n<h3>Estructura de un sistema compuesto<\/h3>\r\n<p data-start=\"593\" data-end=\"796\">Veamos un ejemplo pr&aacute;ctico: tres dep&oacute;sitos conectados por dos tuber&iacute;as, donde el dep&oacute;sito central tiene dos entradas. El esquema ser&iacute;a:<\/p>\r\n<pre>\r\nTANK1 --- PIPE12 --- TANK2 --- PIPE23 --- TANK3<\/pre>\r\n<p data-start=\"593\" data-end=\"796\">Este sistema se define as&iacute;:<\/p>\r\n<pre>\r\nINITIAL LIQUID_SYSTEM, {\r\n&nbsp; COMPONENTS: [&quot;pipe12&quot;, &quot;pipe23&quot;, &quot;tank1&quot;, &quot;tank2&quot;, &quot;tank3&quot;],\r\n&nbsp; CONNECTIONS: [\r\n&nbsp; &nbsp; { NODE: &quot;n1&quot;, CONNECTIONS: [&quot;tank1.A&quot;,&quot;pipe12.A&quot;] },\r\n&nbsp; &nbsp; { NODE: &quot;n2&quot;, CONNECTIONS: [&quot;pipe12.B&quot;, &quot;tank2.A&quot;] },\r\n&nbsp; &nbsp; { NODE: &quot;n3&quot;, CONNECTIONS: [&quot;pipe23.A&quot;, &quot;tank2.B&quot;] },\r\n&nbsp; &nbsp; { NODE: &quot;n4&quot;, CONNECTIONS: [&quot;pipe23.B&quot;, &quot;tank3.A&quot;] }\r\n&nbsp; ]\r\n}<\/pre>\r\n<p data-start=\"593\" data-end=\"796\">Cada <code>NODE <\/code>indica un punto de conexi&oacute;n f&iacute;sica entre uno o m&aacute;s componentes. A cada <code>NODE <\/code>se le asignar&aacute; una <strong data-start=\"1672\" data-end=\"1707\">variable de esfuerzo compartida<\/strong> (como presi&oacute;n o voltaje), y cada componente aportar&aacute; sus propias ecuaciones internas y de flujo.<\/p>\r\n<h3>Definiendo los componentes<\/h3>\r\n<p data-start=\"593\" data-end=\"796\">Tuber&iacute;a (PIPE):<\/p>\r\n<pre>\r\nINITIAL PIPE_CONFIG, {\r\n  EFFORTS: {\r\n    A: { NAME: &quot;PresionA&quot;, UNIT: &quot;Pa&quot; },\r\n    B: { NAME: &quot;PresionB&quot;, UNIT: &quot;Pa&quot; }\r\n  },\r\n  ROLES: {\r\n    QA: { ROLE: &quot;FLOW&quot;, EXPOSED: [&quot;A&quot;] },\r\n    QB: { ROLE: &quot;FLOW&quot;, EXPOSED: [&quot;B&quot;] },\r\n    K: { ROLE: &quot;CONST&quot; }\r\n  },\r\n  EXPRESSIONS: [\r\n    &quot;QA + K * (PresionA - PresionB)&quot;,     ; Flujo desde A a B\r\n    &quot;QA + QB&quot;                             ; Conservaci&oacute;n de flujo interna\r\n  ]\r\n}<\/pre>\r\n<p data-start=\"618\" data-end=\"821\">Todas y cada una de las variables que aparecen en EXPRESSIONS deben quedar defidas claramente en uno de los dos grupos.<\/p>\r\n<ul>\r\n    <li><b>EFFORT<\/b>: A: { NAME: &quot;PresionA&quot;, UNIT: &quot;Pa&quot; } :&nbsp;Define qu&eacute; variables de esfuerzo se igualar&aacute;n con otros componentes conectados en el mismo nodo.&nbsp;Por ejemplo, un tanque tendr&aacute; una presi&oacute;n en ese nodo, que se igualar&aacute; con todas las presiones en ese mismo nodo. Se debe precisar la unidad para tener clara la compatibilidad. En este caso, Pascales (Pa).&nbsp;<\/li>\r\n    <li><b>ROLES<\/b>: QA: { ROLE: &quot;FLOW&quot;, EXPOSED: [&quot;A&quot;] }: Se define el <code>ROLE <\/code>que va a tener el resto de las variables del sistema.\r\n    <ul>\r\n        <li><b>FLOW<\/b>: Variables de flujo que se traslada en el interior de cada componente y el puerto en el que se expone.&nbsp;<code data-start=\"2471\" data-end=\"2477\">FLOW<\/code> puede exponerse en un puerto (una sola malla) o en dos (creando dos mallas distintas y una ecuaci&oacute;n de conservaci&oacute;n).<\/li>\r\n        <li><b>CONST<\/b>: Constante del sistema de valor num&eacute;rico.<\/li>\r\n        <li><b>STATE<\/b>: variable que se requiere sea calculada por el sistema.<\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"618\" data-end=\"821\">&nbsp;<\/p>\r\n<p data-start=\"618\" data-end=\"821\">Tanque de dos puertos:<\/p>\r\n<pre>\r\nINITIAL TANK_2_PORTS_CONFIG, {&quot;EFFORTS&quot;: {\r\n  &quot;A&quot;: { &quot;NAME&quot;: &quot;PresionA&quot;, &quot;UNIT&quot;: &quot;Pa&quot; },\r\n  &quot;B&quot;: { &quot;NAME&quot;: &quot;PresionB&quot;, &quot;UNIT&quot;: &quot;Pa&quot; }\r\n},\r\n&quot;ROLES&quot;: {\r\n  &quot;PresionA_PREV&quot;: { &quot;ROLE&quot;: &quot;const&quot;, &quot;UNIT&quot;: &quot;Pa&quot; },\r\n  &quot;IN1&quot;: { &quot;ROLE&quot;: &quot;flow&quot;, &quot;EXPOSED&quot;: [&quot;A&quot;] },\r\n  &quot;IN2&quot;: { &quot;ROLE&quot;: &quot;flow&quot;, &quot;EXPOSED&quot;: [&quot;B&quot;] }\r\n},\r\n&quot;EXPRESSIONS&quot;: [\r\n  &quot;PresionA - PresionA_PREV - (IN1 + IN2) * DT&quot;,  ; Conservaci&oacute;n de masa\/volumen\r\n  &quot;PresionA - PresionB&quot;                           ; Misma presi&oacute;n en los dos puertos\r\n]\r\n}<\/pre>\r\n<h3>Composici&oacute;n final del sistema<\/h3>\r\n<p data-start=\"618\" data-end=\"821\">Despu&eacute;s de declarar componentes y conexiones, simplemente se usa en COMANDO:<\/p>\r\n<pre>\r\nDYNAMIC {name:sys, compositor:V$LIQUID_SYSTEM, X:712, Y:54}<\/pre>\r\n<p data-start=\"3282\" data-end=\"3363\">Este <code data-start=\"3287\" data-end=\"3296\">DYNAMIC<\/code> genera autom&aacute;ticamente todas las ecuaciones necesarias combinando:<\/p>\r\n<ul data-start=\"3365\" data-end=\"3509\">\r\n    <li data-start=\"3365\" data-end=\"3412\">\r\n    <p data-start=\"3367\" data-end=\"3412\">Variables de <strong data-start=\"3380\" data-end=\"3390\">effort<\/strong> compartidas por nodo.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3413\" data-end=\"3459\">\r\n    <p data-start=\"3415\" data-end=\"3459\">Variables de <strong data-start=\"3428\" data-end=\"3436\">flow<\/strong> conectadas por puerto.<\/p>\r\n    <\/li>\r\n    <li data-start=\"3460\" data-end=\"3509\">\r\n    <p data-start=\"3462\" data-end=\"3509\">Reglas de conservaci&oacute;n (de masa, energ&iacute;a, etc.)<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"618\" data-end=\"821\">El resultado que se puede ver en la zona de debug ser&iacute;a:<\/p>\r\n<pre><b>EXPRESSIONS:<\/b> \r\n1: FLOW_1 + PIPE12_K * (PA_N1 - PA_N2) \r\n2: FLOW_1 + FLOW_2 \r\n3: FLOW_3 + PIPE23_K * (PA_N3 - PA_N4) \r\n4: FLOW_3 + FLOW_4 \r\n5: PA_N1 - TANK1_PRESION_PREV - FLOW_1 * DT \r\n6: PA_N2 - TANK2_PRESIONA_PREV - (FLOW_2 + FLOW_3) * DT \r\n7: PA_N2 - PA_N3 \r\n8: PA_N4 - TANK3_PRESION_PREV - FLOW_4 * DT<\/pre>\r\n<p data-start=\"618\" data-end=\"821\">Donde vemos que las variables de esfuerzo se han igualado con una para cada nodo y las de flujo se han numerado por malla.<\/p>\r\n<p>&nbsp;<\/p>\r\n<h2 data-start=\"4104\" data-end=\"4128\">Observaciones finales<\/h2>\r\n<ul data-start=\"4130\" data-end=\"4446\">\r\n    <li data-start=\"4130\" data-end=\"4200\">\r\n    <p data-start=\"4132\" data-end=\"4200\">Cada componente puede reutilizarse f&aacute;cilmente con distintos valores.<\/p>\r\n    <\/li>\r\n    <li data-start=\"4201\" data-end=\"4344\">\r\n    <p data-start=\"4203\" data-end=\"4344\">Es posible construir bibliotecas completas de componentes (<code data-start=\"4262\" data-end=\"4268\">.lib<\/code>) y combinarlas para construir modelos complejos sin reescribir expresiones.<\/p>\r\n    <\/li>\r\n    <li data-start=\"4345\" data-end=\"4446\">\r\n    <p data-start=\"4347\" data-end=\"4446\">El sistema cuida autom&aacute;ticamente los valores previos (_PREV), nombres unificados y mallas internas.<\/p>\r\n    <\/li>\r\n<\/ul>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "320",
                        "nombre": "Librerías de componentes",
                        "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:3, SPEED:1,pause:0}     \r\n\r\nPLOTTER {NAME:movimiento, Y_0:tank1,  Y_1:tank2,  X:TIME_AC1}   \r\nGraphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:\"tTexto\"} \r\n\r\n\r\ninclude .\/library_componets_liquid\/liquid.lib\r\n\r\nINITIAL pipe1_DATA, { K: 0.1 }\r\nDYNAMIC {name:pipe1,config:V$pipe_CONFIG,VALUES:V$pipe1_DATA,X:373,Y:418}\r\n\r\nINITIAL tank1_DATA, { PRESION_PREV: 10 }\r\nDYNAMIC {name:tank1,config:V$tank_CONFIG,VALUES:V$tank1_DATA,X:123,Y:357}\r\n\r\nINITIAL tank2_DATA, { PRESION_PREV: 1 }\r\nDYNAMIC {name:tank2,config:V$tank_CONFIG,VALUES:V$tank2_DATA,X:624,Y:347}\r\n\r\ninitial COMPOSITOR, {\r\n   COMPONENTS: [ \"pipe1\",\"tank1\",\"tank2\"],\r\n   CONNECTIONS: [ \"TANK1.A=PIPE1.A\", \"TANK2.A=PIPE1.B\"]\r\n   }\r\n\r\nDYNAMIC {name:sys,compositor:V$COMPOSITOR,X:347,Y:164}\r\n\r\n\r\nSTART 1\r\n\r\ninclude .\/library_graphics\/tank.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n\r\ntimeout cambiar_deposito,100\r\ntimeout cambiar_deposito2,200\r\n    \r\nwhile (1==1)\r\n\r\n\r\nSOLVE {  name:\"sys\",  DT: 0.1,  SAVEVALUE: \"resultado\"}\r\n\r\n\r\n\r\nSAVEVALUE TANK1_PRESION, X$(resultado.TANK1_PRESION)\r\nSAVEVALUE TANK2_PRESION, X$(resultado.TANK2_PRESION)\r\nSAVEVALUE PIPE1_FLUJO,  X$(resultado.PIPE1_FLUJO)\r\n\r\nPLOT movimiento, X$TANK1_PRESION, X$TANK2_PRESION\r\n\r\n\tassign rFlujo,round(X$PIPE_FLUJO,5)\r\n\tassign rH1,round(X$TANK1_PRESION,5)\r\n\tassign rH2,round(X$TANK2_PRESION,5)\r\n    \r\n    move {name:tTexto, text:\"flujo:\\n P$rFlujo Tot: P$rH1 P$rH2 \"}\r\n\r\n\tCALL tank1.tank.set, X$TANK1_PRESION\r\n\tCALL tank2.tank.set, X$TANK2_PRESION\r\n\r\n\r\n    IF (ABS(X$PIPE1_FLUJO) < 0.001)\r\n        stop\r\n    ENDIF\r\n\r\nadvance 1,0\r\n\r\nendwhile\r\n\r\nstop\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    CALL crear_depositos\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_depositos\r\n\r\n\tassign config,{title:\"TANK 1\"\r\n    \t\t,x:100,y:100 \r\n            ,width:50 ,height:180\r\n            ,value:X$TANK1_PRESION\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\tcall tank1.tank.init,V$config\r\n\r\n\tassign config,{title:\"TANK 2\"\r\n    \t\t,x:500,y:100 \r\n            ,width:50 ,height:180\r\n            ,value:X$TANK2_PRESION\r\n            ,max_value:10\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\tcall tank2.tank.init,V$config\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\nPROCEDURE cambiar_deposito\r\n    assign nuevosParams,{PIPE1_K:0.3}\r\n    dynamic_set sys,V$nuevosParams\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nPROCEDURE cambiar_deposito2\r\n    assign nuevosParams,{TANK2_PRESION_PREV:10}\r\n    dynamic_set sys,V$nuevosParams\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n\r\n",
                        "descripcion": "<h3 data-start=\"257\" data-end=\"282\">&iquest;Qu&eacute; es una librer&iacute;a?<\/h3>\r\n<p data-start=\"284\" data-end=\"510\">Una librer&iacute;a es un conjunto de componentes predefinidos con sus configuraciones y comportamientos matem&aacute;ticos ya listos para usarse.&nbsp;En el caso de los componentes de GPSS-Plus se limita a guardar los <b>CONFIG <\/b>de cada elemento. Por ejemplo:<\/p>\r\n<pre>\r\ninitial PIPE_CONFIG, {\r\n&nbsp; TYPE: &quot;PIPE&quot;,\r\n&nbsp; EXPRESSIONS: [ &quot;FLUJO + K * (PRESION_A - PRESION_B)&quot; ],\r\n&nbsp; STATES: [&quot;FLUJO&quot;],\r\n&nbsp; VARIABLES: [&quot;K&quot;],\r\n&nbsp; OWNED_VARS: { A: [&quot;FLUJO&quot;], B: [&quot;-FLUJO&quot;] },\r\n&nbsp; REQUIRED_VARS: { A: [&quot;PRESION_A&quot;], B: [&quot;PRESION_B&quot;] }\r\n}<\/pre>\r\n<p><b>Solo necesitamos realizar un <code>INCLUDE <\/code>del fichero que la contiene:<\/b><\/p>\r\n<pre><b>include .\/library_componets_liquid\/liquid.lib<\/b>\r\n<\/pre>\r\n<p data-start=\"1238\" data-end=\"1312\">Y usamos esos config como si los hubi&ntilde;esemos escrito en el c&oacute;digo principal.<\/p>\r\n<pre>\r\ninitial pipe1_data, { K: 0.1 }\r\ndynamic { name: pipe1, config: V$PIPE_CONFIG, values: V$pipe1_data, x: 100, y: 300 }\r\n\r\ninitial tank1_data, { PRESION_PREV: 10 }\r\ndynamic { name: tank1, config: V$TANK_CONFIG, values: V$tank1_data, x: 50, y: 400 }\r\n\r\ninitial tank2_data, { PRESION_PREV: 5 }\r\ndynamic { name: tank2, config: V$TANK_CONFIG, values: V$tank2_data, x: 200, y: 400 }\r\n\r\n<\/pre>\r\n<h3 data-start=\"2390\" data-end=\"2420\">Ventajas de usar librer&iacute;as<\/h3>\r\n<ul data-start=\"2422\" data-end=\"2523\">\r\n    <li data-start=\"2422\" data-end=\"2456\">\r\n    <p data-start=\"2424\" data-end=\"2456\">Reutilizaci&oacute;n y estandarizaci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2457\" data-end=\"2480\">\r\n    <p data-start=\"2459\" data-end=\"2480\">Reducci&oacute;n de errores.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2481\" data-end=\"2523\">\r\n    <p data-start=\"2483\" data-end=\"2523\">Modelado m&aacute;s r&aacute;pido y con mayor calidad.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "375",
                "nombre": "No Lineales Restricciones",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "376",
                        "nombre": "Introducción a las restricciones",
                        "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:VISUAL, MODE:3D, V_WIDTH:70, V_HEIGHT:50, CAMERA:0}\r\n\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     \r\n;SYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\n\r\nGraphic {NAME:linea1,Type:LINE,X1:0,Y1:0,X2:0,Y2:500}\r\nGraphic {NAME:linea2,Type:LINE,X1:0,Y1:0,X2:500,Y2:0}\r\nGraphic {NAME:linea3,Type:LINE,X1:0,Y1:0,X2:0,Y2:0,Z2:500}\r\n\r\nGRAPHIC {NAME:masa, TYPE:SPHERE, X:0, Y:3, Z:0, radius:3, color:red}\r\n\r\nGRAPHIC {NAME:soporteIz, TYPE:BOX, X:-1, Y:30, Z:0, WIDTH:2, HEIGHT:60, DEPTH:20, color:yellow}\r\nGRAPHIC {NAME:soporteDe, TYPE:BOX, X:31, Y:30, Z:0, WIDTH:2, HEIGHT:60, DEPTH:20, color:yellow}\r\nGRAPHIC {NAME:soporteSu, TYPE:BOX, X:15, Y:61, Z:0, WIDTH:30, HEIGHT:2, DEPTH:20, color:yellow}\r\nGRAPHIC {NAME:soporteIn, TYPE:BOX, X:15, Y:-1, Z:0, WIDTH:30, HEIGHT:2, DEPTH:20, color:yellow}\r\n\r\n\r\n\r\nINITIAL pelota2D_config, {\r\n  EXPRESSIONS: [\r\n    ; velocidad constante (sin fuerzas)\r\n    \"VX - VX_PREV\",\r\n    \"VY - VY_PREV\",\r\n\r\n    ; integración explícita\r\n    \"X - X_PREV - DT * VX\",\r\n    \"Y - Y_PREV - DT * VY\"\r\n  ],\r\n  STATES: [\r\n    \"X\",\"Y\",\"VX\",\"VY\"\r\n  ],\r\n  VARIABLES: [\r\n    \"X_PREV\",\"Y_PREV\",\"VX_PREV\",\"VY_PREV\",\r\n    \"R\",\"E\",\r\n    \"MINX\",\"MAXX\",\"MINY\",\"MAXY\"\r\n  ],\r\n  \r\n  ; Las CONDITIONS corrigen el estado después del paso libre (mover → corregir).\r\n  \r\n  CONDITIONS: [\r\n    ; --- pared izquierda\/derecha ---\r\n    {\r\n      \"VARIABLE\":\"VX_PREV\",\r\n      \"EXPRESSION\":\"(X <= (MINX+R) and VX < 0) ? -VX*E : ((X >= (MAXX-R) and VX > 0) ? -VX*E : VX)\"\r\n    },\r\n    {\r\n      \"VARIABLE\":\"X_PREV\",\r\n      \"EXPRESSION\":\"(X <= (MINX+R) and VX < 0) ? (MINX+R) : ((X >= (MAXX-R) and VX > 0) ? (MAXX-R) : X)\"\r\n    },\r\n\r\n    ; --- pared abajo\/arriba ---\r\n    {\r\n      \"VARIABLE\":\"VY_PREV\",\r\n      \"EXPRESSION\":\"(Y <= (MINY+R) and VY < 0) ? -VY*E : ((Y >= (MAXY-R) and VY > 0) ? -VY*E : VY)\"\r\n    },\r\n    {\r\n      \"VARIABLE\":\"Y_PREV\",\r\n      \"EXPRESSION\":\"(Y <= (MINY+R) and VY < 0) ? (MINY+R) : ((Y >= (MAXY-R) and VY > 0) ? (MAXY-R) : Y)\"\r\n    }\r\n  ]\r\n}\r\n\r\nINITIAL pelota2D_values, {\r\n  R: 3,\r\n  E: 0.8,\r\n\r\n  MINX: 0,  MAXX: 30,\r\n  MINY: 0,  MAXY: 60,\r\n\r\n  X_PREV: 20,\r\n  Y_PREV: 20,\r\n  VX_PREV: 12,\r\n  VY_PREV: 7\r\n}\r\n\r\nDYNAMIC {\r\n  name: box2d,\r\n  CONFIG: V$pelota2D_config,\r\n  VALUES: V$pelota2D_values,\r\n  X: 200,\r\n  Y: 200\r\n}\r\n\r\n\r\nSTART 1000 \r\n\r\ninclude .\/library_graphics\/speedometer.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n\r\n   \r\nwhile (1==1)\r\n\r\n    SOLVE {  name:\"box2d\",  DT: 0.1,  SAVEVALUE: \"resultado\"}\r\n\r\n    SAVEVALUE X, X$(resultado.X)\r\n    SAVEVALUE Y, X$(resultado.Y)\r\n\r\n    ; === Posición de la masa\r\n    MOVE {name:masa, X:X$X , Y:X$Y}\r\n\r\n\tassign vx,round(X$(resultado.VX),3)\r\n\tassign vy,round(X$(resultado.VY),3)\r\n\r\n\tCALL indi1.speedometer.set, P$vx\r\n\tCALL indi2.speedometer.set, P$vy\r\n\r\n\t; CONDICIÓN DE PARADA\r\n    ; IF (ABS(P$vx) < 0.001)\r\n    ;    stop\r\n    ; ENDIF\r\n\r\nadvance 0.1,0\r\n\r\nendwhile\r\n\r\n\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    CALL crear_indicadores\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_indicadores\r\n\r\n\tassign config,{title:\"VX\"\r\n    \t\t,x:50,y:15 \r\n            ,width:5 ,height:18\r\n            ,value:0\r\n            ,min_value:-15\r\n            ,max_value:15\r\n            ,\"color\":\"#ff0000\"\r\n            ,font:\"3px\"}\r\n            \r\n\tcall indi1.speedometer.init,V$config\r\n\r\n\tassign config,{title:\"VY\"\r\n    \t\t,x:80,y:15 \r\n            ,width:5 ,height:18\r\n            ,value:0\r\n            ,min_value:-15\r\n            ,max_value:15\r\n            ,\"color\":\"blue\"\r\n            ,font:\"3px\"}\r\n            \r\n\tcall indi2.speedometer.init,V$config\r\n\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\n\r\n",
                        "descripcion": "<p>Hemos visto varias f&oacute;rmulas f&iacute;sicas, el&eacute;ctricas y de fluidos descritas en DYNAMIC evolucionar en el SOLVE.<br>\r\n<br>\r\nResumidamente, introduc&iacute;amos todas las f&oacute;rmulas que deb&iacute;a satisfacer un sistema para ser resueltas a la vez. As&iacute; pod&iacute;amos resolver, por ejemplo, el problema de los 3 tanques de l&iacute;quidos conectados.<br>\r\n<br>\r\nVeremos ahora una forma de utilizar ese mismo recurso para dar soluci&oacute;n a la f&iacute;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&uacute;ltiples objetos movi&eacute;ndose entre ellos con ciertas restricciones de giro o distancia.<br>\r\nNo introducimos un nuevo tipo de simulaci&oacute;n, sino una forma distinta de describir otro problema.<br>\r\n<br>\r\nPara hacernos una idea de c&oacute;mo funciona, es como cualquier otra simulaci&oacute;n, debemos observar qu&eacute; sucede en el mundo real y tras ello, describirlo tal cual.<br>\r\nLos pasos suelen ser los siguientes:<br>\r\n<br>\r\n1.- El objeto u objetos. Por simplificar, usaremos esferas y cubos perfectos. Pueden tener un peso y radio. Se sit&uacute;an en un lugar en el espacio.<br>\r\n<br>\r\n2.- Su velocidad puntual. Debemos saber si est&aacute; est&aacute;tico, con una cierta velocidad libre o est&aacute; sometido a una fuerza que le confiere una aceleraci&oacute;n. Es decir:<br>\r\n<br>\r\nIntegraremos la posici&oacute;n&nbsp;<br>\r\nx=x+v&Delta;t<br>\r\n<br>\r\nY si hay fuerzas involucradas, tambi&eacute;n las fuerzas entrar&aacute;n en juego:&nbsp;<br>\r\nv=v+a&Delta;t<br>\r\n<br>\r\n<br>\r\n3.- La detecci&oacute;n de la restricci&oacute;n. Si hablamos de un objeto que choca con una pared, debemos saberlo antes de efectuar alguna consecuencia. As&iacute; que se verificar&aacute;n si estamos violando alguna restricci&oacute;n.<br>\r\n<br>\r\nSi nuestro objeto fuese una esfera, verificaremos si choca o invade el espacio de:<br>\r\nCon pared: comparar coordenadas<br>\r\nCon esfera: distancia entre centros<br>\r\nCon caja: comparar intervalos (AABB)<br>\r\nCon formas m&aacute;s complejas: m&aacute;s geometr&iacute;a<br>\r\n<br>\r\n<br>\r\n4.- Las restricciones. Si hasta ahora hablamos de posici&oacute;n y velocidad del objeto, una restricci&oacute;n, sea la que sea, debe modificar ambas caracter&iacute;sticas. Si el objeto choca contra una pared, el incremento de x ir&aacute; en la direcci&oacute;n contraria y la velocidad cambiar&aacute; de signo. Una restricci&oacute;n no &ldquo;impide&rdquo;, corrige.<br>\r\n<br>\r\nCosa que no quita para que una restricci&oacute;n sea, por ejemplo, no superar cierta velocidad.<br>\r\n<br>\r\n5.- Escalado. Cuando hay muchos objetos se hace inviable meter miles de objetos en el sistema y se realiza por partes.<br>\r\nSe requiere filtrar candidatos por m&eacute;todos de rejillas o &aacute;rboles..<\/p>\r\n<p>&nbsp;<\/p>\r\n<p><b>El ejemplo:<\/b><\/p>\r\n<p>Una sola masa esf&eacute;rica que bota contra las paredes que lo encierran.<\/p>\r\n<p>Mostramos las f&oacute;rmulas de las velocidades constantes y sus integrales para obtener las posiciones.<\/p>\r\n<p>Los estados son los 4 nombrados: Posici&oacute;n (x,y) y sus velocidades (vx,vy)<\/p>\r\n<p>Y tras esto, las restricciones. Que observemos la primera:<\/p>\r\n<pre>\r\n{\r\n&nbsp; &quot;VARIABLE&quot;:&quot;VX_PREV&quot;,\r\n&nbsp; &quot;EXPRESSION&quot;:&quot;(X &lt;= (MINX+R) and VX &lt; 0) ? -VX*E : ((X &gt;= (MAXX-R) and VX &gt; 0) ? -VX*E : VX)&quot;\r\n}\r\n<\/pre>\r\n<p><br>\r\nSignifica qu&eacute; vamos a hacer con una determinable variable si suceden ciertas condiciones. En este caso, qu&eacute; le va a suceder a la variable &quot;VX_PREV&quot;, que es uno de los estados previos a la siguiente iteraci&oacute;n del solver.<\/p>\r\n<p>Las variables <code data-start=\"1305\" data-end=\"1313\">*_PREV<\/code> representan el estado que &ldquo;hereda&rdquo; el siguiente paso de <code data-start=\"1370\" data-end=\"1377\">SOLVE.<\/code><br>\r\n<br>\r\nLa salida ser&aacute; o dejarla como est&aacute; en &uacute;ltimo termino (VX) o modificarla si suceden las condiciones establecidas.<br>\r\nSi vamos hacia la izquierda y hemos pasado el l&iacute;mite izquierdo, VX_PREV ser&aacute; -VX*E.<br>\r\nEn caso contrario, comprobaremos la derecha siendo VX_PREV = -VX*E de nuevo.<br>\r\nY como dec&iacute;amos, si no hay restricciones que aplicar, VX_PREV ser&aacute; VX.<\/p>\r\n<p>N&oacute;tese que &quot;E&quot; es la velocidad que conservo tras cada rebote, as&iacute; que se usa como multiplicador para reducir la velocidad. Si E fuese 0, ser&iacute;a una parada en seco.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "326",
                        "nombre": "Restricciones en Sistemas Dinámicos",
                        "texto": "\/*\r\n\r\n Restricciones en Sistemas Dinamicos\r\n \r\n*\/\r\n\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {type:OPTIONS, TIME_DECIMALS:1, SPEED:5}     \r\n\r\nPLOTTER {NAME:posiciones, Y_0:X1,  Y_1:X2,  X:TIME_AC1}   \r\n\r\nGraphic {NAME:tTexto,Type:TEXT,X:327,Y:486,Text:\"tTexto\"}\r\n\r\n\r\n\r\nINITIAL mech_config, {\r\n  EXPRESSIONS: [\r\n    \"F1 - M1 * (V1 - V1_PREV) \/ DT\",\r\n    \"FD1 - C1 * V1\",\r\n    \"F_spring1 - K1 * X1\",\r\n\r\n    \"F_spring2 - K2 * (X2 - X1)\",\r\n    \"FD2 - C2 * V2\",\r\n    \"F2 - M2 * (V2 - V2_PREV) \/ DT\",\r\n\r\n    \"F_restriction + Lambda\",  ; fuerza de restricción (con signo)\r\n\r\n    \"F1 + F_spring1 + FD1 - F_spring2 - F_restriction\",\r\n    \"F2 + F_spring2 + FD2 + F_restriction\",\r\n\r\n    \"X1 - X1_PREV - DT * V1\",\r\n    \"X2 - X2_PREV - DT * V2\",\r\n\r\n    \"X2 - X1 - L\"  ; restricción geométrica\r\n  ],\r\n\r\n  STATES: [\r\n    \"X1\", \"X2\",\r\n    \"V1\", \"V2\",\r\n    \"F1\", \"F2\",\r\n    \"FD1\", \"FD2\",\r\n    \"F_spring1\", \"F_spring2\",\r\n    \"F_restriction\",\r\n    \"Lambda\"\r\n  ],\r\n\r\n  VARIABLES: [\r\n    \"M1\", \"M2\",\r\n    \"K1\", \"K2\",\r\n    \"C1\", \"C2\",\r\n    \"L\",\r\n    \"X1_PREV\", \"X2_PREV\",\r\n    \"V1_PREV\", \"V2_PREV\"\r\n  ]\r\n}\r\n\r\n\r\n\r\n\r\nINITIAL mech_values, {\r\n  M1: 1.5,\r\n  M2: 1.5,\r\n\r\n  K1: 5,\r\n  K2: 8,\r\n\r\n  C1: 0.1,\r\n  C2: 0.1,\r\n\r\n  X1_PREV: 1.5,\r\n  V1_PREV: 2,\r\n\r\n  X2_PREV: 3.5,\r\n  V2_PREV: -1,\r\n\r\n  L: 2   ; restricción de distancia entre masas\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\nDYNAMIC {\r\n  name: sys,\r\n  CONFIG: V$mech_config,\r\n  VALUES: V$mech_values,\r\n  X: 300,\r\n  Y: 300,\r\n  TOLERANCE: 1e-6,\r\n  MAX_ITER: 10\r\n}\r\n\r\n\r\nSTART 1000 \r\n\r\ninclude .\/library_graphics\/speedometer.lib\r\n\r\n;==============================================================\r\n\r\nPROCEDURE agente.init\r\n\r\n    \r\nwhile (1==1)\r\n\r\n    SOLVE {  name:\"sys\",  DT: 0.1,  SAVEVALUE: \"resultado\"}\r\n\r\n    SAVEVALUE X1, X$(resultado.X1)\r\n    SAVEVALUE X2, X$(resultado.X2)\r\n    SAVEVALUE V1, X$(resultado.V1)\r\n\r\n    PLOT posiciones, X$X1, X$X2\r\n\r\n\tassign rX1,round(X$X1,5)\r\n\tassign rX2,round(X$X2,5)\r\n    \r\n\tCALL tank1.speedometer.set, P$rX1\r\n\tCALL tank2.speedometer.set, P$rX2\r\n\r\n    IF (ABS(X$V1) < 0.00001)\r\n        stop\r\n    ENDIF\r\n\r\nadvance 0.1,0\r\n\r\nendwhile\r\n\r\nstop\r\nENDPROCEDURE\r\n;====================================================================\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    CALL crear_indicadores\r\n    \r\n    TIMEOUT agente.init,0\r\n\r\n\tTERMINATE_VE \r\nENDPROCEDURE\r\n;=================================================\r\nPROCEDURE crear_indicadores\r\n\r\n\tassign config,{title:\"X1\"\r\n    \t\t,x:100,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,min_value:-5\r\n            ,max_value:5\r\n            ,\"color\":\"#ff0000\"}\r\n            \r\n\tcall tank1.speedometer.init,V$config\r\n\r\n\tassign config,{title:\"X2\"\r\n    \t\t,x:400,y:50 \r\n            ,width:50 ,height:180\r\n            ,value:0\r\n            ,min_value:-5\r\n            ,max_value:5\r\n            ,\"color\":\"purple\"}\r\n            \r\n\tcall tank2.speedometer.init,V$config\r\n\r\n\t\r\nENDPROCEDURE\r\n\r\n;=================================================\r\n\r\n\r\n\r\n",
                        "descripcion": "<h3 data-start=\"275\" data-end=\"303\">&iquest;Qu&eacute; es una restricci&oacute;n?<\/h3>\r\n<p data-start=\"305\" data-end=\"653\">En din&aacute;mica de sistemas, una <strong data-start=\"334\" data-end=\"349\">restricci&oacute;n<\/strong> es una condici&oacute;n que debe cumplirse <strong data-start=\"386\" data-end=\"405\">en todo momento<\/strong> durante la simulaci&oacute;n. A diferencia de una expresi&oacute;n derivada del comportamiento natural de un componente, la restricci&oacute;n <strong data-start=\"528\" data-end=\"555\">no cambia con el tiempo<\/strong> (no depende de la evoluci&oacute;n de las variables), sino que <strong data-start=\"612\" data-end=\"640\">impone una relaci&oacute;n fija<\/strong> entre ellas.<\/p>\r\n<p data-start=\"655\" data-end=\"797\">Esto se conoce tambi&eacute;n como <strong data-start=\"683\" data-end=\"709\">restricci&oacute;n algebraica<\/strong> y convierte el sistema en un sistema de ecuaciones diferenciales algebraicas (<strong data-start=\"788\" data-end=\"795\">DAE<\/strong>).<\/p>\r\n<h3>Ejemplo: Dos masas unidas por una varilla r&iacute;gida<\/h3>\r\n<p data-start=\"858\" data-end=\"1012\">Supongamos dos masas conectadas por muelles y amortiguadores, pero adem&aacute;s unidas por una <strong data-start=\"947\" data-end=\"963\">barra r&iacute;gida<\/strong> que mantiene la distancia constante entre ambas.<\/p>\r\n<p data-start=\"1014\" data-end=\"1051\">Esta condici&oacute;n puede expresarse como:<\/p>\r\n<pre>\r\nX2 - X1 = L<\/pre>\r\n<p data-start=\"1076\" data-end=\"1082\">Donde:<\/p>\r\n<ul>\r\n    <li><code data-start=\"1086\" data-end=\"1090\">X1<\/code> y <code data-start=\"1093\" data-end=\"1097\">X2<\/code> son las posiciones de las masas<\/li>\r\n    <li><code data-start=\"1132\" data-end=\"1135\">L<\/code> es la longitud de la barra<\/li>\r\n<\/ul>\r\n<p data-start=\"1164\" data-end=\"1382\">Esta igualdad debe mantenerse durante toda la simulaci&oacute;n. Para ello, se introduce una <strong data-start=\"1250\" data-end=\"1275\">fuerza de restricci&oacute;n<\/strong>, calculada mediante un <strong data-start=\"1299\" data-end=\"1328\">multiplicador de Lagrange<\/strong> (<code data-start=\"1330\" data-end=\"1338\">Lambda<\/code>) que se a&ntilde;ade a las ecuaciones del sistema.<\/p>\r\n<h3>Configuraci&oacute;n del sistema<\/h3>\r\n<pre>\r\nINITIAL mech_config, {\r\n  EXPRESSIONS: [\r\n\r\n    ; Leyes de Newton para cada masa\r\n&nbsp; &nbsp; &quot;F1 - M1 * (V1 - V1_prev) \/ DT&quot;,\r\n&nbsp; &nbsp; &quot;F2 - M2 * (V2 - V2_prev) \/ DT&quot;,\r\n\r\n&nbsp; &nbsp; ; Fuerzas internas: muelle y amortiguador\r\n&nbsp; &nbsp; &quot;FD1 - C1 * V1&quot;,\r\n&nbsp; &nbsp; &quot;F_spring1 - K1 * X1&quot;,\r\n&nbsp; &nbsp; &quot;FD2 - C2 * V2&quot;,\r\n&nbsp; &nbsp; &quot;F_spring2 - K2 * (X2 - X1)&quot;,\r\n\r\n&nbsp; &nbsp; ; Fuerza de restricci&oacute;n impuesta por Lagrange\r\n&nbsp; &nbsp; &quot;F_restriction + Lambda&quot;,\r\n\r\n&nbsp; &nbsp; ; Suma de fuerzas sobre cada masa\r\n&nbsp; &nbsp; &quot;F1 + F_spring1 + FD1 - F_spring2 - F_restriction&quot;,\r\n&nbsp; &nbsp; &quot;F2 + F_spring2 + FD2 + F_restriction&quot;,\r\n\r\n&nbsp; &nbsp; ; Integraci&oacute;n de posici&oacute;n\r\n&nbsp; &nbsp; &quot;X1 - X1_prev - DT * V1&quot;,\r\n&nbsp; &nbsp; &quot;X2 - X2_prev - DT * V2&quot;,\r\n\r\n&nbsp; &nbsp; ; ***** Restricci&oacute;n geom&eacute;trica *****\r\n&nbsp; &nbsp; &quot;X2 - X1 - L&quot;\r\n&nbsp; ],\r\n\r\n&nbsp; STATES: [\r\n&nbsp; &nbsp; &quot;X1&quot;, &quot;X2&quot;,\r\n&nbsp; &nbsp; &quot;V1&quot;, &quot;V2&quot;,\r\n&nbsp; &nbsp; &quot;F1&quot;, &quot;F2&quot;,\r\n&nbsp; &nbsp; &quot;FD1&quot;, &quot;FD2&quot;,\r\n&nbsp; &nbsp; &quot;F_spring1&quot;, &quot;F_spring2&quot;,\r\n&nbsp; &nbsp; &quot;F_restriction&quot;,\r\n&nbsp; &nbsp; &quot;Lambda&quot;&nbsp; ; variable de Lagrange\r\n&nbsp; ],\r\n\r\n&nbsp; VARIABLES: [\r\n&nbsp; &nbsp; &quot;M1&quot;, &quot;M2&quot;,\r\n&nbsp; &nbsp; &quot;K1&quot;, &quot;K2&quot;,\r\n&nbsp; &nbsp; &quot;C1&quot;, &quot;C2&quot;,\r\n&nbsp; &nbsp; &quot;L&quot;,\r\n&nbsp; &nbsp; &quot;X1_prev&quot;, &quot;X2_prev&quot;,\r\n&nbsp; &nbsp; &quot;V1_prev&quot;, &quot;V2_prev&quot;\r\n&nbsp; ]\r\n}\r\n\r\n<\/pre>\r\n<h3 data-start=\"2407\" data-end=\"2436\">C&oacute;mo act&uacute;a la restricci&oacute;n<\/h3>\r\n<p data-start=\"2438\" data-end=\"2501\">La ecuaci&oacute;n <code data-start=\"2450\" data-end=\"2465\">&quot;X2 - X1 - L&quot;<\/code> es una <strong data-start=\"2473\" data-end=\"2500\">ecuaci&oacute;n de restricci&oacute;n<\/strong>.<\/p>\r\n<p data-start=\"2503\" data-end=\"2731\">El sistema debe resolver todas las ecuaciones simult&aacute;neamente (incluyendo la restricci&oacute;n), y para ello introduce <code data-start=\"2616\" data-end=\"2624\">Lambda<\/code>, una variable interna que representa la <strong data-start=\"2665\" data-end=\"2685\">fuerza necesaria<\/strong> para mantener esa condici&oacute;n en todo instante.<\/p>\r\n<p data-start=\"2733\" data-end=\"2815\">Esta fuerza luego se suma (o resta) a las ecuaciones de fuerza total en cada masa:<\/p>\r\n<pre>\r\n&quot;F1 + ... - F_restriction&quot;\r\n&quot;F2 + ... + F_restriction&quot;<\/pre>\r\n<p>Esto asegura que si el sistema tiende a violar la restricci&oacute;n (por una aceleraci&oacute;n, por ejemplo), la fuerza de restricci&oacute;n reacciona inmediatamente para mantenerla.<\/p>\r\n<h3 data-start=\"3053\" data-end=\"3081\">&iquest;Qu&eacute; resuelve GPSS-Plus?<\/h3>\r\n<p data-start=\"3083\" data-end=\"3330\">Todo esto se resuelve en el <code data-start=\"3111\" data-end=\"3120\">DYNAMIC<\/code>, con el uso interno del m&eacute;todo de <strong data-start=\"3155\" data-end=\"3173\">Newton-Raphson<\/strong> y matrices jacobianas, que permiten resolver tanto <strong data-start=\"3225\" data-end=\"3248\">variables din&aacute;micas<\/strong> (<code data-start=\"3250\" data-end=\"3254\">X1<\/code>, <code data-start=\"3256\" data-end=\"3260\">V1<\/code>, etc.) como <strong data-start=\"3273\" data-end=\"3298\">variables algebraicas<\/strong> (<code data-start=\"3300\" data-end=\"3308\">Lambda<\/code>) en un mismo sistema.<\/p>\r\n<h2 data-start=\"3657\" data-end=\"3670\">Conclusi&oacute;n<\/h2>\r\n<p data-start=\"3672\" data-end=\"3755\">Las <strong data-start=\"3676\" data-end=\"3693\">restricciones<\/strong> permiten simular comportamientos realistas y complejos, como:<\/p>\r\n<ul data-start=\"3757\" data-end=\"3893\">\r\n    <li data-start=\"3757\" data-end=\"3773\">\r\n    <p data-start=\"3759\" data-end=\"3773\">Barras r&iacute;gidas<\/p>\r\n    <\/li>\r\n    <li data-start=\"3774\" data-end=\"3818\">\r\n    <p data-start=\"3776\" data-end=\"3818\">Pistones hidr&aacute;ulicos con volumen constante<\/p>\r\n    <\/li>\r\n    <li data-start=\"3819\" data-end=\"3857\">\r\n    <p data-start=\"3821\" data-end=\"3857\">Relaciones geom&eacute;tricas en mecanismos<\/p>\r\n    <\/li>\r\n    <li data-start=\"3858\" data-end=\"3893\">\r\n    <p data-start=\"3860\" data-end=\"3893\">Conservaci&oacute;n de energ&iacute;a o momento<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"3895\" data-end=\"4051\">El uso de <strong data-start=\"3905\" data-end=\"3917\"><code data-start=\"3907\" data-end=\"3915\">Lambda<\/code><\/strong> como multiplicador de Lagrange es una herramienta clave para introducirlas de forma expl&iacute;cita y controlada en tus sistemas din&aacute;micos.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            }
        ]
    },
    {
        "id": "328",
        "nombre": "Season 12: Acoplamiento físico y real",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "330",
                "nombre": "*Acoplamiento con el mundo real",
                "texto": "",
                "descripcion": "<p data-start=\"330\" data-end=\"616\">Hasta ahora, trabaj&aacute;bamos  construyendo modelos que operaban dentro de un universo cerrado y  controlado dentro del motor de GPSS-Plus. El motor de simulaci&oacute;n decid&iacute;a  cu&aacute;ndo avanzaba el tiempo, qu&eacute; ocurr&iacute;a en cada momento y c&oacute;mo se  comportaban los elementos del sistema. Era un entorno l&oacute;gico,  determinista y aut&oacute;nomo.<\/p>\r\n<p data-start=\"618\" data-end=\"848\">Vamos a romper con todo eso con la entrada en escena del <b>mundo real<\/b>.  Ya nada ser&aacute; lo mismo: el enfoque del motor cambia, las entidades  vivir&aacute;n seg&uacute;n otros patrones y los flujos dejar&aacute;n de estar solo bajo  nuestro control.<\/p>\r\n<p data-start=\"1217\" data-end=\"1344\">Las entidades no deber&iacute;an avanzar porque lo decide un n&uacute;mero aleatorio, sino porque un sensor f&iacute;sico ha detectado su presencia.<\/p>\r\n<p><b>El tiempo<\/b><\/p>\r\n<p data-start=\"1366\" data-end=\"1472\">Una de las primeras consecuencias de esta apertura al exterior es que el <strong data-start=\"1439\" data-end=\"1471\">tiempo deja de pertenecernos<\/strong>.<\/p>\r\n<p data-start=\"1474\" data-end=\"1776\">Ya no podemos acelerarlo ni  pausarlo arbitrariamente. Si el motor quiere leer de un sensor,  deber&aacute; esperar lo que ese sensor tarde en responder. Si la lectura de un  fichero se retrasa, tambi&eacute;n lo har&aacute; la simulaci&oacute;n. Por eso, al trabajar  con el mundo real, <strong data-start=\"1735\" data-end=\"1775\">la ejecuci&oacute;n debe ser en tiempo real<\/strong>.<\/p>\r\n<p data-start=\"1778\" data-end=\"1886\">Lo que antes era solo un &ldquo;momento&rdquo;, ahora es estrictamente un segundo del reloj del sistema. Se declara as&iacute;:<\/p>\r\n<pre>\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1,TIME_DECIMALS:1}<\/pre>\r\n<p data-pm-slice=\"1 1 []\">Un <code>ADVANCE 10<\/code> significa exactamente espera 10 segundos <b>reales<\/b>.<\/p>\r\n<p data-pm-slice=\"1 1 []\">A partir de ah&iacute;, no hay marcha atr&aacute;s: todo  ocurre al ritmo del mundo f&iacute;sico, y la simulaci&oacute;n se convierte en una  parte subordinada de la realidad.<br>\r\nEl motor deja de ser un simulador puro y pasa a comportarse como un <strong data-start=\"3197\" data-end=\"3231\">orquestador de procesos reales<\/strong><\/p>\r\n<p data-pm-slice=\"1 1 []\"><b>La nueva arquitectura del sistema&nbsp;<\/b><\/p>\r\n<p data-start=\"1769\" data-end=\"2296\">Podemos identificar ahora tres grandes componentes del sistema:<\/p>\r\n<ul>\r\n    <li><b>GPSS-Plus y e<\/b><b>l recurso BRIDGER<\/b>,&nbsp;que usar&aacute;n las entidades para emitir y recibir &oacute;rdenes.<\/li>\r\n    <li><b>El mundo exterior<\/b>,&nbsp;que puede incluir sensores, bases de  datos, sistemas de archivos o dispositivos f&iacute;sicos o virtuales. Incluso  otros motores de simulaci&oacute;n.<\/li>\r\n    <li><b>El middleware.js<\/b>, que es un nuevo software bidireccional que sabr&aacute; comunicarse con el <code>BRIDGER <\/code>y cualquier elemento del <b>mundo exterior<\/b>.<\/li>\r\n<\/ul>\r\n<p data-start=\"1769\" data-end=\"2296\"><b>EL BRIDGER:<\/b><\/p>\r\n<p>Desde el punto de vista de una entidad, adem&aacute;s del COMANDO <code>BRIDGER<\/code>, solo tiene 6 BLOQUES:<\/p>\r\n<ul>\r\n    <li>BRIDGE_READ<\/li>\r\n    <li>BRIDGE_WRITE<\/li>\r\n    <li>BRIDGE_SUBSCRIBE<\/li>\r\n    <li>BRIDGE_UNSUBSCRIBE<\/li>\r\n    <li>BRIDGE_BROWSE<\/li>\r\n    <li>BRIDGE_CALL<\/li>\r\n<\/ul>\r\n<p data-start=\"1769\" data-end=\"2296\">&nbsp;Estos BLOQUES son los que se comunican con el <b>middleware.js<\/b><\/p>\r\n<p data-start=\"1769\" data-end=\"2296\">&nbsp;<\/p>\r\n<p data-start=\"1769\" data-end=\"2296\"><b>EL MIDDLEWARE.js<\/b><\/p>\r\n<p>Disponible en el cap&iacute;tulo de descargas.<\/p>\r\n<p>Es un conjunto de archivos en javascript para ejecutar en entorno  node.js que recibe las instrucci&oacute;nes del <code>BRIDGER<\/code> y se las retransmite al  dispositivo exterior y viceversa.<\/p>\r\n<p>En resumen, <strong data-start=\"225\" data-end=\"299\"><em data-start=\"227\" data-end=\"242\">middleware.js<\/em> funciona como una capa de intercambio entre protocolos<\/strong>, transformando los cinco bloques definidos en GPSS-Plus en mensajes compatibles con <strong data-start=\"384\" data-end=\"392\">MQTT<\/strong> (el m&aacute;s utilizado, basado en <em data-start=\"422\" data-end=\"430\">topics<\/em>) y <strong data-start=\"434\" data-end=\"444\">OPC-UA<\/strong> (el m&aacute;s completo, con estructuras jer&aacute;rquicas y tipos de dato definidos).<\/p>\r\n<p>Para las pruebas iniciales existe un servicio p&uacute;blico operativo en:<\/p>\r\n<p>wss:\/\/bridger.gpss-plus.com:3000<\/p>\r\n<p>&nbsp;<\/p>\r\n<p data-start=\"1769\" data-end=\"2296\"><b>EL mundo exterior<\/b><\/p>\r\n<p>Son todos los sensores, actuadores, dispositivos y recursos externos.<\/p>\r\n<p>Para pruebas, dispones de un <strong data-start=\"3615\" data-end=\"3641\">cliente virtual OPC-UA<\/strong> que representa un conjunto de sensores y servicios simulados.<\/p>\r\n<p>Su direcci&oacute;n es: opc.tcp:\/\/opcua.gpss-plus.com:4840<\/p>\r\n<p>Contiene una b&aacute;scula, un sensor de puerta, sensores de distancia, control PID, lectura y escritura de archivos, e incluso ejecuci&oacute;n de programas en segundo plano.<\/p>\r\n<p>Naturalmente, puedes <strong data-start=\"3934\" data-end=\"3967\">modificar y ampliar el c&oacute;digo<\/strong> para a&ntilde;adir tus propios servicios.<\/p>\r\n<p><b>La topolog&iacute;a:<\/b><\/p>\r\n<p>Estos tres elementos se distribuyen de la siguiente manera:<\/p>\r\n<pre>\r\ngpss-plus          192.168.x.x           192.168.n.m\r\nBRIDGER       &lt;-&gt;  middleware.js    &lt;-&gt;  Sensor temperatura \r\n192.168.a.b    |                         Sistema de Ficheros\r\n(El navegador) |                         Base de datos MySQL\r\n               |\r\n               |                         192.168.n.m\r\n               |   192.168.y.y           Generador de n&uacute;meros aleatorios\r\n              &lt;-&gt;  middleware.js    &lt;-&gt;  Red neuronal\r\n               |                         Detector de movimiento OPC-UA\r\n               |\r\n               |\r\n               |   192.168.z.z           192.168.n.m\r\n              &lt;-&gt;  middleware.js    &lt;-&gt;  Sem&aacute;foro entre sistemas\r\n                                     <\/pre>\r\n<p>En resumen, el <strong data-start=\"4777\" data-end=\"4790\">navegador<\/strong> que ejecuta GPSS-Plus debe poder acceder a la IP donde est&eacute; instalado <strong data-start=\"4861\" data-end=\"4878\">middleware.js<\/strong>, y este, a su vez, al dispositivo o servicio externo.<br data-start=\"4932\" data-end=\"4935\">\r\nLa configuraci&oacute;n m&aacute;s estable es tener <strong data-start=\"4973\" data-end=\"5023\">middleware.js en la misma red que los sensores<\/strong>.<\/p>\r\n<p>En los siguientes cap&iacute;tulos veremos c&oacute;mo manejar el BRIDGER paso a paso y construiremos nuestros propios dispositivos f&iacute;sicos.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "336",
                "nombre": "Los recursos externos",
                "texto": "<p>En <strong data-start=\"390\" data-end=\"403\">GPSS-Plus<\/strong> llamamos <em data-start=\"413\" data-end=\"430\">recurso externo<\/em> a cualquier elemento del sistema que <strong data-start=\"468\" data-end=\"523\">no forma parte directamente del motor de simulaci&oacute;n<\/strong>, pero con el que las entidades pueden interactuar.<br data-start=\"574\" data-end=\"577\">\r\nEstos recursos pueden actuar como <strong data-start=\"611\" data-end=\"631\">fuentes de datos<\/strong> o como <strong data-start=\"639\" data-end=\"660\">actuadores reales<\/strong> dentro del entorno f&iacute;sico o digital.<\/p>\r\n<h4 data-start=\"683\" data-end=\"724\">&iquest;Qu&eacute; cuenta como recurso externo?<\/h4>\r\n<p data-start=\"747\" data-end=\"773\">Ejemplos t&iacute;picos incluyen:<\/p>\r\n<ul data-start=\"775\" data-end=\"1166\">\r\n    <li data-start=\"775\" data-end=\"825\">\r\n    <p data-start=\"777\" data-end=\"825\">Un <strong data-start=\"780\" data-end=\"791\">archivo<\/strong> en el sistema de ficheros (FS).<\/p>\r\n    <\/li>\r\n    <li data-start=\"826\" data-end=\"894\">\r\n    <p data-start=\"828\" data-end=\"894\">Una <strong data-start=\"832\" data-end=\"847\">tabla MySQL<\/strong> o cualquier base de datos accesible por red.<\/p>\r\n    <\/li>\r\n    <li data-start=\"895\" data-end=\"1008\">\r\n    <p data-start=\"897\" data-end=\"1008\">Un <strong data-start=\"900\" data-end=\"917\">sensor f&iacute;sico<\/strong>, como un detector de temperatura o movimiento, conectado mediante <strong data-start=\"984\" data-end=\"994\">OPC-UA<\/strong> o <strong data-start=\"997\" data-end=\"1005\">MQTT<\/strong>.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1009\" data-end=\"1069\">\r\n    <p data-start=\"1011\" data-end=\"1069\">Una <strong data-start=\"1015\" data-end=\"1027\">API REST<\/strong> que devuelve estados o acepta comandos.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1070\" data-end=\"1166\">\r\n    <p data-start=\"1072\" data-end=\"1166\">Un <strong data-start=\"1075\" data-end=\"1115\">dispositivo industrial o electr&oacute;nico<\/strong>, como una b&aacute;scula, un motor o una c&eacute;lula de carga.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"994\" data-end=\"1157\">En resumen, <strong data-start=\"1180\" data-end=\"1246\">todo aquello que vive fuera del motor pero puede influir en &eacute;l<\/strong> se considera un recurso externo.<\/p>\r\n<h3>Un solo mecanismo para todos<\/h3>\r\n<p data-start=\"1325\" data-end=\"1502\">GPSS-Plus no diferencia entre un sensor y una base de datos.<br data-start=\"1385\" data-end=\"1388\">\r\nAmbos son tratados de forma uniforme mediante el recurso <code><strong data-start=\"1445\" data-end=\"1456\">BRIDGER<\/strong><\/code>, que act&uacute;a como interfaz universal de acceso.<\/p>\r\n<p data-start=\"1504\" data-end=\"1710\">Esto simplifica enormemente el modelo mental:<br data-start=\"1549\" data-end=\"1552\">\r\npuedes sustituir un sensor real por una tabla MySQL, o por un archivo local, <strong data-start=\"1629\" data-end=\"1657\">sin reescribir tu modelo<\/strong>, simplemente cambiando la configuraci&oacute;n del <code>BRIDGER<\/code>.<\/p>\r\n<h3>El sensor de ejemplo: OPC-UA Virtual Device<\/h3>\r\n<p data-start=\"1770\" data-end=\"2022\"><strong data-start=\"1770\" data-end=\"1834\">OPC-UA (Open Platform Communications &ndash; Unified Architecture)<\/strong> es un est&aacute;ndar industrial para la comunicaci&oacute;n estructurada entre dispositivos y sistemas.<br data-start=\"1925\" data-end=\"1928\">\r\nPermite intercambiar <strong data-start=\"1949\" data-end=\"1979\">nodos, atributos y eventos<\/strong> de manera segura, jer&aacute;rquica y extensible.<\/p>\r\n<p data-start=\"2024\" data-end=\"2181\">En GPSS-Plus puedes conectarte tanto a un <strong data-start=\"2066\" data-end=\"2090\">servidor OPC-UA real<\/strong>, como al <strong data-start=\"2100\" data-end=\"2132\">dispositivo virtual incluido<\/strong> para pruebas y desarrollo sin hardware f&iacute;sico.<\/p>\r\n<p data-start=\"2183\" data-end=\"2236\">Este dispositivo virtual expone nodos simulados como:<\/p>\r\n<ul data-start=\"2069\" data-end=\"2152\">\r\n    <li data-start=\"2069\" data-end=\"2086\">\r\n    <p data-start=\"2071\" data-end=\"2086\"><code data-start=\"2071\" data-end=\"2086\">Sensor_Temp_1<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"2087\" data-end=\"2105\">\r\n    <p data-start=\"2089\" data-end=\"2105\"><code data-start=\"2089\" data-end=\"2105\">Motor_X.Status<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"2106\" data-end=\"2125\">\r\n    <p data-start=\"2108\" data-end=\"2125\"><code data-start=\"2108\" data-end=\"2125\">Contador_Piezas<\/code><\/p>\r\n    <\/li>\r\n    <li data-start=\"2126\" data-end=\"2152\">\r\n    <p data-start=\"2128\" data-end=\"2152\"><code data-start=\"2128\" data-end=\"2152\">Puerta_Acceso_A.IsOpen<\/code><\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2154\" data-end=\"2252\">Podr&aacute;s <strong data-start=\"2322\" data-end=\"2368\">leer, escribir o suscribirte a sus cambios<\/strong> usando siempre el mismo mecanismo del <code>BRIDGER<\/code>.<\/p>\r\n<p data-start=\"2448\" data-end=\"2489\">En los siguientes cap&iacute;tulos veremos c&oacute;mo:<\/p>\r\n<ul data-start=\"2491\" data-end=\"2671\">\r\n    <li data-start=\"2491\" data-end=\"2553\">\r\n    <p data-start=\"2493\" data-end=\"2553\">Detectar eventos, como la apertura de una puerta simulada.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2554\" data-end=\"2626\">\r\n    <p data-start=\"2556\" data-end=\"2626\">Acceder a sistemas de archivos o servicios en l&iacute;nea de solo lectura.<\/p>\r\n    <\/li>\r\n    <li data-start=\"2627\" data-end=\"2671\">\r\n    <p data-start=\"2629\" data-end=\"2671\">Ejecutar aplicaciones o scripts remotos.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p data-start=\"2673\" data-end=\"2841\">Tanto el <strong data-start=\"2682\" data-end=\"2712\">dispositivo OPC-UA virtual<\/strong> como el <strong data-start=\"2721\" data-end=\"2738\">middleware.js<\/strong> pueden descargarse y modificarse libremente para adaptarlos a tus propias necesidades o hardware real.<\/p>",
                "descripcion": "",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "353",
                "nombre": "BRIDGE_SUBSCRIPTION obtener datos en tiempo real",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\nFacility {NAME:facility1,X:446,Y:514}\r\n\r\nGraphic {Type:TEXT,Name:Text1,X:376,Y:358}\r\nGraphic {Type:TEXT,Name:TextError,X:376,Y:330,Text:\"On error....\"}\r\n\r\nInitial options,{user:\"user\", pass:\"1234\"}\r\n\r\nBRIDGER {NAME:bridge1,X:375,Y:398\r\n\t,SERVER:\"wss:\/\/bridger.gpss-plus.com:3000\"\r\n\t,CLIENT:\"opc.tcp:\/\/opcua.gpss-plus.com:4840\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS1,X:264,Y:511}\r\n\r\nPOSITION {NAME:POS2,X:658,Y:509}\r\n\r\n\r\nSTART 100\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"Sensor_Door_IsOpen\"\r\n    , TRIGGER: on_open \r\n    }\r\n\r\n\tTERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 0,0,0,0 {NAME:GEN1,X:141,Y:351}\r\n    ADVANCE 5,0 {TO:POS1}\r\n    ADVANCE 5,0 {TO:facility1}\r\n    SEIZE facility1\r\n    advance 10,10\r\n    RELEASE facility1\r\n    ADVANCE 10,0 {TO:POS2}\r\nENDGENERATE 1\r\n;----------------------------------------------------------------------------\r\nprocedure on_open\r\n        move {name:Text1,TEXT:\"DOOR P$(PARAM_A.VALUE)\"}\r\n        if (P$(PARAM_A.VALUE.EXIST) && P$(PARAM_A.VALUE)==1)\r\n    \tNEW GEN1\r\n    \tendif\r\n\tTERMINATE_VE\r\nendprocedure\r\n;----------------------------------------------------------------------------\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:\"P$PARAM_A\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p data-start=\"444\" data-end=\"575\">Veamos un ejemplo concreto para entender c&oacute;mo fluye la informaci&oacute;n entre <strong data-start=\"517\" data-end=\"530\">GPSS-Plus<\/strong>, <strong data-start=\"532\" data-end=\"549\">middleware.js<\/strong> y el <strong data-start=\"555\" data-end=\"574\">servidor OPC-UA<\/strong>.<\/p>\r\n<p data-start=\"577\" data-end=\"805\">Vamos a modificar el comportamiento de un bloque <strong data-start=\"626\" data-end=\"638\">GENERATE<\/strong> para que <strong data-start=\"648\" data-end=\"709\">las entidades se creen cuando un sensor f&iacute;sico (simulado)<\/strong> detecte la apertura de una puerta &mdash;en lugar de hacerlo por par&aacute;metros de tiempo o probabilidad.<\/p>\r\n<h3 data-start=\"812\" data-end=\"853\"><strong data-start=\"816\" data-end=\"853\">Creando el puente de comunicaci&oacute;n<\/strong><\/h3>\r\n<p data-start=\"855\" data-end=\"934\">Lo primero es definir el <strong data-start=\"880\" data-end=\"891\">BRIDGER<\/strong>, que ser&aacute; el enlace con el mundo exterior:<\/p>\r\n<pre>\r\nInitial options,{user:&quot;user&quot;, pass:&quot;1234&quot;}\r\n\r\nBRIDGER {NAME:bridge1,X:333,Y:444\r\n&nbsp; &nbsp; ,SERVER:&quot;wss:\/\/bridger.gpss-plus.com:3000&quot;\r\n&nbsp; &nbsp; ,CLIENT:&quot;opc.tcp:\/\/opcua.gpss-plus.com:4840&quot;\r\n&nbsp; &nbsp; ,OPTIONS:V$options\r\n&nbsp; &nbsp; ,ON_ERROR:bridge1_on_error\r\n&nbsp; &nbsp; }<\/pre>\r\n<p data-start=\"1183\" data-end=\"1214\">Los par&aacute;metros principales son:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"1218\" data-end=\"1226\">NAME<\/strong>: identificador interno del BRIDGER (por ejemplo, <code data-start=\"1276\" data-end=\"1285\">bridge1<\/code>).<\/li>\r\n    <li><strong data-start=\"1290\" data-end=\"1300\">SERVER<\/strong>: direcci&oacute;n WebSocket segura donde escucha el <code data-start=\"1346\" data-end=\"1361\">middleware.js<\/code>.<\/li>\r\n    <li><strong data-start=\"1365\" data-end=\"1375\">CLIENT<\/strong>: direcci&oacute;n del servidor OPC-UA (normalmente en la misma red que el middleware).<\/li>\r\n    <li><strong data-start=\"1458\" data-end=\"1469\">OPTIONS<\/strong>: par&aacute;metros adicionales (como usuario y contrase&ntilde;a) requeridos para la conexi&oacute;n.<\/li>\r\n    <li><strong data-start=\"1553\" data-end=\"1565\">ON_ERROR<\/strong>: <em data-start=\"1567\" data-end=\"1576\">trigger<\/em> que se ejecutar&aacute; si ocurre un error en la comunicaci&oacute;n.<\/li>\r\n<\/ul>\r\n<p>M&aacute;s adelante veremos que tanto el <code data-start=\"1670\" data-end=\"1685\">middleware.js<\/code> como el cliente OPC-UA virtual pueden modificarse para adaptarse a otras topolog&iacute;as o protocolos.<\/p>\r\n<h3 data-start=\"1790\" data-end=\"1824\"><strong data-start=\"1794\" data-end=\"1824\">Suscribi&eacute;ndose a un sensor<\/strong><\/h3>\r\n<p data-start=\"1826\" data-end=\"1990\">Una vez definido el puente, debemos <strong data-start=\"1862\" data-end=\"1885\">iniciar la conexi&oacute;n<\/strong> dentro del <code data-start=\"1897\" data-end=\"1906\">PRE_RUN<\/code> del modelo, y <strong data-start=\"1921\" data-end=\"1937\">suscribirnos<\/strong> a la variable que representa el estado de la puerta:<\/p>\r\n<pre>\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n&nbsp; &nbsp; &nbsp; , VARIABLE: &quot;Sensor_Door_IsOpen&quot;\r\n&nbsp; &nbsp; , TRIGGER: on_open&nbsp;\r\n&nbsp; &nbsp; }<\/pre>\r\n<p>Esta instrucci&oacute;n mantiene un canal abierto con el sensor para recibir <strong data-start=\"2177\" data-end=\"2207\">notificaciones autom&aacute;ticas<\/strong> cada vez que cambie su valor.<\/p>\r\n<p>Donde de nuevo, los par&aacute;metros son m&iacute;nimos:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"2254\" data-end=\"2262\">NAME<\/strong>: el nombre del BRIDGER que gestionar&aacute; la comunicaci&oacute;n.<\/li>\r\n    <li><strong data-start=\"2322\" data-end=\"2334\">VARIABLE<\/strong>: el nombre exacto de la variable publicada por el sensor.&nbsp;(La variable expuesta que m&aacute;s adelante veremos c&oacute;mo se obtiene)<\/li>\r\n    <li><strong data-start=\"2397\" data-end=\"2408\">TRIGGER<\/strong>: el procedimiento que procesar&aacute; cada actualizaci&oacute;n recibida.<\/li>\r\n<\/ul>\r\n<p>Las suscripciones permiten recibir datos en tiempo real sin necesidad de ejecutar lecturas peri&oacute;dicas.<\/p>\r\n<h3 data-start=\"2582\" data-end=\"2615\"><strong data-start=\"2586\" data-end=\"2615\">El procedimiento asociado<\/strong><\/h3>\r\n<p data-start=\"2617\" data-end=\"2781\">Cada vez que se recibe un nuevo valor, se ejecuta el <em data-start=\"2670\" data-end=\"2679\">trigger<\/em> indicado.<br data-start=\"2689\" data-end=\"2692\">\r\nEl par&aacute;metro <code data-start=\"2705\" data-end=\"2722\">P$(PARAM_A.VALUE)<\/code> contendr&aacute; el valor actual del sensor (0 o 1 en este caso). PARAM_B contiene el n&uacute;mero de la entidad invocante y PARAM_C el tiempo.<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\nprocedure on_open\r\n&nbsp; &nbsp;move {name:Text1,TEXT:&quot;DOOR P$(PARAM_A.VALUE)&quot;}\r\n&nbsp; &nbsp; &nbsp;if (P$(PARAM_A.VALUE.EXIST) &amp;&amp; P$(PARAM_A.VALUE)==1)\r\n&nbsp; &nbsp; &nbsp; &nbsp; NEW GEN1\r\n&nbsp; &nbsp;&nbsp; endif\r\n&nbsp; &nbsp;TERMINATE_VE\r\nendprocedure\r\n<\/pre>\r\n<p>En este ejemplo, <strong data-start=\"2992\" data-end=\"3060\">cada vez que el sensor detecta que la puerta se abre (valor = 1)<\/strong>, el sistema <strong data-start=\"3073\" data-end=\"3104\">crea una nueva entidad GEN1<\/strong>, simulando la entrada de una persona.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>Tambi&eacute;n cabe la opci&oacute;n de anular la subscripci&oacute;n:<\/p>\r\n<pre>\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n&nbsp; &nbsp; &nbsp; &nbsp; , VARIABLE: &quot;Sensor_Door_IsOpen&quot;\r\n&nbsp; &nbsp; &nbsp; &nbsp; }<\/pre>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "354",
                "nombre": "BRIDGE_BROWSE Vista del dispositivo ",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\nGraphic {Type:TEXT,Name:Text1,X:653,Y:294}\r\nGraphic {Type:TEXT,Name:TextError,X:440,Y:329,Text:\"On error....\"}\r\n\r\nInitial options,{user:\"user\", pass:\"1234\"}\r\n\r\nBRIDGER {NAME:bridge1,X:443,Y:405\r\n\t,SERVER:\"wss:\/\/bridger.gpss-plus.com:3000\"\r\n\t,CLIENT:\"opc.tcp:\/\/opcua.gpss-plus.com:4840\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS1,X:209,Y:347}\r\n\r\nSTART 1\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n    BRIDGE_BROWSE { NAME: bridge1\r\n    , TRIGGER: on_browse \r\n    }\r\n\r\n\tTERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 10,0,0,1 {NAME:GEN1,X:141,Y:351}\r\n    ADVANCE 5,0 {TO:POS1}\r\nENDGENERATE 1\r\n;----------------------------------------------------------------------------\r\nprocedure on_browse\r\n        move {name:Text1,TEXT:\"BROWSE P$(PARAM_A)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n;----------------------------------------------------------------------------\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:\"P$PARAM_A\"}\r\n    TERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p>Hemos visto que en el OPC-UA virtual pod&iacute;amos leer repetidamente el valores de la variable &quot;Sensor_Door_IsOpen&quot;.&nbsp;<\/p>\r\n<p data-start=\"467\" data-end=\"834\">Sin embargo, <strong data-start=\"624\" data-end=\"679\">no siempre sabremos qu&eacute; variables o m&eacute;todos existen<\/strong> dentro de un dispositivo. Por ejemplo, un servicio de archivos podr&iacute;a listar directorios, o una base de datos podr&iacute;a exponer tablas cuyos nombres cambian.<\/p>\r\n<p data-start=\"836\" data-end=\"968\">Para eso existe el bloque <strong data-start=\"862\" data-end=\"881\"><code data-start=\"864\" data-end=\"879\">BRIDGE_BROWSE<\/code><\/strong>, que permite <strong data-start=\"895\" data-end=\"930\">consultar la estructura interna<\/strong> del dispositivo o servicio conectado.<\/p>\r\n<p><strong data-start=\"979\" data-end=\"1011\">&iquest;Por qu&eacute; usar BRIDGE_BROWSE?<\/strong><\/p>\r\n<p data-start=\"1013\" data-end=\"1061\">El comando <code data-start=\"1024\" data-end=\"1039\">BRIDGE_BROWSE<\/code> sirve para descubrir:<\/p>\r\n<ul>\r\n    <li>Qu&eacute; <strong data-start=\"1069\" data-end=\"1082\">variables<\/strong> est&aacute;n disponibles.<\/li>\r\n    <li>Qu&eacute; tipo de dato tiene cada una (Boolean, Double, String, etc.).<\/li>\r\n    <li>Si son de <strong data-start=\"1185\" data-end=\"1196\">lectura<\/strong>, <strong data-start=\"1198\" data-end=\"1211\">escritura<\/strong> o ambas.<\/li>\r\n    <li>Qu&eacute; <strong data-start=\"1229\" data-end=\"1240\">m&eacute;todos<\/strong> u operaciones est&aacute;n expuestos.<\/li>\r\n<\/ul>\r\n<p data-start=\"1275\" data-end=\"1340\">En otras palabras, es el <strong data-start=\"1300\" data-end=\"1339\">mapa de capacidades del dispositivo<\/strong>.<\/p>\r\n<p>Por lo que de los 5 BLOQUES del <code>BRIDGER<\/code> vamos a ver qu&eacute; se obtiene de <code>BRIDGE_BROWSE<\/code>.<\/p>\r\n<ul>\r\n    <li>BRIDGE_READ<\/li>\r\n    <li>BRIDGE_WRITE<\/li>\r\n    <li>BRIDGE_SUBSCRIBE<\/li>\r\n    <li>BRIDGE_BROWSE<\/li>\r\n    <li>BRIDGE_CALL<\/li>\r\n<\/ul>\r\n<p><code data-start=\"1514\" data-end=\"1529\">BRIDGE_BROWSE<\/code> se ejecuta <strong data-start=\"1541\" data-end=\"1588\">autom&aacute;ticamente durante la primera conexi&oacute;n<\/strong>, pero tambi&eacute;n puede <strong data-start=\"1609\" data-end=\"1634\">invocarse manualmente<\/strong> si se desea explorar un nuevo dispositivo o verificar qu&eacute; ha cambiado.<\/p>\r\n<h3 data-start=\"1712\" data-end=\"1754\"><strong data-start=\"1716\" data-end=\"1754\">Ejemplo de salida (debug o canvas)<\/strong><\/h3>\r\n<p data-start=\"1756\" data-end=\"1858\">Al ejecutar <code data-start=\"1768\" data-end=\"1783\">BRIDGE_BROWSE<\/code>, el middleware devuelve un listado jer&aacute;rquico de las variables accesibles:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\nSensor_Motion_Detected: ns=1;i=1000 [Boolean] \r\nSensor_Door_IsOpen: ns=1;i=1001 [Boolean] \r\nPIDControl_CurrentTemperature: ns=1;i=1002 [Double] \r\nPIDControl_Setpoint: ns=1;i=1003 [Double] (writable) \r\nPIDControl_PIDOutput: ns=1;i=1004 [Double] \r\nDevice_Motor_Enabled: ns=1;i=1005 [Boolean] (writable) \r\nSystem_Battery_BatteryLevel: ns=1;i=1006 [Double] \r\nDevice_Scale_Weight: ns=1;i=1007 [Double] \r\nDevice_Scale_Trigger: ns=1;i=1008 [Boolean] (writable) \r\nDevice_Piston_IsExtended: ns=1;i=1009 [Boolean] \r\nDevice_Piston_Trigger: ns=1;i=1010 [Boolean] (writable)\r\n...\r\n...<\/pre>\r\n<p data-start=\"2411\" data-end=\"2430\">Cada l&iacute;nea muestra:<\/p>\r\n<ul>\r\n    <li>El <strong data-start=\"2437\" data-end=\"2462\">nombre de la variable<\/strong>.<\/li>\r\n    <li>Su <strong data-start=\"2471\" data-end=\"2496\">identificador interno<\/strong> (<code data-start=\"2498\" data-end=\"2511\">ns=1;i=1000<\/code>, t&iacute;pico en OPC-UA).<\/li>\r\n    <li>El <strong data-start=\"2539\" data-end=\"2555\">tipo de dato<\/strong>.<\/li>\r\n    <li>Si es o no <strong data-start=\"2572\" data-end=\"2586\">escribible<\/strong> (<code data-start=\"2588\" data-end=\"2600\">(writable)<\/code>).<\/li>\r\n    <li>Si es un m&eacute;todo ejecutable.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"2609\" data-end=\"2647\"><strong data-start=\"2613\" data-end=\"2647\">Usar BRIDGE_BROWSE manualmente<\/strong><\/h3>\r\n<p data-start=\"2649\" data-end=\"2889\">Antes de interactuar con un nuevo dispositivo, ejecuta <code data-start=\"2704\" data-end=\"2719\">BRIDGE_BROWSE<\/code> una vez para conocer su estructura.<br data-start=\"2755\" data-end=\"2758\">\r\nDespu&eacute;s podr&aacute;s usar <code data-start=\"2778\" data-end=\"2791\">BRIDGE_READ<\/code>, <code data-start=\"2793\" data-end=\"2807\">BRIDGE_WRITE<\/code> o <code data-start=\"2810\" data-end=\"2828\">BRIDGE_SUBSCRIBE<\/code> de forma precisa sobre las variables que hayas identificado.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "355",
                "nombre": "BRIDGE_WRITE BRIDGE_READ el actuador",
                "texto": "SYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\nGraphic {Type:TEXT,Name:Text1,X:653,Y:294}\r\nGraphic {Type:TEXT,Name:Text2,X:653,Y:194}\r\nGraphic {Type:TEXT,Name:TextError,X:440,Y:329,Text:\"On error....\"}\r\n\r\nInitial options,{user:\"user\", pass:\"1234\"}\r\n\r\nBRIDGER {NAME:bridge1,X:443,Y:405\r\n\t,SERVER:\"wss:\/\/bridger.gpss-plus.com:3000\"\r\n\t,CLIENT:\"opc.tcp:\/\/opcua.gpss-plus.com:4840\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS1,X:209,Y:347}\r\nPOSITION {NAME:POS2,X:409,Y:347}\r\n\r\nSTART 100\r\n\r\n;*****************************************************\r\nGENERATE 3,0,0,10 {NAME:GEN1,X:141,Y:351}\r\n\r\n\tASSIGN values, [\"Device_Motor_Enabled\"]\r\n\tBRIDGE_READ {name:bridge1, trigger:on_read, VALUES:V$values}\r\n\tADVANCE 5,0 {TO:POS1}\r\n\r\n\tif (D$N %2 == 0 )\r\n    ASSIGN values, { Device_Motor_Enabled: 0 }\r\n\tBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\n    else\r\n    ASSIGN values, { Device_Motor_Enabled: 1 }\r\n\tBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\n    endif\r\n    \r\n\tADVANCE 5,0 {TO:POS2}\r\nENDGENERATE 1\r\n;----------------------------------------------------------------------------\r\nprocedure on_write\r\n        move {name:Text1,TEXT:\"AC1$ WRITE P$(PARAM_A)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n;----------------------------------------------------------------------------\r\nprocedure on_read\r\n        move {name:Text2,TEXT:\"AC1$ READ P$(PARAM_A)  \\n::  Valor: P$(PARAM_A.Device_Motor_Enabled)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n;----------------------------------------------------------------------------\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:\"AC1$ P$PARAM_A\"}\r\n   TERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p>Hasta ahora hemos recibido datos del mundo exterior.<br data-start=\"538\" data-end=\"541\">\r\nAhora aprenderemos a <strong data-start=\"562\" data-end=\"586\">enviar instrucciones<\/strong> y <strong data-start=\"589\" data-end=\"617\">leer valores espec&iacute;ficos<\/strong> desde GPSS-Plus hacia un dispositivo OPC-UA, MQTT o cualquier otro conectado por el <code data-start=\"702\" data-end=\"717\">middleware.js<\/code><\/p>\r\n<h3 data-start=\"725\" data-end=\"759\"><strong data-start=\"729\" data-end=\"759\">Escribir en un dispositivo<\/strong><\/h3>\r\n<p data-start=\"761\" data-end=\"836\">Dar una orden a un actuador es tan simple como asignar un valor y enviarlo:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\n    ASSIGN values, { Device_Motor_Enabled: 1 }\r\n    BRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}<\/pre>\r\n<p data-start=\"959\" data-end=\"1115\">Esto escribe en la variable <code data-start=\"987\" data-end=\"1009\">Device_Motor_Enabled<\/code> del dispositivo, encendiendo el motor (1 = ON, 0 = OFF).<br data-start=\"1066\" data-end=\"1069\">\r\nLa estructura del comando es siempre la misma:<\/p>\r\n<ul>\r\n    <li><strong data-start=\"1119\" data-end=\"1130\">VALUES:<\/strong> un conjunto clave-valor con las variables a modificar.<\/li>\r\n    <li><strong data-start=\"1190\" data-end=\"1202\">TRIGGER:<\/strong> el procedimiento que se ejecutar&aacute; cuando el middleware confirme la entrega.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<h3 data-start=\"1290\" data-end=\"1328\"><strong data-start=\"1294\" data-end=\"1328\">Leer el estado de una variable<\/strong><\/h3>\r\n<p data-start=\"1330\" data-end=\"1369\">La lectura se realiza de forma an&aacute;loga:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\n&nbsp; &nbsp; ASSIGN values, [&quot;Device_Motor_Enabled&quot;]\r\n&nbsp; &nbsp; BRIDGE_READ {name:bridge1, trigger:on_read, VALUES:V$values}<\/pre>\r\n<p>En este caso, la lista <code data-start=\"1510\" data-end=\"1536\">[&quot;Device_Motor_Enabled&quot;]<\/code> indica qu&eacute; variables queremos consultar.<br data-start=\"1577\" data-end=\"1580\">\r\nEl valor llegar&aacute; de forma <strong data-start=\"1606\" data-end=\"1619\">as&iacute;ncrona<\/strong>, y se recibir&aacute; dentro del <em data-start=\"1646\" data-end=\"1655\">trigger<\/em> correspondiente (<code data-start=\"1673\" data-end=\"1682\">on_read<\/code>).<\/p>\r\n<h3 data-start=\"1691\" data-end=\"1729\"><strong data-start=\"1695\" data-end=\"1729\">Ejecuci&oacute;n as&iacute;ncrona o s&iacute;ncrona<\/strong><\/h3>\r\n<p data-start=\"1731\" data-end=\"1897\">Por defecto, las operaciones <strong data-start=\"1760\" data-end=\"1768\">READ<\/strong> y <strong data-start=\"1771\" data-end=\"1780\">WRITE<\/strong> son <strong data-start=\"1785\" data-end=\"1799\">as&iacute;ncronas<\/strong>:<br data-start=\"1800\" data-end=\"1803\">\r\nel motor GPSS-Plus contin&uacute;a su ejecuci&oacute;n sin detenerse a esperar la respuesta del dispositivo.<\/p>\r\n<p data-start=\"1899\" data-end=\"2008\">Esto es lo habitual en sistemas f&iacute;sicos, donde la comunicaci&oacute;n puede tardar varios cientos de milisegundos.<\/p>\r\n<p data-start=\"2010\" data-end=\"2147\">Si el recurso es <strong data-start=\"2027\" data-end=\"2040\">inmediato<\/strong> (por ejemplo, un fichero local o una base de datos interna), puedes forzar una operaci&oacute;n <strong data-start=\"2130\" data-end=\"2142\">s&iacute;ncrona<\/strong> con:<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\n BRIDGE_READ { name:bridge1, VALUES:V$values, SYNC:1, trigger:on_read }<\/pre>\r\n<p>De este modo, la entidad y el motor se <strong data-start=\"2261\" data-end=\"2272\">bloquea<\/strong> hasta recibir la respuesta.<br data-start=\"2300\" data-end=\"2303\">\r\nDebe usarse solo en contextos controlados, ya que detener el tiempo del simulador afecta a todo el sistema.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "338",
                "nombre": "Ejemplo completo: control de pesaje y clasificación",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\nRestroom {NAME:LiberadorUnitario,X:155,Y:332}\r\n\r\nQueuer {NAME:zonaBascula,X:163,Y:32}\r\nGraphic {NAME:lineZonaBascula,Type:LINE,color:#FF0000, X1:82,Y1:64,X2:260,Y2:65}\r\n\r\nRestroom {NAME:RestroomBascula,X:226,Y:110}\r\n\r\ninitial pesoLimite,8\r\n\r\nInitial options,{user:\"user\", pass:\"1234\"}\r\n\r\nBRIDGER {NAME:bridge1,X:733,Y:556\r\n\t,SERVER:\"wss:\/\/bridger.gpss-plus.com:3000\"\r\n\t,CLIENT:\"opc.tcp:\/\/opcua.gpss-plus.com:4840\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS1,X:97,Y:437}\r\n\r\nPOSITION {NAME:POS2,X:95,Y:183}\r\n\r\n\r\nPOSITION {NAME:PosBascula,X:225,Y:199}\r\nPOSITION {NAME:PosSeparador,X:387,Y:201}\r\nPOSITION {NAME:PosPesados,X:513,Y:302}\r\nPOSITION {NAME:PosPesados2,X:594,Y:302}\r\nPOSITION {NAME:PosLigeros,X:511,Y:197}\r\nPOSITION {NAME:PosLigeros2,X:592,Y:197}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:167,Y:484,Text:\"On OPEN\"}\r\nGraphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:\"Error\"}\r\n\r\nGraphic {NAME:lineBascula,Type:LINE,color:#FF0000, X1:191,Y1:176,X2:263,Y2:176}\r\nGraphic {NAME:txtBascula,Type:TEXT,X:226,Y:156,Text:\"bascula\"}\r\n\r\nGraphic {NAME:lineSeparador,Type:LINE,color:#FF0000, X1:363,Y1:201,X2:432,Y2:201}\r\nGraphic {NAME:txtSeparador,Type:TEXT,X:394,Y:143,Text:\"separador\"}\r\n\r\nSTART 10\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"Sensor_Door_IsOpen\"\r\n    , TRIGGER: on_open \r\n    }\r\nsavevalue pesoActual,0\r\nsavevalue pesoAnterior,X$pesoActual\r\n\r\n\tBRIDGE_SUBSCRIPTION { NAME: bridge1\r\n        , VARIABLE: \"Device_Scale_Weight\"\r\n        , TRIGGER: on_subscription_bascula \r\n        }\r\n\tBRIDGE_SUBSCRIPTION { NAME: bridge1\r\n        , VARIABLE: \"Device_Piston_IsExtended\"\r\n        , TRIGGER: on_subscription_piston \r\n        }\r\n\r\n\r\nASSIGN values, { Device_Scale_Trigger: 0 }\r\nBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\n \r\n\tTERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 0,0,0,0 {NAME:GEN1,X:91,Y:547}\r\n\r\nADVANCE 5,0 {TO:POS1,flow:1}\r\nADVANCE 5,0 {TO:LiberadorUnitario,flow:1}\r\nif (R$(zonaBascula,in)>0)\r\n\trest LiberadorUnitario\r\nendif\r\nqueue zonaBascula\r\n\r\nADVANCE 5,0 {TO:POS2,flow:1}\r\nADVANCE 5,0 {TO:PosBascula,flow:1}\r\n\r\nASSIGN values, { Device_Scale_Trigger: 1 }\r\nBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\n\r\nrest RestroomBascula\r\ndepart zonaBascula\r\ncall liberarRestroomBascula\r\n\r\nADVANCE 2\r\nmod {subtitle:\"P$peso Kg\"}\r\nif (P$peso > X$pesoLimite)\r\n    mod {color:\"red\"}\r\nelse\r\n    mod {color:\"green\"}\r\nendif\r\n\r\n\r\n\r\nASSIGN values, { Device_Scale_Trigger: 0 }\r\nBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\n\r\ntimeout moverPiston,4.3,P$peso\r\n\r\nADVANCE 5,0 {TO:PosSeparador,flow:1}\r\n\r\n\r\nif (P$peso > X$pesoLimite)\r\n    ADVANCE 3,0 {TO:PosPesados}\r\n\tADVANCE 3,0 {TO:PosPesados2,flow:1}\r\nelse\r\n    ADVANCE 3,0 {TO:PosLigeros}\r\n \tADVANCE 3,0 {TO:PosLigeros2,flow:1}\r\nendif\r\n\r\n\r\n   \r\nENDGENERATE 1\r\n;----------------------------------------------------------------------------\r\n\r\nprocedure on_open\r\n       move {name:Text1,TEXT:\"DOOR P$(PARAM_A.VALUE)\"}\r\n       if (P$(PARAM_A.VALUE.EXIST))\r\n       if (P$(PARAM_A.VALUE)==1)\r\n    \tNEW GEN1\r\n    \tendif\r\n        endif\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:\"P$PARAM_A\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n;-------------------------\r\nprocedure on_subscription_bascula\r\n\tsavevalue pesoActual,P$(PARAM_A.VALUE)\r\n    move {name:txtBascula,TEXT:\"X$pesoActual Kg\"}\r\n    if (ABS(X$pesoAnterior-X$pesoActual)<=0.1 && X$pesoActual>1)\r\n\t    FOREACH entidad, IN_RESOURCE, RestroomBascula\r\n\t    \tassign peso,round(X$pesoActual,1), P$entidad\r\n\t\tENDFOREACH\r\n    \r\n\t    wake RestroomBascula\r\n    endif\r\n\tsavevalue pesoAnterior,X$pesoActual\r\n\r\n\tTERMINATE_VE\r\nendprocedure\r\n;-------------------------\r\nprocedure on_write\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_subscription_piston\r\n\tif (P$(PARAM_A.VALUE)==1)\r\n    move {name:lineSeparador,Y2:220}\r\n    else\r\n    move {name:lineSeparador,Y2:200}\r\n    endif\r\n\tmove {name:txtSeparador,text:\"Piston P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n\r\nprocedure liberarRestroomBascula\r\n\tassign hecho,0\r\n\t    FOREACH entidad, IN_RESOURCE, zonaBascula\r\n            if (P$hecho==0)\r\n\t    \twake RestroomBascula,P$entidad\r\n            endif\r\n            assign hecho,1\r\n\t\tENDFOREACH\r\nendprocedure\r\n\r\n\r\nprocedure moverPiston\r\n    if (P$PARAM_A>X$pesoLimite)\r\n     ASSIGN values, { Device_Piston_Trigger: 1 }\r\n     BRIDGE_WRITE { name:bridge1, VALUES:V$values, TRIGGER:on_write }\r\n\tassign tmp,\"pesados\"\r\n    else \r\n    assign tmp,\"ligeros\"\r\n    endif\r\n ;   move {name:txtSeparador,text:\"Piston P$(PARAM_A) Kg -- P$tmp\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p data-start=\"681\" data-end=\"769\">En este ejemplo vamos a ver <strong data-start=\"715\" data-end=\"768\">todos los elementos del sistema en funcionamiento<\/strong>.<\/p>\r\n<p data-start=\"771\" data-end=\"984\">El modelo representa una <strong data-start=\"796\" data-end=\"832\">l&iacute;nea de producci&oacute;n automatizada<\/strong> en la que los paquetes llegan uno a uno, son pesados en una b&aacute;scula, y seg&uacute;n su peso son desviados hacia un camino u otro mediante un pist&oacute;n neum&aacute;tico.<\/p>\r\n<h3 data-start=\"991\" data-end=\"1022\"><strong data-start=\"995\" data-end=\"1022\">Descripci&oacute;n del proceso<\/strong><\/h3>\r\n<ol data-start=\"1024\" data-end=\"2098\">\r\n    <li data-start=\"1024\" data-end=\"1231\">\r\n    <p data-start=\"1027\" data-end=\"1231\"><strong data-start=\"1027\" data-end=\"1051\">Detecci&oacute;n de entrada<\/strong><br data-start=\"1051\" data-end=\"1054\">\r\n    Un sensor de puerta detecta la llegada de un nuevo paquete (simboliza un producto que entra en la zona).<br data-start=\"1172\" data-end=\"1175\">\r\n    Cada detecci&oacute;n genera una nueva entidad en GPSS-Plus.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1233\" data-end=\"1467\">\r\n    <p data-start=\"1236\" data-end=\"1467\"><strong data-start=\"1236\" data-end=\"1265\">Control de flujo unitario<\/strong><br data-start=\"1265\" data-end=\"1268\">\r\n    Un <em data-start=\"1274\" data-end=\"1284\">restroom<\/em> act&uacute;a como <strong data-start=\"1296\" data-end=\"1318\">liberador unitario<\/strong>, permitiendo que los paquetes pasen de uno en uno hacia la b&aacute;scula.<br data-start=\"1386\" data-end=\"1389\">\r\n    El siguiente paquete solo avanza cuando el anterior ha terminado el pesaje.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1469\" data-end=\"1651\">\r\n    <p data-start=\"1472\" data-end=\"1651\"><strong data-start=\"1472\" data-end=\"1482\">Pesaje<\/strong><br data-start=\"1482\" data-end=\"1485\">\r\n    Al llegar a la b&aacute;scula, la entidad activa el sensor (<code data-start=\"1541\" data-end=\"1567\">Device_Scale_Trigger = 1<\/code>).<br data-start=\"1569\" data-end=\"1572\">\r\n    Cuando el peso se estabiliza, el sistema asigna el valor le&iacute;do a la entidad.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1653\" data-end=\"1875\">\r\n    <p data-start=\"1656\" data-end=\"1875\"><strong data-start=\"1656\" data-end=\"1673\">Clasificaci&oacute;n<\/strong><br data-start=\"1673\" data-end=\"1676\">\r\n    Si el peso supera un umbral (<code data-start=\"1708\" data-end=\"1720\">pesoLimite<\/code>), se activa el pist&oacute;n (<code data-start=\"1744\" data-end=\"1771\">Device_Piston_Trigger = 1<\/code>) que desv&iacute;a el paquete hacia la zona de pesados.<br data-start=\"1820\" data-end=\"1823\">\r\n    Si no, contin&uacute;a recto hacia la zona de ligeros.<\/p>\r\n    <\/li>\r\n    <li data-start=\"1877\" data-end=\"2098\">\r\n    <p data-start=\"1880\" data-end=\"2098\"><strong data-start=\"1880\" data-end=\"1921\">Sincronizaci&oacute;n con actuadores f&iacute;sicos<\/strong><br data-start=\"1921\" data-end=\"1924\">\r\n    El pist&oacute;n se retrae autom&aacute;ticamente.<br data-start=\"1963\" data-end=\"1966\">\r\n    GPSS-Plus se <strong data-start=\"1982\" data-end=\"2044\">suscribe al estado del pist&oacute;n (<code data-start=\"2015\" data-end=\"2041\">Device_Piston_IsExtended<\/code>)<\/strong> para saber cu&aacute;ndo est&aacute; listo para el siguiente ciclo.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"771\" data-end=\"984\"><b>Arquitectura del sistema:<\/b><\/p>\r\n<pre>\r\n[Sensor Puerta]  -&gt; genera entidad\r\n         &darr;\r\n [Liberador Unitario]\r\n         &darr;\r\n       [B&aacute;scula]  &larr;&rarr; OPC-UA Device_Scale_Weight\r\n         &darr;\r\n   [Separador \/ Pist&oacute;n]\r\n         &darr;\r\n [Salida Ligeros] \/ [Salida Pesados]\r\n\r\n<\/pre>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "356",
                "nombre": "Descargas e instalación componentes BRIDGER",
                "texto": "<p>Para ejecutar los ejemplos m&aacute;s avanzados y conectar GPSS-Plus con el mundo real, ser&aacute; necesario <strong data-start=\"679\" data-end=\"715\">instalar tu propio middleware.js<\/strong> y, opcionalmente, el <strong data-start=\"737\" data-end=\"767\">dispositivo virtual OPC-UA<\/strong> y los <strong data-start=\"774\" data-end=\"809\">firmwares de microcontroladores<\/strong>.<\/p>\r\n<hr>\r\n<p><a href=\"https:\/\/gpss-plus.com\/BRIDGER\/middleware_v_1.03.zip\"><b>middleware.js<\/b><\/a><b>&nbsp;: Versi&oacute;n 1.03<\/b><\/p>\r\n<p data-start=\"857\" data-end=\"1041\">El <em data-start=\"860\" data-end=\"872\">middleware<\/em> act&uacute;a como capa intermedia entre GPSS-Plus y los sensores o actuadores f&iacute;sicos.<br data-start=\"952\" data-end=\"955\">\r\nDebe ejecutarse en un entorno <strong data-start=\"985\" data-end=\"996\">Node.js<\/strong> con comunicaci&oacute;n segura (<strong data-start=\"1022\" data-end=\"1037\">HTTPS \/ WSS<\/strong>).<\/p>\r\n<h4 data-start=\"1043\" data-end=\"1070\"><strong data-start=\"1048\" data-end=\"1070\">Requisitos previos<\/strong><\/h4>\r\n<ul>\r\n    <li>Node.js versi&oacute;n 18 o superior.<\/li>\r\n    <li>Certificados SSL v&aacute;lidos (necesarios para el canal seguro).<\/li>\r\n    <li>Conectividad de red con GPSS-Plus y los dispositivos OPC-UA o MQTT.<\/li>\r\n<\/ul>\r\n<p data-start=\"1172\" data-end=\"1239\">&nbsp;<\/p>\r\n<p data-start=\"1172\" data-end=\"1239\"><b>Instalaci&oacute;n de certificados<\/b><\/p>\r\n<p>&nbsp;<\/p>\r\n<p data-start=\"1283\" data-end=\"1376\">Dependiendo de si lo ejecutar&aacute;s en una red privada o en un dominio p&uacute;blico, hay dos opciones:<\/p>\r\n<h4 data-start=\"1378\" data-end=\"1419\"><strong data-start=\"1383\" data-end=\"1419\">a) Entorno local &ndash; usando mkcert<\/strong><\/h4>\r\n<p data-start=\"1421\" data-end=\"1455\">Ideal para pruebas en red interna.<\/p>\r\n<p>Ejemplo para mcert sobre Debian:<\/p>\r\n<pre>\r\n# Crea una entrada de DNS en tui dominio a tu IP local. Ejemplo:\r\nip11.midominio.com -&gt; 192.168.1.11\r\n\r\n<span class=\"hljs-comment\"># Instalar el paquete libnss3-tools (que contiene certutil)<\/span>\r\nsudo apt install libnss3-tools\r\n\r\n<span class=\"hljs-comment\"># Descarga mkcert<\/span>\r\nwget https:\/\/github.com\/FiloSottile\/mkcert\/releases\/latest\/download\/mkcert-v1.4.4-linux-arm\r\n\r\nmv mkcert-v1.4.4-linux-arm \/usr\/local\/bin\/mkcert\r\nchmod +x \/usr\/local\/bin\/mkcert\r\nmkcert -version (v1.4.4)\r\n\r\n# instalar el certificado raiz\r\nmkcert -install\r\n\r\n# crea el certificado para tu subdominio particular de la entrada DNS creada:\r\nmkcert ip11.tudomino.com 192.168.1.11\r\n\r\n# Mueve los certificados a una ubicaci&oacute;n est&aacute;ndar:\r\nmkdir -p \/etc\/ssl\/localcerts\r\nmv ip11.tudominio.com+1.pem \/etc\/ssl\/localcerts\/\r\nmv ip11.tudominio.com+1-key.pem \/etc\/ssl\/localcerts\/\r\nchown root:root \/etc\/ssl\/localcerts\/*\r\nchmod 600 \/etc\/ssl\/localcerts\/*-key.pem\r\n\r\n#Para evitar errores de confianza, instala el archivo rootCA.pem (ubicado en ~\/.local\/share\/mkcert) \r\n#como &ldquo;Entidad de certificaci&oacute;n ra&iacute;z de confianza&rdquo; en tu sistema.\r\n mkcert -CAROOT\r\n\/root\/.local\/share\/mkcert (ruta probable de localicaci&oacute;n del rectificado rootCA.pem)\r\n<\/pre>\r\n<h4 data-start=\"2367\" data-end=\"2418\"><strong data-start=\"2372\" data-end=\"2418\">b) Servidor p&uacute;blico &ndash; usando Let&rsquo;s Encrypt<\/strong><\/h4>\r\n<p data-start=\"2420\" data-end=\"2489\">En servidores accesibles desde Internet, utiliza certificados reales de, por ejemplo let&acute;s Encrypt<\/p>\r\n<p><b>Edita el archivo middleware.js<\/b><\/p>\r\n<p>Descarga y edita el archivo <b>middleware.js<\/b> para incorporar las rutas de los certificados.<\/p>\r\n<pre>\r\nconst host = process.env.SERVER_HOST || 'ipXXX.your.domain';\r\n\r\n...\r\n\r\nconst certOptions = {\r\n    cert: fs.readFileSync(&quot;\/etc\/letsencrypt\/live\/PATH_CHAIN\/fullchain.pem&quot;),\r\n    key: fs.readFileSync(&quot;\/etc\/letsencrypt\/live\/PATH_PRIVKEY\/privkey.pem&quot;)\r\n};\r\nOR\r\nconst certOptions = {\r\n    cert: fs.readFileSync(&quot;\/etc\/ssl\/localcerts\/ipXXX.your.domain.pem&quot;),\r\n    key: fs.readFileSync(&quot;\/etc\/ssl\/localcerts\/ipXXX.your.domain-key.pem&quot;)\r\n};<\/pre>\r\n<p>Ejecuta previniendo la instalaci&oacute;n de las librer&iacute;as necesarias:<\/p>\r\n<pre>\r\nnpm install express ws cors aedes node-opcua mysql2 serialport mqtt\r\n\r\nnpm install\r\n\r\nnode middleware.js<\/pre>\r\n<p>La salida esperada es:<\/p>\r\n<pre>\r\n# node middleware.js\r\n[INFO] [MQTT_EMBEDDED] Embedded broker listening on port 1883\r\n[INFO] Server listening on https:\/\/your.domain:3000\r\n\r\n<\/pre>\r\n<p>Una vez en ejecuci&oacute;n, puedes configurarlo como un servicio (<code>systemd<\/code>, <code>pm2<\/code>, etc.).<\/p>\r\n<hr>\r\n<p><a href=\"https:\/\/gpss-plus.com\/BRIDGER\/OPC-UA-virtual_v_1.0.zip\">OPC-UA virtual<\/a> : Versi&oacute;n 1.0<\/p>\r\n<p>Este servidor OPC-UA virtual simula un conjunto de sensores y actuadores, ideal para desarrollo y pruebas sin hardware f&iacute;sico o el acceso a los recursos locales.<\/p>\r\n<p>Ejecuta previniendo la instalaci&oacute;n de las librer&iacute;as necesarias:<\/p>\r\n<pre>\r\nnpm install\r\nnpm modbus-serial\r\n\r\n\r\nnode index.js<\/pre>\r\n<p>La salida esperada es:<\/p>\r\n<pre>\r\n# node index.js\r\n[-] RO: 1:Sensor_Motion_Detected\r\n[-] RO: 1:Sensor_Door_IsOpen\r\n[-] RO: 1:PIDControl_CurrentTemperature\r\n[+] RW: 1:PIDControl_Setpoint\r\n[-] RO: 1:PIDControl_PIDOutput\r\n[+] RW: 1:Device_Motor_Enabled\r\n[-] RO: 1:System_Battery_BatteryLevel\r\n[-] RO: 1:Device_Scale_Weight\r\n[+] RW: 1:Device_Scale_Trigger\r\n[-] RO: 1:Device_Piston_IsExtended\r\n[+] RW: 1:Device_Piston_Trigger\r\nOPC-UA running:  opc.tcp:\/\/your.domain:4840\r\n<\/pre>\r\n<hr>\r\n<p><a href=\"https:\/\/gpss-plus.com\/BRIDGER\/firmware_ESP32.c\">Firmware para ESP32<\/a> :&nbsp;Versi&oacute;n 1.0<\/p>\r\n<p data-start=\"4396\" data-end=\"4508\">Firmware b&aacute;sico para microcontroladores <strong data-start=\"4436\" data-end=\"4445\">ESP32<\/strong>, dise&ntilde;ado para enviar temperatura y humedad mediante <strong data-start=\"4499\" data-end=\"4507\">MQTT<\/strong>.<\/p>\r\n<h4 data-start=\"4510\" data-end=\"4535\"><strong data-start=\"4515\" data-end=\"4535\">Caracter&iacute;sticas:<\/strong><\/h4>\r\n<ul>\r\n    <li>Lectura de sensores DHT11 \/ DHT22.<\/li>\r\n    <li>Publicaci&oacute;n peri&oacute;dica en topics MQTT.<\/li>\r\n    <li>Compatible con el middleware.js -&nbsp;<em data-start=\"4668\" data-end=\"4676\">broker<\/em>.<\/li>\r\n<\/ul>\r\n<h4 data-start=\"4679\" data-end=\"4700\"><strong data-start=\"4684\" data-end=\"4700\">Instalaci&oacute;n:<\/strong><\/h4>\r\n<ul>\r\n    <li>Usa <strong data-start=\"4707\" data-end=\"4722\">Arduino IDE<\/strong> o <strong data-start=\"4725\" data-end=\"4739\">PlatformIO<\/strong>.<\/li>\r\n    <li>Conecta el ESP32 por USB.<\/li>\r\n    <li>Carga el firmware incluido en el paquete de descargas.<\/li>\r\n    <li>Instala el driver serial si fuera necesario.<\/li>\r\n    <li>En la cabecera del c&oacute;digo existen m&aacute;s indicaciones.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<hr>\r\n<p>&nbsp;<\/p>\r\n<p><a href=\"https:\/\/gpss-plus.com\/BRIDGER\/firmware_ESP8266.c\">Firmware para ESP8266<\/a> :&nbsp;Versi&oacute;n 1.0<\/p>\r\n<p data-start=\"4928\" data-end=\"4975\">Firmware alternativo m&aacute;s sencillo, que permite:<\/p>\r\n<ul>\r\n    <li>Encender y apagar un LED por MQTT.<\/li>\r\n    <li>Leer temperatura y humedad de un sensor DHT.<\/li>\r\n<\/ul>\r\n<p data-start=\"5063\" data-end=\"5188\">El proceso de instalaci&oacute;n es id&eacute;ntico al del ESP32.<br data-start=\"5114\" data-end=\"5117\">\r\nSolo cambia la configuraci&oacute;n del tipo de placa y puerto en Arduino IDE.<\/p>\r\n<p>&nbsp;<\/p>",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "378",
                "nombre": "BRIDGE_CALL: Ejecución de Métodos Remotos",
                "texto": "SYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\ninitial txt_error,\" \"\r\n\r\nGraphic {Type:TEXT,Name:Text1,X:295,Y:123}\r\nGraphic {Type:TEXT,Name:Text2,X:295,Y:95}\r\nGraphic {Type:TEXT,Name:Text3,X:295,Y:66}\r\nGraphic {Type:TEXT,Name:Text4,X:295,Y:35}\r\nGraphic {Type:TEXT,Name:TextError,X:192,Y:478,Text:\"On error....\"}\r\n\r\nRestRoom {name:esperaCall,x:543,y:488}\r\n\r\nInitial options,{user:\"user\", pass:\"1234\"}\r\n\r\nBRIDGER {NAME:bridge1,X:542,Y:434\r\n\t,SERVER:\"wss:\/\/tu-dominio.com:3000\"\r\n\t,CLIENT:\"opc.tcp:\/\/tu-dominio.com:4840\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\nPOSITION {NAME:POS1,X:183,Y:300}\r\nPOSITION {NAME:POS2,X:422,Y:300}\r\nPOSITION {NAME:POS3,X:704,Y:300}\r\n\r\nSTART 10\r\n\r\nPROCEDURE PRE_RUN\r\n    ; Suscripciones para monitorizar errores de red o sistema\r\n\tBRIDGE_SUBSCRIPTION { NAME: bridge1, VARIABLE: \"HTTP_Request_Error\", TRIGGER: bridge1_on_error }\r\n\tBRIDGE_SUBSCRIPTION { NAME: bridge1, VARIABLE: \"Topics_CurrentTime\", TRIGGER: on_clock }\r\n\r\n    ; Lectura de un archivo en el sistema de archivos remoto (FS)\r\n \tASSIGN params, [{}] \r\n\tBRIDGE_CALL {name:bridge1, trigger:on_read_file, params:V$params, method:\"Methods_FS_Read\"}\r\n\r\n    ; Petición a una API externa vía HTTP\r\n    ASSIGN params, [{ \"method\": \"GET\", \"params\": { \"name\": \"Antonio\" } }]\r\n    BRIDGE_CALL { name:bridge1, trigger:on_http, params:V$params, method:\"Methods_HTTP_Request\" }\r\n\r\n\tTERMINATE_VE\r\nENDPROCEDURE 1\r\n\r\nGENERATE 3,0,0,10 {NAME:GEN1,X:59,Y:300}\r\n\tADVANCE 3,0 {TO:POS1}\r\n    ; Llamada a un método de cálculo (Suma) en el servidor\r\n\tASSIGN params, [{ \"Num1\": (D$N), \"Num2\": (0) }]\r\n\tBRIDGE_CALL {name:bridge1, trigger:on_call, params:V$params, method:\"Methods_Do_Sum\"}\r\n    \r\n    ; Sincronización asíncrona: la entidad espera la respuesta\r\n    rest esperaCall\r\n    mod {subtitle:\"Suma: P$(suma)\"}\r\n\t\r\n    ADVANCE 4,4 {TO:POS2}\r\n\tASSIGN params, [{ \"Num1\": (D$N), \"Num2\": (D$N) }]\r\n\tBRIDGE_CALL {name:bridge1, trigger:on_call, params:V$params, method:\"Methods_Do_Sum\"}\r\n    rest esperaCall\r\n    mod {subtitle:\"Suma2: P$(suma)\"}\r\n    ADVANCE 4,4 {TO:POS3}\r\nENDGENERATE 1\r\n\r\n; --- Procedimientos de Respuesta ---\r\nprocedure on_clock\r\n    move {name:Text2,TEXT:\"Hora Servidor: P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_read_file\r\n    move {name:Text3,TEXT:\"Contenido Archivo: P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_http\r\n    move {name:Text4,TEXT:\"Respuesta HTTP: P$(PARAM_A.VALUE.message)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_call\r\n\tmove {name:Text1,TEXT:\"Suma remota: P$(PARAM_A.sum) Entidad: P$(PARAM_B)\"}\r\n\tassign suma,P$(PARAM_A.sum),P$(PARAM_B)\r\n    ; Despertamos a la entidad específica que hizo la llamada\r\n    wake esperaCall,-1,P$(PARAM_B)\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure bridge1_on_error\r\n\tsavevalue txt_error,\"X$txt_error\\nP$(PARAM_A.VALUE)\"\r\n    move {name:TextError,TEXT:\"X$txt_error\"}\r\n\tTERMINATE_VE\r\nendprocedure",
                "descripcion": "<p>Mientras que <code>BRIDGE_READ<\/code> y <code>BRIDGE_WRITE<\/code> se limitan a manejar variables de datos simples, <code>BRIDGE_CALL<\/code> permite a GPSS-Plus ejecutar <strong>m&eacute;todos o funciones remotas<\/strong> definidas en el middleware o en dispositivos industriales.<\/p>\r\n<p>Esta capacidad es fundamental para tareas que exceden el &aacute;mbito de la simulaci&oacute;n pura, tales como:<\/p>\r\n<ul>\r\n    <li>Acceder a bases de datos externas o al sistema de archivos del servidor (File System).<\/li>\r\n    <li>Realizar peticiones HTTP a APIs de terceros (consultar el clima, precios, stocks reales).<\/li>\r\n    <li>Ejecutar algoritmos complejos u operaciones matem&aacute;ticas pesadas fuera del motor de simulaci&oacute;n.<\/li>\r\n<\/ul>\r\n<h3><strong>Sincronizaci&oacute;n As&iacute;ncrona (Patr&oacute;n Rest\/Wake)<\/strong><\/h3>\r\n<p>Dado que estas llamadas ocurren a trav&eacute;s de la red, la respuesta no es instant&aacute;nea. Para evitar que la entidad contin&uacute;e su camino antes de recibir el resultado, se utiliza el patr&oacute;n <strong>Rest\/Wake<\/strong>:<\/p>\r\n<ol>\r\n    <li>La entidad lanza el <code>BRIDGE_CALL<\/code>.<\/li>\r\n    <li>Inmediatamente despu&eacute;s, entra en un estado de espera con <code>rest esperaCall<\/code>.<\/li>\r\n    <li>Cuando el procedimiento de <code>trigger<\/code> recibe la respuesta del servidor, identifica a la entidad mediante <code>P$(PARAM_B)<\/code> y la libera con un <code>wake<\/code>.<\/li>\r\n<\/ol>\r\n<p>Este mecanismo permite modelar con total realismo las <strong>latencias de red<\/strong> y el procesamiento as&iacute;ncrono en sistemas industriales.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "407",
                "nombre": "Un gemelo digital",
                "texto": "",
                "descripcion": "<p>Cuando trabajamos con <code data-start=\"267\" data-end=\"276\">BRIDGER<\/code> no siempre vamos a tener un dispositivo real al otro lado. Si queremos construir un gemelo digital, necesitamos una fuente de datos que represente lo que est&aacute; ocurriendo en el mundo real, y muchas veces ese sistema a&uacute;n no existe o no est&aacute; disponible.<\/p>\r\n<p>En esos casos, podemos crear nosotros esa &ldquo;realidad&rdquo; mediante un modelo que se comporte como si fuese el sistema f&iacute;sico y que emita su propia telemetr&iacute;a. A este mundo simulado que act&uacute;a como origen de datos lo llamamos <strong data-start=\"747\" data-end=\"755\">ALFA<\/strong>.<\/p>\r\n<p data-start=\"204\" data-end=\"300\">En este ejemplo vamos a conectar un mundo <strong data-start=\"246\" data-end=\"254\">ALFA<\/strong> (la realidad simulada) con su gemelo digital.<\/p>\r\n<p data-start=\"302\" data-end=\"543\">El <b>ALFA <\/b>est&aacute; formado por entidades (veh&iacute;culos) que se desplazan entre dos posiciones, alternando de un lado a otro con un tiempo aleatorio en cada recorrido. Cada vez que cambian de estado, emiten una telemetr&iacute;a a trav&eacute;s de <code data-start=\"526\" data-end=\"532\">MQTT<\/code> indicando:<\/p>\r\n<ul data-start=\"545\" data-end=\"697\">\r\n    <li data-section-id=\"16gynlt\" data-start=\"545\" data-end=\"565\">su identificador<\/li>\r\n    <li data-section-id=\"340jye\" data-start=\"566\" data-end=\"597\">su posici&oacute;n l&oacute;gica (estado)<\/li>\r\n    <li data-section-id=\"1nfu2x0\" data-start=\"598\" data-end=\"646\">el instante en el que comienza el movimiento<\/li>\r\n    <li data-section-id=\"14tp6q3\" data-start=\"647\" data-end=\"697\">y el tiempo estimado hasta el siguiente estado<\/li>\r\n<\/ul>\r\n<p data-start=\"699\" data-end=\"769\">Con esta informaci&oacute;n es suficiente para reconstruir su comportamiento.<\/p>\r\n<p data-start=\"771\" data-end=\"839\"><b>El gemelo digital<\/b> se suscribe a esa telemetr&iacute;a y, para cada entidad:<\/p>\r\n<ul data-start=\"841\" data-end=\"908\">\r\n    <li data-section-id=\"1jipvrl\" data-start=\"841\" data-end=\"866\">si no existe, la crea<\/li>\r\n    <li data-section-id=\"1ptypbx\" data-start=\"867\" data-end=\"908\">si ya existe, recalcula su movimiento<\/li>\r\n<\/ul>\r\n<p data-start=\"910\" data-end=\"1185\">Cuando recibe nuevos datos, interrumpe el <code data-start=\"952\" data-end=\"961\">ADVANCE<\/code> en curso&nbsp;(<code>UPDATE <\/code>a tiempo actual), y prepara los datos para que el nuevo <code data-start=\"1027\" data-end=\"1036\">ADVANCE<\/code>&nbsp;use los datos recibidos. Como la telemetr&iacute;a incluye el instante original, es posible compensar la latencia del mensaje y ajustar el tiempo restante.<\/p>\r\n<p data-start=\"910\" data-end=\"1185\">De este modo,&nbsp;el gemelo no solo replica posiciones sino el comportamiento en el tiempo.<\/p>\r\n<p data-start=\"1187\" data-end=\"1450\">Adem&aacute;s, se incluye un caso de rehidrataci&oacute;n: durante un intervalo se detiene la suscripci&oacute;n, por lo que el gemelo deja de recibir datos y las entidades contin&uacute;an seg&uacute;n su &uacute;ltimo estado conocido. Cuando la conexi&oacute;n se restablece, el sistema vuelve a sincronizarse.<\/p>\r\n<p data-start=\"1452\" data-end=\"1567\">Para ejecutar este ejemplo se utilizan dos motores en paralelo: uno que act&uacute;a como ALFA y otro como gemelo digital.<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\n; ALFA\r\n\r\nSYSTEM {TyPE:PRE_RUN,TRIggER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1,time_decimals:1}\r\n\r\n\r\nInitial options,{ }\r\n\r\nBRIDGER {NAME:bridge1,X:746,Y:571\r\n;\t,SERVER:&quot;wss:\/\/bridger.gpss-plus.com:3000&quot;\r\n\t,SERVER:&quot;wss:\/\/ip10.nuevoiris.com:3000&quot;\r\n\t,CLIENT:&quot;mqtt:\/\/localhost&quot;\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\n\r\nPOSITION {NAME:POS_0,X:238,Y:302}\r\nPOSITION {NAME:POS_1,X:717,Y:292}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:422,Y:533,Text:&quot;On OPEN&quot;}\r\nGraphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:&quot;On Error&quot;}\r\n\r\nSTART 1\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n\tTERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,3 {NAME:GEN1,X:86,Y:306}\r\nassign State,0\r\nassign id,&quot;Id_D$N&quot;\r\nWHILE (1==1)\r\n\r\n    assign State,(P$State+1&gt;1?0:1)\r\n    assign t_delta,round((random * 8) + 2,1)\r\n    assign sys, SYS$\r\n    ASSIGN now,P$(sys.date.time)\r\n\r\n    ASSIGN values, {&quot;positions&quot;: {\r\n    \tid:&quot;P$id&quot;\r\n        ,State:&quot;P$State&quot;\r\n        ,t_start:P$now\r\n        ,t_delta:P$t_delta\r\n        ,txt:&quot;positions&quot;\r\n        }\r\n        }\r\n\tBRIDGE_WRITE { name:bridge1, VALUES:V$values,TRIGGER:bridge1_on_write }\r\n\r\n    ADVANCE P$t_delta {TO:&quot;POS_P$State&quot;}\r\n\r\nENDWHILE\r\n\r\nADVANCE 1,0 {TO:POS2}\r\n   \r\nENDGENERATE 0\r\n;----------------------------------------------------------------------------\r\n\r\n\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:&quot;P$PARAM_A&quot;}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\nprocedure bridge1_on_write\r\n    move {name:TextError,TEXT:&quot;P$PARAM_A&quot;}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n<\/pre>\r\n<pre>\r\n;DIGITAL TWIN\r\n\r\nSYSTEM {TyPE:PRE_RUN,TRIggER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1,time_decimals:1}\r\n\r\n\r\nInitial options,{ }\r\n\r\nBRIDGER {NAME:bridge1,X:746,Y:571\r\n    ,SERVER:&quot;wss:\/\/bridger.gpss-plus.com:3000&quot;\r\n    ,CLIENT:&quot;mqtt:\/\/localhost&quot;\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS_0,X:238,Y:302}\r\nPOSITION {NAME:POS_1,X:717,Y:292}\r\n\r\nQUEUER {NAME:QTrucks,X:61,Y:506}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:419,Y:423,Text:&quot;Text1&quot;}\r\nGraphic {NAME:Text2,Type:TEXT,X:421,Y:455,Text:&quot;Text2&quot;}\r\nGraphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:&quot;OnError&quot;}\r\n\r\nSTART 1\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n    timeout startSubscripcion,1\r\n    timeout cancelSubscripcion,20\r\n    timeout startSubscripcion,50\r\n\tTERMINATE_VE\r\n\r\n    ASSIGN values, {&quot;State&quot;:\t{id:&quot;0&quot;,txt:&quot;null&quot;}}\r\n    BRIDGE_WRITE { name:bridge1, VALUES:V$values,TRIGGER:bridge1_on_write  }\r\n\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 0,0,0,0 {NAME:GEN1,X:86,Y:306}\r\nQUEUE QTrucks\r\nassign id,&quot;P$PARAM_A&quot;\r\nassign t_delta,P$PARAM_B\r\nassign State,P$PARAM_C\r\nmod {subtitle:&quot;[P$id]&quot;}\r\n\r\nmove {name:Text1,TEXT:&quot;AC1$: Create -&gt; P$(PARAM_A) P$t_delta&quot;}\r\n\r\nassign t_start,AC1$\r\n\r\nwhile (1==1)\r\n    assign To,P$State\r\n    assign From,(P$To&gt;=1?0:1)\r\n\r\n    move {name:Text1,TEXT:&quot;AC1$: Move -&gt; P$(PARAM_A) P$t_delta | P$From P$to&quot;}\r\n\tadvance (P$t_delta+0.1),0 {from:&quot;POS_P$From&quot;, to:&quot;POS_P$To&quot;}\r\nendwhile\r\n\r\n\r\n\r\nDEPART QTrucks\r\nENDGENERATE 0\r\n;----------------------------------------------------------------------------\r\n\r\nprocedure on_Subscription\r\n    assign datos,V$(PARAM_A.VALUE)\r\n    if (&quot;P$(datos.txt)&quot;==&quot;null&quot;)\r\n        TERMINATE_VE\r\n    endif\r\n    \r\n    assign id,&quot;P$(datos.id)&quot;\r\n    assign t_delta,P$(datos.t_delta)\r\n    assign State,P$(datos.State)\r\n    \r\n    move {name:Text1,TEXT:&quot;AC1$: Position -&gt; P$(PARAM_A.VALUE)&quot;}\r\n\r\n    assign idExist,0\r\n    FOREACH entityNumber,IN_RESOURCE,QTrucks\r\n        if (&quot;P$(id,P$entityNumber)&quot;==&quot;P$(id)&quot;)\r\n        assign idExist,P$entityNumber\r\n        endif\r\n    ENDFOREACH\r\n \r\n    if (P$idExist==0)\r\n        NEW GEN1,0,&quot;P$id&quot;,P$t_delta,P$State\r\n        TERMINATE_VE\r\n    endif\r\n    \r\n    assign sys, SYS$\r\n    ASSIGN now, P$(sys.date.time) ; Real time\r\n    assign delta_latencia, (P$now - P$(datos.t_start)) \/ 1000 ; From ms to seconds\r\n    \r\n    \r\n    assign State,P$(State),P$idExist\r\n    assign t_start,P$(datos.t_start),P$idExist\r\n    assign t_delta,P$(datos.t_delta) - P$delta_latencia,P$idExist\r\n    \r\n    update P$idExist,(AC1$) ; go out of advance\r\n\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n;--------------------------------------------------------------------------\r\nprocedure cancelSubscripcion\r\n    move {name:Text2,TEXT:&quot;Cancel subscripcion&quot;}\r\n    BRIDGE_UNSUBSCRIBE { \r\n        NAME: bridge1, \r\n        VARIABLE: &quot;positions&quot; \r\n    }\r\n\tTERMINATE_VE\r\nendprocedure\r\n;--------------------------------------------------------------------------\r\n\r\nprocedure startSubscripcion\r\n    move {name:Text2,TEXT:&quot;Start subscripcion&quot;}\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n        , VARIABLE: &quot;positions&quot;\r\n        , TRIGGER: on_Subscription \r\n        }\r\n\tTERMINATE_VE\r\nendprocedure\r\n;--------------------------------------------------------------------------\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:&quot;P$PARAM_A&quot;}\r\n\tTERMINATE_VE\r\nendprocedure\r\n<\/pre>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "339",
                "nombre": "IoT con MQTT",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nSYSTEM {TYPE:OPTIONS, REAL_TIME:1}\r\n\r\n\r\nInitial options,{  }\r\n\r\nBRIDGER {NAME:bridge1,X:733,Y:556\r\n\t,SERVER:\"wss:\/\/ip10.tidominio.com:3000\"\r\n\t,CLIENT:\"mqtt:\/\/localhost\"\r\n    ,OPTIONS:V$options\r\n    ,on_error:bridge1_on_error\r\n    }\r\n\r\n\r\nPOSITION {NAME:POS1,X:101,Y:378}\r\n\r\nPOSITION {NAME:POS2,X:143,Y:115}\r\n\r\n\r\nGraphic {NAME:txtSubscribeL,Type:TEXT,X:394,Y:465,Text:\"On SUBS\"}\r\nGraphic {NAME:txtSubscribeH,Type:TEXT,X:394,Y:435,Text:\"On SUBS\"}\r\nGraphic {NAME:txtSubscribeT,Type:TEXT,X:394,Y:405,Text:\"On SUBS\"}\r\nGraphic {NAME:txtSubscribeS,Type:TEXT,X:394,Y:375,Text:\"On SUBS\"}\r\n\r\nGraphic {NAME:TextBrowse,Type:TEXT,X:116,Y:283,Text:\"On Browse\"}\r\nGraphic {NAME:TextWrite,Type:TEXT,X:391,Y:59,Text:\"On Write\"}\r\nGraphic {NAME:TextError,Type:TEXT,X:424,Y:582,Text:\"Error\"}\r\n\r\n\r\nSTART 10\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"devices\/elprimero\/sensor\/humidity\"\r\n    , TRIGGER: on_humidity \r\n    }\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"devices\/elprimero\/sensor\/lux\"\r\n    , TRIGGER: on_luz \r\n    }\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"devices\/elprimero\/sensor\/temperature\"\r\n    , TRIGGER: on_temperature \r\n    }\r\n    BRIDGE_SUBSCRIPTION { NAME: bridge1\r\n  \t, VARIABLE: \"devices\/elprimero\/status\"\r\n    , TRIGGER: on_status \r\n    }\r\n\r\n; Sin salida\r\nBRIDGE_BROWSE {name:bridge1, trigger:on_BROWSE }\r\n\r\n\r\n \r\n\tTERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;*****************************************************\r\nGENERATE 5,0 {NAME:GEN1,X:91,Y:547}\r\n\r\nADVANCE 5,0 {TO:POS1,flow:1}\r\n\r\n\r\nif (D$N% 2== 1)\r\nASSIGN values, {\"devices\/elprimero\/commands\/led\": \"on\"}\r\nBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\nelse\r\nASSIGN values, {\"devices\/elprimero\/commands\/led\": \"off\"}\r\nBRIDGE_WRITE { name:bridge1, VALUES:V$values ,TRIGGER:on_write}\r\nendif\r\n\r\nADVANCE 5,0 {TO:POS2,flow:1}\r\n\r\nENDGENERATE 1\r\n;----------------------------------------------------------------------------\r\nprocedure on_read\r\n    move {name:TextRead,TEXT:\"WIFI: P$(PARAM_A)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure bridge1_on_error\r\n    move {name:TextError,TEXT:\"P$PARAM_A\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n;-------------------------\r\nprocedure on_write\r\nmove {name:TextWrite,TEXT:\"WRITE T:AC1$: P$(PARAM_A.devices\/elprimero\/commands\/led)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n\r\nprocedure on_luz\r\n\tmove {name:txtSubscribeL,text:\"Subs LUX P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_humidity\r\n\tmove {name:txtSubscribeH,text:\"Subs HUM P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_temperature\r\n\tmove {name:txtSubscribeT,text:\"Subs TEM P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n\r\nprocedure on_status\r\n\tmove {name:txtSubscribeS,text:\"Subs STA P$(PARAM_A.VALUE)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n\r\n\r\n\r\n\r\nprocedure on_BROWSE\r\n   move {name:TextBrowse,text:\"on_BROWSE P$(PARAM_A)\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n",
                "descripcion": "<p>El protocolo MQTT de comunicaciones es un est&aacute;ndar de los dispositivos para IoT.<\/p>\r\n<p>Funciona por &quot;topics&quot;, como las redes sociales. Un dispositivo como un term&oacute;metro env&iacute;a la temperatura obtenida sin saber qui&eacute;n va a requerirla y leerla.<\/p>\r\n<p>Por lo tanto se requerir&aacute; que un &quot;broker MQTT&quot; est&eacute; constantemente escuchando lo que los sensores env&iacute;en y estar dispuesto a transmit&iacute;rselo a quien los necesite.<\/p>\r\n<p>Un broker est&aacute;ndar es Mosquitto pero el <b>middleware.js<\/b> incorpora una labor muy parecida.<\/p>\r\n<p>As&iacute; que volvemos a tener la misma arquitectura de sistema:<\/p>\r\n<p>GPSS-Plus &lt;-&gt; (middleware.js + broker MQTT) &lt;-&gt; dispositivo<\/p>\r\n<p><b>En este punto pasamos a programar y utilizar hardware extra.<\/b><\/p>\r\n<p>No nos preocupemos demasiado, s&iacute; ser&iacute;a aconsejable tener ciertas nociones de programaci&oacute;n en C pero en general es suficiente con pedir a nuestra IA favorita que nos construya aquello que queramos.<\/p>\r\n<p>Vamos a programar un microcontrolador ESP8266 con WIFI que gestione:<\/p>\r\n<ul>\r\n    <li>Un sensor de luz BH1750<\/li>\r\n    <li>Un sensor de humedad y temperatura DHT22<\/li>\r\n    <li>El encendido y apagado de un led protegido por una resistencia.<\/li>\r\n<\/ul>\r\n<p>Para ello, usaremos estos 5 elementos hardware y un software para guardar el firmware del&nbsp;ESP8266.<\/p>\r\n<p>Uno sencillo es &quot;Arduino IDE&quot; que contiene todas las librer&iacute;as necesarias.<\/p>\r\n<p>Un apunte r&aacute;pido del camino a seguir es:<\/p>\r\n<ul>\r\n    <li>1.- Obtener el hardware<\/li>\r\n    <li>2.- Descargar Arduino IDE<\/li>\r\n    <li>3.- Instalar el driver necesario para conectar nuestro equipo a trav&eacute;s USB simulando un puerto serial.<\/li>\r\n    <li>4.- Compilar el c&oacute;digo C del firmware&nbsp;Arduino IDE&nbsp;del cap&iacute;tulo de descargas. En el c&oacute;digo fuente tienes m&aacute;s informaci&oacute;n.<\/li>\r\n    <li>5.- Pasarlo al dispositivo a trav&eacute;s del mismo&nbsp;Arduino IDE.<\/li>\r\n    <li>6.- Enviar por el puerto serial en la misma consola de Arduino IDE los datos de nuestra WIFI y la IP del broker<\/li>\r\n    <li>7.- Ejecutar GPSS-Plus y ver los datos de temperatura, humedad y ver encender y apagar el led.<\/li>\r\n<\/ul>\r\n<p>Tras esto, hecho uno, hechos todos. Todos los dispositivos en general funcionan a trav&eacute;s de microcontroladores ESP32 o&nbsp;ESP8266 y otros y se conectan de la misma manera.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>En el ejemplo, podemos ver como realiza las 3 opciones de MQTT est&aacute;ndar:<\/p>\r\n<ul>\r\n    <li>Subscribirse a un sensor (leer la humedad).<\/li>\r\n    <li>Escribir en un comando (encender un led)<\/li>\r\n    <li>Comprobar el status&nbsp;(Last Will and Testament - LWT)<\/li>\r\n<\/ul>\r\n<p>Por lo que deber&iacute;amos ver los 3 datos de temperatura, humedad y luminosidad adem&aacute;s del encendido y apagado del led.<\/p>\r\n<p>Debe entenderse que la comprobaci&oacute;n de WRITE (Respuesta &quot;OK&quot;) es algo que comunica el BRIDGER en la capa de red y no el dispositivo al que solo puede uno subscribirse.<\/p>\r\n<p>El browse no tiene contenido puesto que el broker no dispone de esa informaci&oacute;n.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "329",
                "nombre": "OPC-UA Virtual Device",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nOPC {NAME:opc1,X:100,Y:121,visible:1, TYPE:REAL,\r\n  CLIENT:\"opc.tcp:\/\/192.168.1.10:4840\"}\r\n\r\n\r\n\r\nPOSITION {NAME:POS1,X:251,Y:335}\r\nPOSITION {NAME:POS2,X:469,Y:341}\r\n\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:425,Y:314,Text:\"Entidad\"}\r\nGraphic {NAME:Text2,Type:TEXT,X:424,Y:124,Text:\"qqqqqq\"}\r\nGraphic {NAME:Text3,Type:TEXT,X:425,Y:514,Text:\"write\"}\r\nGraphic {NAME:Text4,Type:TEXT,X:115,Y:398,Text:\"door\"}\r\n\r\ninitial nAgente,0\r\nSTART 500\r\n\r\n;*****************************************************\r\n\r\nPROCEDURE PRE_RUN\r\n;\ttimeout agente.main,0\r\n\r\n    OPC_SUBSCRIPTION { NAME: opc1\r\n    \t, VARIABLE: \"PIDControl_CurrentTemperature\"\r\n        , TRIGGER: on_suscription \r\n        }\r\n    OPC_SUBSCRIPTION { NAME: opc1\r\n    \t, VARIABLE: \"Sensor_Door_IsOpen\"\r\n        , TRIGGER: puerta_abierta \r\n        }\r\n\tTERMINATE_VE\r\nENDPROCEDURE 1\r\n\r\n;*****************************************************\r\nGENERATE 0,0,0,0 {NAME:GEN1,X:43,Y:300}\r\n\r\n;OPC_BROWSE {      name:opc1,      savevalue:variables    }\r\nif (D$N==1)\r\n    ASSIGN values, { PIDCONTROL_SETPOINT: 10 }\r\n;    OPC_WRITE { NAME: opc1, VALUES: V$values }\r\n    OPC_WRITE { NAME: opc1, VALUES: V$values ,async:1,trigger:on_write}\r\n   \r\nendif\r\n\r\n;move {name:Text1,TEXT:\"Entidad Resultado X$(variables)\"}\r\nOPC_READ {name:opc1, savevalue:temperatura  }\r\n;OPC_READ {name:opc1, async:1,trigger:on_results }\r\n\r\nmove {name:Text1,TEXT:\"Resultado READ: X$(temperatura.Sensor_Proximity_DistanceInCm)\"}\r\n\t\r\nADVANCE 20,0 {TO:pos1}\r\n   \r\nENDGENERATE 1\r\n\r\nprocedure on_suscription\r\n\t\r\n    move {name:Text2,TEXT:\"on_suscription AC1$:\\n P$PARAM_A \\N P$PARAM_B P$PARAM_C\"}\r\n    \r\n    TERMINATE_VE\r\nendprocedure\r\n\r\n\r\nprocedure on_results\r\n\t\r\n    move {name:Text2,TEXT:\"on_results AC1$:\\n P$PARAM_A \\N P$PARAM_B P$PARAM_C\"}\r\n    \r\n    TERMINATE_VE\r\nendprocedure\r\n\r\nprocedure on_write\r\n       move {name:Text3,TEXT:\"on_write AC1$:\\n P$PARAM_A \\N P$PARAM_B P$PARAM_C\"}\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\nprocedure puerta_abierta\r\n       move {name:Text4,TEXT:\"DOOR AC1$ ; P$PARAM_A\"}\r\n       if (P$(PARAM_A.value)==1) {debug:1}\r\n    \tNEW GEN1\r\n    \tendif\r\n\tTERMINATE_VE\r\nendprocedure\r\n\r\n\r\n",
                "descripcion": "<h3>OPC-UA Virtual Device Manual<\/h3>\r\n<p>This document describes the virtual devices and sensors simulated by the <strong>Virtual OPC-UA Plant<\/strong> server.<\/p>\r\n<h2>1. PIDControl<\/h2>\r\n<p>Simulates a PID temperature control loop.<\/p>\r\n<ul>\r\n    <li><code>PIDControl_CurrentTemperature<\/code>: Current temperature (read-only)<\/li>\r\n    <li><code>PIDControl_Setpoint<\/code>: Target temperature (read-write)<\/li>\r\n    <li><code>PIDControl_PIDOutput<\/code>: PID output signal (read-only)<\/li>\r\n<\/ul>\r\n<h2>2. Sensor_Motion<\/h2>\r\n<p>Simulates a motion detector.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Motion_Detected<\/code>: Boolean value indicating motion detected (read-only)<\/li>\r\n<\/ul>\r\n<h2>3. Sensor_Door<\/h2>\r\n<p>Simulates a door open\/close sensor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Door_IsOpen<\/code>: Boolean value indicating if the door is open (read-write)<\/li>\r\n<\/ul>\r\n<h2>4. Sensor_Humidity<\/h2>\r\n<p>Simulates an environmental humidity sensor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Humidity_Value<\/code>: Humidity level in % (read-only)<\/li>\r\n<\/ul>\r\n<h2>5. Sensor_Proximity<\/h2>\r\n<p>Simulates a proximity sensor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Proximity_Distance<\/code>: Distance to object in cm (read-only)<\/li>\r\n<\/ul>\r\n<h2>6. Sensor_Light<\/h2>\r\n<p>Simulates a light level sensor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Light_Lux<\/code>: Light level in lux (read-only)<\/li>\r\n<\/ul>\r\n<h2>7. Sensor_Gas<\/h2>\r\n<p>Simulates a gas leakage detector.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Gas_Concentration<\/code>: Gas concentration in ppm (read-only)<\/li>\r\n<\/ul>\r\n<h2>8. Sensor_Vibration<\/h2>\r\n<p>Simulates a vibration sensor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Vibration_Level<\/code>: Vibration intensity (read-only)<\/li>\r\n<\/ul>\r\n<h2>9. Sensor_Smoke<\/h2>\r\n<p>Simulates a smoke detector.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Smoke_Detected<\/code>: Boolean value indicating presence of smoke (read-only)<\/li>\r\n<\/ul>\r\n<h2>10. Sensor_Battery<\/h2>\r\n<p>Simulates a battery status monitor.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Battery_Level<\/code>: Battery level in % (read-only)<\/li>\r\n<\/ul>\r\n<h2>11. Sensor_Network<\/h2>\r\n<p>Simulates network signal strength.<\/p>\r\n<ul>\r\n    <li><code>Sensor_Network_Strength<\/code>: Signal strength from 0 to 100 (read-only)<\/li>\r\n<\/ul>\r\n<h2>12. System_PowerStatus<\/h2>\r\n<p>Indicates if the system is powered.<\/p>\r\n<ul>\r\n    <li><code>System_PowerStatus_IsPowered<\/code>: Boolean value indicating power status (read-only)<\/li>\r\n<\/ul>\r\n<hr>\r\n<p><strong>Note:<\/strong> Writable variables are intended for use with commands such as <code>OPC_WRITE<\/code> or <code>OPC_SUBSCRIPTION<\/code>.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "399",
        "nombre": "Season 13: recursos externos. Inyección de código Javascript",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "400",
                "nombre": "La necesidad y límites",
                "texto": "<h4 data-path-to-node=\"5\">El equilibrio entre especificidad y potencia<\/h4>\r\n<p data-path-to-node=\"6\">Los lenguajes generalistas GPL no compiten con los lenguajes espec&iacute;ficos de dominio (DSL); sus naturalezas son distintas. No es la intenci&oacute;n de <b data-path-to-node=\"6\" data-index-in-node=\"140\">GPSS-Plus<\/b> sustituir la programaci&oacute;n orientada a objetos que constituye la base de la casi totalidad del software actual.<\/p>\r\n<p data-path-to-node=\"7\">Este DSL se presenta siendo muy consciente de sus l&iacute;mites frente a lenguajes generalistas como C o Python, y su vasta base de librer&iacute;as. Sin embargo, es necesario delimitar las fronteras entre el modelado de dominio, el lenguaje generalista y el puramente matem&aacute;tico. En este sentido, todo modelo debe ser auditable y testado bajo un proceso riguroso, como el marco de <b data-path-to-node=\"7\" data-index-in-node=\"369\">V&amp;V H-M-E<\/b> que defendemos.<\/p>\r\n<h4 data-path-to-node=\"8\">El dilema del modelador<\/h4>\r\n<p data-path-to-node=\"9\">Crear un sinf&iacute;n de <i data-path-to-node=\"9\" data-index-in-node=\"19\">scripts<\/i> aislados, sin m&aacute;s conexi&oacute;n que la que el modelador guarda en su cabeza, no es razonable para un proceso de validaci&oacute;n serio. Pero, al mismo tiempo, limitar el objetivo &uacute;ltimo del modelo por una carencia heredada del DSL es un error a&uacute;n mayor.<\/p>\r\n<p data-path-to-node=\"10\"><b data-path-to-node=\"10\" data-index-in-node=\"0\">&iquest;Cu&aacute;l es entonces la soluci&oacute;n?<\/b><\/p>\r\n<h4 data-path-to-node=\"11\">El Modelo H&iacute;brido<\/h4>\r\n<p data-path-to-node=\"12\">GPSS-Plus deja abierta la frontera del DSL est&aacute;ndar permitiendo la importaci&oacute;n de clases y medios externos en el el GPL Javascript. Esta capacidad transforma el modelo en un <b data-path-to-node=\"12\" data-index-in-node=\"159\">ecosistema h&iacute;brido<\/b>:<\/p>\r\n<ol start=\"1\" data-path-to-node=\"13\">\r\n    <li>\r\n    <p data-path-to-node=\"13,0,0\"><b data-path-to-node=\"13,0,0\" data-index-in-node=\"0\">El Modelo Nativo (Caja de Cristal):<\/b> La l&oacute;gica de bloques, flujos y gesti&oacute;n de recursos. Es totalmente transparente, auditable y verificable mediante el m&eacute;todo <b data-path-to-node=\"13,0,0\" data-index-in-node=\"159\">V&amp;V H-M-E<\/b>.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"13,1,0\"><b data-path-to-node=\"13,1,0\" data-index-in-node=\"0\">Los Recursos Inyectados (Caja Negra):<\/b> Mediante <code data-path-to-node=\"13,1,0\" data-index-in-node=\"47\">LOAD_CLASS<\/code> y <code data-path-to-node=\"13,1,0\" data-index-in-node=\"60\">LOAD_MEDIA<\/code>, el modelador integra librer&iacute;as de c&aacute;lculo, sistemas de sonido o interfaces gr&aacute;ficas. El motor trata a estos componentes como &quot;entidades externas&quot; cuya l&oacute;gica es opaca a la auditor&iacute;a sem&aacute;ntica. <u>Su validaci&oacute;n debe ser previa a su integraci&oacute;n en el modelo.<\/u><\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-start=\"1725\" data-end=\"2122\">Estos nuevos recursos l&oacute;gicos (no los recursos f&iacute;sicos que afectan al flujo de las entidades) inyectados deben ser tratados con la misma jerarqu&iacute;a operativa que un recurso nativo. Podr&iacute;amos inyectar una FSM (M&aacute;quina de Estados Finitos) sustitutiva de la nativa, un solver espec&iacute;fico de un dominio de conocimiento &mdash;como pueda ser el qu&iacute;mico o sobre bot&aacute;nica&mdash;, o una librer&iacute;a de c&aacute;lculo matricial.<\/p>\r\n<p data-start=\"2129\" data-end=\"2388\">Estos recursos no participan en la mec&aacute;nica interna del motor, sino que act&uacute;an como componentes externos que el modelo puede utilizar. El motor los trata como entidades l&oacute;gicas cuyo comportamiento interno es opaco, respetando as&iacute; la separaci&oacute;n de cometidos.<\/p>\r\n<p data-start=\"2395\" data-end=\"2520\">No en vano se les ha privado de API de acceso al motor: por ejemplo, no pueden crear entidades virtuales (VE) en su interior.<\/p>\r\n<p data-path-to-node=\"15\">Bajo esta filosof&iacute;a, disponemos de dos v&iacute;as principales para inyectar c&oacute;digo Javascript basado en clases:<\/p>\r\n<ul data-path-to-node=\"16\">\r\n    <li>\r\n    <p data-path-to-node=\"16,0,0\"><b data-path-to-node=\"16,0,0\" data-index-in-node=\"0\">Recursos L&oacute;gicos:<\/b> Creaci&oacute;n de librer&iacute;as o clases inyectables para el c&aacute;lculo y la l&oacute;gica sobre el motor.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"16,1,0\"><b data-path-to-node=\"16,1,0\" data-index-in-node=\"0\">Recursos Multimedia:<\/b> Creaci&oacute;n de componentes inyectables para el visualizador y la interfaz de usuario.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>El modelo sigue siendo la fuente de verdad; el c&oacute;digo externo solo resuelve problemas que el modelo formula.<\/p>",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "401",
                "nombre": "Ejemplo básico de clase",
                "texto": "\/*\r\n\r\n  \t \tRecursos inyectados\r\n \r\n*\/\r\n\r\n\r\nPOSITION {NAME:POS1,X:620,Y:360}\r\nGRAPHIC {NAME:Text1,TYPE:TEXT,X:373,Y:268,TEXT:\"DSL Graphic\",font:\"24\"} ; Create text graphic\r\nFacility {NAME:fac1,X:373,Y:365,capacity:3}\r\n\r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/Sum.js\r\nINSTANCE_CLASS {name:sum, class:Sum}\r\n\r\nSTART 2000\r\n\r\n\r\n\r\n\r\n;*****************************************************\r\nGENERATE 10,3 {NAME:GEN1,X:111,Y:366}\r\nADVANCE 5 {TO:fac1}\r\n\r\n\r\nassign inputs,{a:10,b:20,acc:D$N}\r\nEXEC_CLASS {name:sum,in:V$inputs,savevalue:data}\r\n\r\nMove {name:Text1,text:\"Result X$(data.out) \\nACC: E$(sum,acc) \\nACCstr: E$(sum,accString)\"}\r\n\r\nseize fac1\r\n\tADVANCE 20,20\r\nrelease fac1\r\n\r\nADVANCE 20 {TO:POS1}\r\n\r\nENDGENERATE 1\r\n",
                "descripcion": "<p data-start=\"285\" data-end=\"368\">GPSS-Plus permite incorporar l&oacute;gica externa mediante clases escritas en Javascript.<\/p>\r\n<p data-start=\"370\" data-end=\"640\">Se utilizan para incorporar l&oacute;gica espec&iacute;fica que no forma parte del modelo: c&aacute;lculos, reglas de negocio o librer&iacute;as de un dominio concreto (qu&iacute;mica, log&iacute;stica, optimizaci&oacute;n, c&aacute;lculo, visualizaci&oacute;n, etc.). <\/p>\r\n<p data-start=\"370\" data-end=\"640\">El modelo define el comportamiento del sistema; la clase resuelve operaciones concretas.<\/p>\r\n<h3>Naturaleza de las clases<\/h3>\r\n<p data-start=\"642\" data-end=\"758\">Estas clases se integran en el modelo como recursos l&oacute;gicos y son ejecutadas por las entidades durante su recorrido.<\/p>\r\n<ul>\r\n    <li><b>No disponen de API.<\/b> <\/li>\r\n    <li>No crean VEs ni alteran directamente un SAVEVALUE, ya que ello romper&iacute;a la coherencia del modelo sin control.<\/li>\r\n    <li>No modifican el flujo ni el tiempo del modelo<\/li>\r\n    <li>Las clases son s&iacute;ncronas: se ejecutan en el mismo instante en que son invocadas.<\/li>\r\n<\/ul>\r\n<h3>Contrato de la clase<\/h3>\r\n<p data-start=\"978\" data-end=\"1119\">Este primer ejemplo muestra el caso m&aacute;s simple: una clase <code data-start=\"1036\" data-end=\"1041\">Sum<\/code> que recibe dos valores, acumula un dato, los procesa y devuelve un resultado.<\/p>\r\n<p data-start=\"1121\" data-end=\"1176\">Este es el archivo Javascript con la clase en cuesti&oacute;n del ejemplo:<\/p>\r\n<pre style=\"color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; overflow-wrap: break-word; white-space: pre-wrap;\">\r\nclass Sum {\r\n\r\n  constructor(name) {\r\n    this.name = name;\r\n\tthis.acc = 0;\r\n  }\r\n\r\n  accString() {\r\n    return &quot;'ACC: &quot;+this.acc+&quot;'&quot;;\r\n  }\r\n\r\n\t\r\n  exec(input) {\r\n    const output = {};\r\n\tif (input.acc) this.acc += input.acc;\r\n    output.out = input.a + input.b + this.acc;\r\n\tif (input.a&lt;0) output.error=&quot;exec: Negative number&quot;; \r\n    return output;\r\n  }\r\n}\r\n<\/pre>\r\n<p data-start=\"468\" data-end=\"592\">La clase sigue el contrato esperado por el motor:<\/p>\r\n<ul>\r\n    <li>M&eacute;todo constructor<\/li>\r\n    <li>M&eacute;todo exec(input)&nbsp;que recibe un objeto y devuelve un objeto.<\/li>\r\n    <li>Un m&eacute;todo de obtenci&oacute;n de datos interno para usar a trav&eacute;s del SNA E$(instancia,m&eacute;todo) que no admite par&aacute;metros y retorna un string.<\/li>\r\n    <li>Si <code data-start=\"1841\" data-end=\"1847\">exec<\/code> devuelve la propiedad <code data-start=\"1870\" data-end=\"1879\">&quot;error&quot;<\/code> en el objeto de salida, el modelo se detendr&aacute;.<\/li>\r\n<\/ul>\r\n<p data-start=\"468\" data-end=\"592\">Todas las clases deber&aacute;n incorporar al menos el constructor y el m&eacute;todo <code data-start=\"2000\" data-end=\"2006\">exec<\/code>.<\/p>\r\n<p data-start=\"468\" data-end=\"592\">&nbsp;<\/p>\r\n<h3>Uso desde el modelo:<\/h3>\r\n<p data-start=\"468\" data-end=\"592\">Para ejecutar su l&oacute;gica desde el modelo se siguen estos pasos:<\/p>\r\n<p data-start=\"468\" data-end=\"592\">1.- Cargar la clase a trav&eacute;s de <code>LOAD_CLASS<\/code> (la URL debe soportar CORS):<\/p>\r\n<pre>\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/Sum.js<\/pre>\r\n<p data-start=\"468\" data-end=\"592\">Instanciar la clase a trav&eacute;s de <code>INSTANCE_CLASS<\/code>:<\/p>\r\n<pre>\r\nINSTANCE_CLASS {name:sum, class:Sum}<\/pre>\r\n<p data-start=\"468\" data-end=\"592\">Y llamar al m&eacute;todo exec con el input que necesitemos a trav&eacute;s de <code>EXEC_CLASS<\/code>:<\/p>\r\n<pre>\r\nassign inputs,{a:10,b:20,acc:D$N}\r\nEXEC_CLASS {name:sum,in:V$inputs,savevalue:data} ; opcional assign:data<\/pre>\r\n<p data-start=\"468\" data-end=\"592\">El recurso externo no altera el flujo del modelo: recibe datos, los procesa y devuelve un resultado que el modelo utiliza mediante un <code data-start=\"773\" data-end=\"784\">savevalue<\/code> o un <code data-start=\"790\" data-end=\"798\">assign<\/code> en la entidad invocante.<\/p>\r\n<p data-start=\"468\" data-end=\"592\">Adem&aacute;s, se puede acceder a sus datos internos mediante el SNA:<\/p>\r\n<pre>\r\nE$(instancia,m&eacute;todo)<\/pre>\r\n<p data-start=\"468\" data-end=\"592\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "402",
                "nombre": "Recursos multimedia — representación e interacción",
                "texto": "\/* \r\nInjected Resources \r\n*\/ \r\n\r\nPOSITION {NAME:POS1,X:620,Y:360} \r\nGRAPHIC {NAME:Text1,TYPE:TEXT,X:373,Y:268,TEXT:\"DSL Graphic\",font:\"24\"} ; Create text graphic \r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/Sum.js \r\nINSTANCE_CLASS {name:sum, class:Sum} \r\n\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/Display.js \r\nINSTANCE_MEDIA {name:displayA, class:Display} \r\nINSTANCE_MEDIA {name:displayB, class:Display} \r\n\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/ClickSound.js \r\nINSTANCE_MEDIA {name:clickA, class:ClickSound} \r\nINSTANCE_MEDIA {name:clickB, class:ClickSound} \r\n\r\nFACILITY {NAME:counter1,X:373,Y:365,capacity:3} \r\nSTART 2000 \r\n\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN} \r\n;***************************************** \r\nPROCEDURE PRE_RUN \r\nassign config,{ \r\n    operation:\"init\", \r\n    sx:220, sy:50, \r\n    ; fontSize:28, \r\n    font:\"24px sans-serif\", \r\n    color:\"#ffff00\", \r\n    background:\"rgba(0,0,0,0.5)\", \r\n    align:\"center\", \r\n    x:120, y:140, z:0 \r\n    } \r\n\r\nEXEC_MEDIA {name:displayA,in:V$config} \r\nassign config,{ \r\n    operation:\"init\", \r\n    sx:220, sy:50, \r\n    ;fontSize:28, \r\n    font:\"24px Arial\", \r\n    color:\"#a0b7b8ff\", \r\n    background:\"rgba(0,0,0,0.5)\", \r\n    align:\"center\", x:420, y:140, z:0 \r\n    } \r\nEXEC_MEDIA {name:displayB,in:V$config} \r\nassign soundConfig,{ \r\n    operation:\"init\", \r\n    volume:0.3, \r\n    frequency:800, \r\n    duration:0.03 \r\n    }\r\n\r\n\r\nEXEC_MEDIA {name:clickA,in:V$soundConfig} \r\nassign soundConfig,{ \r\n    operation:\"init\", \r\n    volume:0.3, \r\n    frequency:400, \r\n    duration:0.03 \r\n    } \r\n EXEC_MEDIA {name:clickB,in:V$soundConfig} \r\n TERMINATE_VE \r\n ENDPROCEDURE \r\n \r\n ;***************************************************** \r\n GENERATE 10,3 {NAME:GEN1,X:111,Y:366} \r\n    ADVANCE 5 {TO:counter1} \r\n    assign playInput,{operation:\"play\"} \r\n    \r\n    assign inputs,{a:10,b:20,acc:D$N} \r\n    EXEC_CLASS {name:sum,in:V$inputs,savevalue:result} \r\n    \r\n    MOVE {name:Text1,text:\"Result X$(result.out) ACC: E$(sum,acc)\"} \r\n    ASSIGN panel,{operation:\"write\",value:\"Result X$(result.out)\"} \r\n\r\n    EXEC_MEDIA {name:displayA,in:V$panel} \r\n    EXEC_MEDIA {name:displayB,in:V$panel} \r\n    EXEC_MEDIA {name:clickA,in:P$playInput} \r\n\r\n    SEIZE counter1 \r\n    ADVANCE 20,20 \r\n    EXEC_MEDIA {name:clickB,in:P$playInput} \r\n    RELEASE counter1 \r\n\r\n    ADVANCE 20 {TO:POS1} \r\n ENDGENERATE 1",
                "descripcion": "<p data-start=\"287\" data-end=\"433\">Adem&aacute;s de l&oacute;gica, GPSS-Plus permite integrar recursos multimedia que interact&uacute;an con el modelo en tiempo real: visualizaci&oacute;n, sonido e interfaces.<\/p>\r\n<h3>Naturaleza de los recursos multimedia<\/h3>\r\n<p>Los recursos multimedia se integran como componentes externos que reciben instrucciones desde el modelo.<\/p>\r\n<ul>\r\n    <li>No disponen de API del motor<\/li>\r\n    <li>No crean VEs ni alteran el estado del modelo<\/li>\r\n    <li>No modifican el flujo ni el tiempo<\/li>\r\n    <li>No devuelven datos al modelo<\/li>\r\n<\/ul>\r\n<p>Su funci&oacute;n es exclusivamente ejecutar acciones de representaci&oacute;n o interacci&oacute;n.<\/p>\r\n<p data-start=\"1035\" data-end=\"1062\">A diferencia de las clases l&oacute;gicas:<\/p>\r\n<ul data-start=\"1064\" data-end=\"1175\">\r\n    <li data-section-id=\"ubl7ym\" data-start=\"1064\" data-end=\"1113\"><strong data-start=\"1066\" data-end=\"1075\">CLASS<\/strong> &rarr; transforma datos (input &rarr; output)<\/li>\r\n    <li data-section-id=\"18m9oy0\" data-start=\"1114\" data-end=\"1175\"><strong data-start=\"1116\" data-end=\"1125\">MEDIA<\/strong> &rarr; ejecuta acciones (input &rarr; efecto visual\/sonoro)<\/li>\r\n<\/ul>\r\n<p data-start=\"435\" data-end=\"541\">Esto garantiza que la capa de representaci&oacute;n no interfiera con la l&oacute;gica del modelo.<\/p>\r\n<h3><span role=\"text\"><strong data-start=\"1277\" data-end=\"1291\">Uso b&aacute;sico<\/strong><\/span><\/h3>\r\n<p data-start=\"1293\" data-end=\"1359\">Los recursos multimedia se utilizan de forma an&aacute;loga a las clases:<\/p>\r\n<p>1. Cargar el recurso<\/p>\r\n<pre>\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/Display.js<\/pre>\r\n<p>2.-&nbsp;Instanciarlo<\/p>\r\n<pre>\r\nINSTANCE_MEDIA {name:displayA, class:Display}<\/pre>\r\n<p>3. Ejecutarlo<\/p>\r\n<pre>\r\nASSIGN panel,{operation:&quot;write&quot;,value:&quot;Result X$(result.out)&quot;}\r\nEXEC_MEDIA {name:displayA,in:V$panel}<\/pre>\r\n<p>El modelo env&iacute;a instrucciones al recurso mediante un objeto de entrada.<br data-start=\"1677\" data-end=\"1680\">\r\nEl recurso ejecuta la acci&oacute;n correspondiente, sin alterar el estado del modelo.<\/p>\r\n<h3><span role=\"text\"><strong data-start=\"1769\" data-end=\"1780\">En el ejemplo<\/strong><\/span><\/h3>\r\n<ul data-start=\"1800\" data-end=\"1961\">\r\n    <li data-section-id=\"1plbrqw\" data-start=\"1800\" data-end=\"1857\">Se utiliza una clase (<code data-start=\"1824\" data-end=\"1829\">Sum<\/code>) para realizar un c&aacute;lculo<\/li>\r\n    <li data-section-id=\"tye496\" data-start=\"1858\" data-end=\"1903\">Se muestran los resultados en dos paneles<\/li>\r\n    <li data-section-id=\"1mux9d7\" data-start=\"1904\" data-end=\"1961\">Se reproducen sonidos en distintos momentos del flujo<\/li>\r\n<\/ul>\r\n<p data-start=\"1963\" data-end=\"2067\">El modelo sigue siendo el que gobierna la ejecuci&oacute;n; los recursos multimedia &uacute;nicamente reaccionan a &eacute;l.<\/p>\r\n<p><b>Si se requiere una visualizaci&oacute;n continua o dependiente del tiempo<\/b>, los recursos MEDIA pueden implementar dos m&eacute;todos especiales:<\/p>\r\n<pre>\r\nonRenderFrame(AC1,x,y,z,visible,subtitle){\r\n\tthis.mesh.visible = visible;\r\n        this.mesh.rotation.y = (AC1);\r\n        this.mesh.position.set(x,y,z);\r\n\t}\r\n\t\t\r\nonChangeVector(AC1,vector) {\r\n\t\/*\tvector composition\r\n\t\t{\r\n\t\t  position: [x,y,z],\r\n\t\t  visible: true,\r\n\t\t  from: [x,y,z],\r\n\t\t  to: [x,y,z],\r\n\t\t  time: [t_start,t_end]\r\n\t\t}\t\t\r\n\t*\/\r\n        }<\/pre>\r\n<p><code>onRenderFrame <\/code>ser&aacute; invocado autom&aacute;ticamente por el motor de visualizaci&oacute;n en cada ciclo de redibujado, recibiendo como par&aacute;metro el tiempo actual de la simulaci&oacute;n y posici&oacute;n.<\/p>\r\n<p>De esta forma se crean por separado la recepci&oacute;n de los valores necesarios (EXEC_MEDIA) para la visualizaci&oacute;n de la propia visualizaci&oacute;n dependente del tiempo.<br>\r\n<br>\r\nPor otro lado, <code>onChangeVector <\/code>ser&aacute; invocado cuando cambie el vector de posicionado, es decir, cada <code>ADVANCE <\/code>o&nbsp;<code>UPDATE<\/code>.<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "406",
                "nombre": "Recursos multimedia HTML - interfaz desacoplada del canvas",
                "texto": "FACILITY {NAME:FAC1,X:321,Y:351,capacity:4}\r\n\r\nPOSITION {NAME:POSFIN,X:559,Y:299,TYPE:TERMINATE,TITLE:TERMINATE}\r\n\r\nSTART 100\r\n\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/LogHTML.js\r\nINSTANCE_MEDIA {name:logUI, class:LogHTML}\r\n\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN} \r\n;***************************************** \r\nPROCEDURE PRE_RUN \r\nassign config,{ \r\n    operation:\"init\", \r\n    maxLines:50,\r\n    rows:8\r\n    } \r\n\r\nEXEC_MEDIA {name:logUI,in:V$config} \r\nTERMINATE_VE\r\nENDPROCEDURE\r\n;************************************************************************\r\n\r\nGENERATE 10,0 {NAME:GEN1,X:43,Y:296}\r\n\r\n    ADVANCE 10,0 {TO:FAC1,flow:1,layout:H}\r\n\r\n    ASSIGN msg,{operation:\"write\",value:\"Entity D$N process\"}\r\n    EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n    SEIZE FAC1\r\n    ADVANCE 20,0\r\n    RELEASE FAC1\r\n\r\n    ADVANCE 10,0 {TO:POSFIN,flow:1,layout:H}\r\n\r\nENDGENERATE 1\r\n\r\n\r\n\r\n",
                "descripcion": "<p>Adem&aacute;s del canvas 3D, GPSS-Plus dispone de un contenedor HTML para representar interfaces fuera del entorno gr&aacute;fico.<\/p>\r\n<pre><span class=\"ͼg\">&lt;div<\/span><span> <\/span><span class=\"ͼf\">id<\/span><span class=\"ͼ8\">=<\/span><span class=\"ͼc\">&quot;ui-layer&quot;<\/span><span class=\"ͼg\">&gt;&lt;\/div&gt;<\/span><\/pre>\r\n<p>Los recursos MEDIA pueden escribir en <code data-start=\"407\" data-end=\"417\">ui-layer<\/code> utilizando el DOM (por ejemplo, creando elementos con <code data-start=\"472\" data-end=\"509\">document.getElementById(&quot;ui-layer&quot;)<\/code>).<\/p>\r\n<h2 data-section-id=\"1hryw4s\" data-start=\"660\" data-end=\"666\">Uso<\/h2>\r\n<p data-start=\"668\" data-end=\"706\">Exactamente igual que cualquier MEDIA:<\/p>\r\n<pre>\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/LogHTML.js\r\nINSTANCE_MEDIA {name:logUI, class:LogHTML}\r\n\r\nASSIGN msg,{operation:&quot;write&quot;,value:&quot;Hola&quot;}\r\nEXEC_MEDIA {name:logUI,in:V$msg}<\/pre>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "403",
                "nombre": "ejemplo continuo — modelo epidemiológico",
                "texto": "SYSTEM {TYPE:OPTIONS,SPEED:8,TIME_DECIMALS:2}\r\n\r\nGRAPHIC {NAME:Text1,TYPE:TEXT,X:233,Y:244,TEXT:\"Epidemic model\",font:\"12\"}\r\n\r\nPLOTTER {NAME:epiPlot,Y_0:S,Y_1:I,Y_2:R,X:TIME_AC1}\r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/EpidemicModel.js\r\nINSTANCE_CLASS {name:epidemic, class:EpidemicModel}\r\n\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/VerticalGauge.js\r\nINSTANCE_MEDIA {name:gaugeS, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeI, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeR, class:VerticalGauge}\r\n\r\nSTART 2000\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\nPROCEDURE PRE_RUN\r\n\r\n    ASSIGN epiInit,{\r\n      operation:\"init\",\r\n      S:990,\r\n      I:10,\r\n      R:0,\r\n      beta:0.4,\r\n      gamma:0.08\r\n    }\r\n\r\n    EXEC_CLASS {name:epidemic,in:V$epiInit,savevalue:epiState}\r\n\r\n    ASSIGN cfgS,{\r\n      operation:\"init\",\r\n      title:\"S\",\r\n      x:120,\r\n      y:400,\r\n      width:80,\r\n      height:260,\r\n      value:990,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#f39c12\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"12px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeS,in:V$cfgS}\r\n\r\n    ASSIGN cfgI,{\r\n      operation:\"init\",\r\n      title:\"I\",\r\n      x:260,\r\n      y:400,\r\n      width:80,\r\n      height:260,\r\n      value:10,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#3498db\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"12px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeI,in:V$cfgI}\r\n\r\n    ASSIGN cfgR,{\r\n      operation:\"init\",\r\n      title:\"R\",\r\n      x:400,\r\n      y:400,\r\n      width:80,\r\n      height:260,\r\n      value:0,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#8e44ad\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"12px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeR,in:V$cfgR}\r\n\r\n    ASSIGN epiInit,{\r\n      operation:\"init\",\r\n      S:990,\r\n      I:10,\r\n      R:0,\r\n      beta:0.4,\r\n      gamma:0.08\r\n    }\r\n\r\n    EXEC_CLASS {name:epidemic,in:V$epiInit,savevalue:epiState}\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nGENERATE 0,0,0,1\r\n\r\n    while (1==1)\r\n\r\n        ASSIGN epiStep,{operation:\"step\",dt:0.2}\r\n        EXEC_CLASS {name:epidemic,in:V$epiStep,savevalue:epiState}\r\n\r\n        ASSIGN s,round(X$(epiState.S),2)\r\n        ASSIGN i,round(X$(epiState.I),2)\r\n        ASSIGN r,round(X$(epiState.R),2)\r\n        ASSIGN t,round(X$(epiState.time),2)\r\n\r\n        MOVE {name:Text1,text:\"t=P$t  S=P$s  I=P$i  R=P$r\"}\r\n\r\n        PLOT epiPlot, P$s, P$i, P$r\r\n\r\n        ASSIGN setS,{operation:\"set\",value:P$s}\r\n        EXEC_MEDIA {name:gaugeS,in:V$setS}\r\n\r\n        ASSIGN setI,{operation:\"set\",value:P$i}\r\n        EXEC_MEDIA {name:gaugeI,in:V$setI}\r\n\r\n        ASSIGN setR,{operation:\"set\",value:P$r}\r\n        EXEC_MEDIA {name:gaugeR,in:V$setR}\r\n\r\n        IF (X$(epiState.I) <= 0.01)\r\n            stop\r\n        ENDIF\r\n\r\n        ADVANCE 0.2,0\r\n    endwhile\r\n\r\nENDGENERATE 1",
                "descripcion": "<p data-start=\"413\" data-end=\"518\">Este ejemplo muestra un caso muy simple de integraci&oacute;n entre el modelo y una clase externa en Javascript en el mundo de la simulaci&oacute;n cont&iacute;nua.<\/p>\r\n<p data-start=\"520\" data-end=\"659\">La clase <code data-start=\"529\" data-end=\"544\">EpidemicModel<\/code> contiene la l&oacute;gica matem&aacute;tica de evoluci&oacute;n de una epidemia.<br data-start=\"604\" data-end=\"607\">\r\nEl modelo no implementa esas ecuaciones porque son incorporadas por la clase esterna. El modelo se encarga de:<\/p>\r\n<ul data-start=\"661\" data-end=\"800\">\r\n    <li data-section-id=\"15sikxf\" data-start=\"661\" data-end=\"702\">inicializa la clase con unos par&aacute;metros<\/li>\r\n    <li data-section-id=\"17nhlrw\" data-start=\"703\" data-end=\"741\">ejecuta pasos sucesivos de evoluci&oacute;n<\/li>\r\n    <li data-section-id=\"vpaabk\" data-start=\"742\" data-end=\"769\">recoge el estado devuelto<\/li>\r\n    <li data-section-id=\"b0j329\" data-start=\"770\" data-end=\"800\">y lo representa gr&aacute;ficamente<\/li>\r\n<\/ul>\r\n<p data-start=\"802\" data-end=\"963\">De este modo, la parte algor&iacute;tmica queda encapsulada en la clase, mientras que GPSS-Plus sigue gobernando la ejecuci&oacute;n del sistema, el tiempo y su visualizaci&oacute;n.<\/p>\r\n<h3>En el ejemplo<\/h3>\r\n<p data-start=\"991\" data-end=\"1057\">Se utilizar&aacute; un modelo epidemiol&oacute;gico cl&aacute;sico con tres magnitudes:<\/p>\r\n<ul data-start=\"1059\" data-end=\"1142\">\r\n    <li data-section-id=\"s7mr9v\" data-start=\"1059\" data-end=\"1087\"><code data-start=\"1061\" data-end=\"1064\">S<\/code>: poblaci&oacute;n susceptible<\/li>\r\n    <li data-section-id=\"1hx3f4b\" data-start=\"1088\" data-end=\"1114\"><code data-start=\"1090\" data-end=\"1093\">I<\/code>: poblaci&oacute;n infectada<\/li>\r\n    <li data-section-id=\"2l4ydh\" data-start=\"1115\" data-end=\"1142\"><code data-start=\"1117\" data-end=\"1120\">R<\/code>: poblaci&oacute;n recuperada<\/li>\r\n<\/ul>\r\n<p data-start=\"1144\" data-end=\"1232\">La clase externa calcula su evoluci&oacute;n en cada paso de tiempo.<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "404",
                "nombre": "Ejemplo discreto — rutas y logística",
                "texto": "SYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\nGRAPHIC {NAME:Text1,TYPE:TEXT,X:641,Y:30,TEXT:\"Hello\"}\r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/PathSolver.js\r\nINSTANCE_CLASS {NAME: brain, CLASS: PathSolver}\r\n\r\nINCLUDE .\/manual_es\/Season_13_logica_externa\/bbdd.mod\r\nSTART 1\r\n\r\nGENERATE 0,0,0,1 {visible:0}\r\n\r\n    ; 1. Query contract\r\n    ASSIGN query, { op: \"get_path\", from: \"Salamanca\", to: \"A_Coruna\" }\r\n\r\n    ; 2. Solver call\r\n    EXEC_CLASS { name: brain, in: V$query, assign: result }\r\n\r\n    ; 3. Show result\r\n    MOVE {\r\n        NAME: Text1,\r\n        TEXT: \"Route: X$(result.path.LENGTH) steps. Dist: X$(result.total_dist)\",\r\n        color:red\r\n    }\r\n\r\n    ; 4. Step-by-step movement\r\n    FOREACH point, IN, V$(result.path)\r\n        ADVANCE 10 { TO: \"position_P$point\" }\r\n    ENDFOREACH\r\n\r\n    MOD {color:red}\r\n\r\n    ; Unordered stops\r\n    ASSIGN stops, [\"Malaga\", \"Valencia\"]\r\n\r\n    ; Optimized tour\r\n    ASSIGN q, {\r\n        op: \"get_tour\",\r\n        start_node: \"Madrid\",\r\n        end_node: \"Barcelona\",\r\n        stops: V$stops\r\n    }\r\n\r\n    EXEC_CLASS { name: brain, in: V$q, assign: routePlan }\r\n\r\n    FOREACH stop, IN, V$(routePlan.optimized_path)\r\n        MOD {subtitle:\"P$stop\"}\r\n        ADVANCE 10 { TO: \"position_P$stop\" }\r\n    ENDFOREACH\r\n\r\nENDGENERATE 1\r\n\r\n;----------------------------------------------------------------------\r\nPROCEDURE PRE_RUN\r\n    CALL build_city_map\r\n    CALL build_weighted_graph\r\n\r\n    ASSIGN pack, { op: \"init_graph\", data: V$(weighted_graph) }\r\n    EXEC_CLASS { name: brain, in: V$pack }\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE",
                "descripcion": "<p data-start=\"618\" data-end=\"763\">Este ejemplo muestra un caso simple de integraci&oacute;n entre el modelo y una clase externa en Javascript dentro del &aacute;mbito de la simulaci&oacute;n discreta.<\/p>\r\n<p data-start=\"765\" data-end=\"1044\">El modelo define una red de ciudades conectadas entre s&iacute;. A partir de esas conexiones, construye un grafo con pesos y lo representa visualmente. La clase externa <code data-start=\"927\" data-end=\"939\">PathSolver<\/code> no construye el sistema ni gobierna su ejecuci&oacute;n: &uacute;nicamente resuelve consultas de ruta sobre ese grafo.<\/p>\r\n<p data-start=\"1046\" data-end=\"1070\">El modelo se encarga de:<\/p>\r\n<ul>\r\n    <li>definir ciudades y conexiones<\/li>\r\n    <li>calcular los pesos entre nodos<\/li>\r\n    <li>construir el grafo<\/li>\r\n    <li>invocar al solver con una consulta<\/li>\r\n    <li>recorrer visualmente la ruta devuelta<\/li>\r\n<\/ul>\r\n<p>De este modo, la parte algor&iacute;tmica queda encapsulada en la clase, mientras que GPSS-Plus sigue gobernando el flujo, el tiempo y la representaci&oacute;n del sistema.<\/p>\r\n<p data-start=\"1799\" data-end=\"1853\">La clase externa se limita a responder preguntas como:<\/p>\r\n<ul data-start=\"1855\" data-end=\"1966\">\r\n    <li data-section-id=\"1d124qa\" data-start=\"1855\" data-end=\"1901\"><code data-start=\"1857\" data-end=\"1867\">get_path<\/code>: obtener una ruta entre dos nodos<\/li>\r\n    <li data-section-id=\"zwv5b2\" data-start=\"1902\" data-end=\"1966\"><code data-start=\"1904\" data-end=\"1914\">get_tour<\/code>: obtener un recorrido optimizado con varias paradas<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            },
            {
                "id": "405",
                "nombre": "Ejemplo compuesto — sistema multidominio",
                "texto": "\/*\r\n    MINIMAL UNIVERSE\r\n    Chemistry + Epidemiology + MEDIA\r\n*\/\r\n\r\nSYSTEM {TYPE:OPTIONS,SPEED:8,TIME_DECIMALS:2}\r\n\r\nGRAPHIC {NAME:Text1,TYPE:TEXT,X:409,Y:421,TEXT:\"Universe\",font:\"12\"}\r\n\r\nPLOTTER {NAME:epiPlot,Y_0:S,Y_1:I,Y_2:R,X:TIME_AC1}\r\nPLOTTER {NAME:chemPlot,Y_0:C,Y_1:BETA,X:TIME_AC1}\r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/ChemistryReactor.js\r\nINSTANCE_CLASS {name:reactor, class:ChemistryReactor}\r\n\r\nLOAD_CLASS https:\/\/gpss-plus.com\/imports_js\/EpidemicModel.js\r\nINSTANCE_CLASS {name:epidemic, class:EpidemicModel}\r\n\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/VerticalGauge.js\r\nINSTANCE_MEDIA {name:gaugeS, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeI, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeR, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeC, class:VerticalGauge}\r\nINSTANCE_MEDIA {name:gaugeBeta, class:VerticalGauge}\r\n\r\nSTART 2000\r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\n;========================================================\r\nPROCEDURE PRE_RUN\r\n\r\n    ; --- Chemical system ---\r\n    ASSIGN chemInit,{\r\n      operation:\"init\",\r\n      A:10,\r\n      B:5,\r\n      C:0,\r\n      k:0.05\r\n    }\r\n    EXEC_CLASS {name:reactor,in:V$chemInit,savevalue:chemState}\r\n\r\n    ; --- Epidemiological system ---\r\n    ASSIGN epiInit,{\r\n      operation:\"init\",\r\n      S:990,\r\n      I:10,\r\n      R:0,\r\n      beta:0.45,\r\n      gamma:0.08\r\n    }\r\n    EXEC_CLASS {name:epidemic,in:V$epiInit,savevalue:epiState}\r\n\r\n    ; --- Epidemiology gauges ---\r\n    ASSIGN cfgS,{\r\n      operation:\"init\",\r\n      title:\"S\",\r\n      x:100,\r\n      y:150,\r\n      width:70,\r\n      height:220,\r\n      value:990,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#f39c12\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"18px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeS,in:V$cfgS}\r\n\r\n    ASSIGN cfgI,{\r\n      operation:\"init\",\r\n      title:\"I\",\r\n      x:190,\r\n      y:150,\r\n      width:70,\r\n      height:220,\r\n      value:10,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#3498db\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"18px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeI,in:V$cfgI}\r\n\r\n    ASSIGN cfgR,{\r\n      operation:\"init\",\r\n      title:\"R\",\r\n      x:280,\r\n      y:150,\r\n      width:70,\r\n      height:220,\r\n      value:0,\r\n      min_value:0,\r\n      max_value:1000,\r\n      color:\"#8e44ad\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"18px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeR,in:V$cfgR}\r\n\r\n    ; --- Chemical gauge ---\r\n    ASSIGN cfgC,{\r\n      operation:\"init\",\r\n      title:\"C\",\r\n      x:420,\r\n      y:150,\r\n      width:70,\r\n      height:220,\r\n      value:0,\r\n      min_value:0,\r\n      max_value:5,\r\n      color:\"#27ae60\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"18px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeC,in:V$cfgC}\r\n\r\n    ; --- Effective beta gauge ---\r\n    ASSIGN cfgBeta,{\r\n      operation:\"init\",\r\n      title:\"beta\",\r\n      x:510,\r\n      y:150,\r\n      width:70,\r\n      height:220,\r\n      value:0.45,\r\n      min_value:0,\r\n      max_value:0.5,\r\n      color:\"#e74c3c\",\r\n      background:\"rgba(255,255,255,0.85)\",\r\n      font:\"18px Arial\",\r\n      valueFont:\"12px Arial\"\r\n    }\r\n    EXEC_MEDIA {name:gaugeBeta,in:V$cfgBeta}\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n;========================================================\r\nGENERATE 0,0,0,1 {name:Gen1,x:87,y:527}\r\n\r\n    SAVEVALUE baseBeta, 0.45\r\n    SAVEVALUE alphaChem, 0.30\r\n\r\n    while (1==1)\r\n\r\n        ;----------------------------------\r\n        ; 1) advance chemistry\r\n        ASSIGN chemStep,{operation:\"step\",dt:0.2}\r\n        EXEC_CLASS {name:reactor,in:V$chemStep,savevalue:chemState}\r\n\r\n        ASSIGN chemC, X$(chemState.C)\r\n\r\n        ;----------------------------------\r\n        ; 2) coupling\r\n        ASSIGN betaEff, X$baseBeta \/ (1 + X$alphaChem * P$chemC)\r\n\r\n        ;----------------------------------\r\n        ; 3) advance epidemiology\r\n        ASSIGN epiStep,{\r\n          operation:\"step\",\r\n          dt:0.2,\r\n          beta:P$betaEff\r\n        }\r\n        EXEC_CLASS {name:epidemic,in:V$epiStep,savevalue:epiState}\r\n\r\n        ;----------------------------------\r\n        ; 4) rounding\r\n        ASSIGN t, round(X$(epiState.time),2)\r\n\r\n        ASSIGN s, round(X$(epiState.S),2)\r\n        ASSIGN i, round(X$(epiState.I),2)\r\n        ASSIGN r, round(X$(epiState.R),2)\r\n\r\n        ASSIGN c, round(X$(chemState.C),3)\r\n        ASSIGN betaVal, round(P$betaEff,4)\r\n\r\n        MOVE {\r\n          name:Text1,\r\n          text:\"t=P$t  S=P$s  I=P$i  R=P$r  C=P$c  beta=P$betaVal\"\r\n        }\r\n\r\n        PLOT epiPlot, P$s, P$i, P$r\r\n        PLOT chemPlot, P$c, P$betaVal\r\n\r\n        ;----------------------------------\r\n        ; 5) update MEDIA\r\n        ASSIGN setS,{operation:\"set\",value:P$s}\r\n        EXEC_MEDIA {name:gaugeS,in:V$setS}\r\n\r\n        ASSIGN setI,{operation:\"set\",value:P$i}\r\n        EXEC_MEDIA {name:gaugeI,in:V$setI}\r\n\r\n        ASSIGN setR,{operation:\"set\",value:P$r}\r\n        EXEC_MEDIA {name:gaugeR,in:V$setR}\r\n\r\n        ASSIGN setC,{operation:\"set\",value:P$c}\r\n        EXEC_MEDIA {name:gaugeC,in:V$setC}\r\n\r\n        ASSIGN setBeta,{operation:\"set\",value:P$betaVal}\r\n        EXEC_MEDIA {name:gaugeBeta,in:V$setBeta}\r\n\r\n        IF (X$(epiState.I) <= 0.01)\r\n            STOP\r\n        ENDIF\r\n\r\n        ADVANCE 0.2,0\r\n    endwhile\r\n\r\nENDGENERATE 1",
                "descripcion": "<p data-start=\"700\" data-end=\"814\">Este ejemplo muestra c&oacute;mo varios modelos independientes pueden coexistir e interactuar dentro de un mismo sistema.<\/p>\r\n<p data-start=\"816\" data-end=\"851\">Se combinan dos dominios distintos:<\/p>\r\n<ul data-start=\"853\" data-end=\"899\">\r\n    <li data-section-id=\"x0o5xu\" data-start=\"853\" data-end=\"872\">un modelo qu&iacute;mico<\/li>\r\n    <li data-section-id=\"vg7oc8\" data-start=\"873\" data-end=\"899\">un modelo epidemiol&oacute;gico<\/li>\r\n<\/ul>\r\n<p data-start=\"901\" data-end=\"1068\">Cada uno de ellos est&aacute; implementado en una clase externa y evoluciona de forma independiente. El modelo se encarga de coordinarlos y establecer relaciones entre ellos.<\/p>\r\n<p data-start=\"1070\" data-end=\"1234\">En este caso, la evoluci&oacute;n del sistema qu&iacute;mico modifica din&aacute;micamente el par&aacute;metro <code data-start=\"1153\" data-end=\"1159\">beta<\/code> del modelo epidemiol&oacute;gico, generando un acoplamiento entre ambos sistemas.<\/p>\r\n<p data-start=\"1236\" data-end=\"1246\">El modelo:<\/p>\r\n<ul data-start=\"1248\" data-end=\"1362\">\r\n    <li data-section-id=\"11295vk\" data-start=\"1248\" data-end=\"1283\">ejecuta cada sistema por separado<\/li>\r\n    <li data-section-id=\"atv5tr\" data-start=\"1284\" data-end=\"1321\">intercambia informaci&oacute;n entre ellos<\/li>\r\n    <li data-section-id=\"13tzfay\" data-start=\"1322\" data-end=\"1362\">representa su evoluci&oacute;n en tiempo real<\/li>\r\n<\/ul>\r\n<p>Cada componente mantiene su l&oacute;gica; el modelo define c&oacute;mo se relacionan.<\/p>\r\n<h3 data-section-id=\"14eb7au\" data-start=\"1449\" data-end=\"1473\"><span role=\"text\"><strong data-start=\"1453\" data-end=\"1473\">El ejemplo<\/strong><\/span><\/h3>\r\n<ul data-start=\"1475\" data-end=\"1660\">\r\n    <li data-section-id=\"1m218bk\" data-start=\"1475\" data-end=\"1512\">La qu&iacute;mica produce una magnitud <code data-start=\"1509\" data-end=\"1512\">C<\/code><\/li>\r\n    <li data-section-id=\"1jy7qsg\" data-start=\"1513\" data-end=\"1566\">Esa magnitud modifica la propagaci&oacute;n de la epidemia<\/li>\r\n    <li data-section-id=\"1t7ni7\" data-start=\"1567\" data-end=\"1607\">Ambos sistemas evolucionan en paralelo<\/li>\r\n    <li data-section-id=\"1bfevs2\" data-start=\"1608\" data-end=\"1660\">El modelo los conecta mediante una relaci&oacute;n simple<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": []
            }
        ]
    },
    {
        "id": "308",
        "nombre": "Spin-offs",
        "texto": "",
        "descripcion": null,
        "parametros": null,
        "parametros_json": null,
        "ejemplo": null,
        "hijos": [
            {
                "id": "270",
                "nombre": "Interfaz de Usuario-Modelo UI",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "271",
                        "nombre": "Ejemplo de uso",
                        "texto": ";========== UI DECLARATIVO ==========\r\nUI {TYPE: BUTTON, id:botonA,TEXT: \"Lanzar Entidad A\", LABEL: \"Botón A\", TRIGGER: lanzarA}\r\nUI {TYPE: BUTTON, id:botonB, TEXT: \"Lanzar Entidad B\", LABEL: \"Valores de fábrica\", TRIGGER: lanzarB}\r\n\r\nUI {TYPE: INPUT, ID: unInput, LABEL: \"Nombre\", VALUE: \"Alice\", TRIGGER: capturarInput}\r\nUI {\r\n  TYPE: SLIDER, ID: unSlider, LABEL: \"Velocidad\",\r\n  VALUE: 1, MIN: 0.1, MAX: 2, STEP: 0.1,\r\n  TRIGGER: capturarInput\r\n}\r\nUI {TYPE: SELECT, id:unSelect,LABEL: \"Modo\", OPTIONS: \"Normal,Avanzado,Turbo\", TRIGGER: capturarInput}\r\nUI {TYPE: CHECKBOX, id:unCheck,LABEL: \"Activar turbo\", VALUE: 1, TRIGGER: capturarInput}\r\nUI {TYPE: RADIO, id:unRadio, LABEL: \"Color\", OPTIONS: \"Rojo,Verde,Azul\", TRIGGER: capturarInput}\r\n\r\n;========== GRÁFICOS ==========\r\nGRAPHIC {NAME:infoTexto, TYPE:TEXT, X:400, Y:100, TEXT:\"Esperando...\"}\r\nGRAPHIC {NAME:infoDato, TYPE:TEXT, X:400, Y:130, TEXT:\"Ningún valor aún\"}\r\n\r\n;========== POSICIONES Y ANIMACIÓN ==========\r\nPOSITION {NAME:ENTRADA, X:200, Y:220}\r\nPOSITION {NAME:SALIDA, X:500, Y:220}\r\n\r\nSTART 100\r\n;-----------------------------------------------------------------\r\n\r\nGENERATE 0,0,0,0 {NAME:GEN_A, X:102,Y:161,ECOLOR:#0099ff}\r\nADVANCE 10,0 {TO:ENTRADA}\r\nADVANCE 10,0 {TO:SALIDA}\r\nTERMINATE 1\r\n\r\nGENERATE 0,0,0,0 {NAME:GEN_B, X:104,Y:406,ECOLOR:#ff9900}\r\nADVANCE 10,0 {TO:ENTRADA}\r\nADVANCE 10,0 {TO:SALIDA}\r\nSET_UI unSlider, 1.5\r\nSET_UI unInput, \"Fin camino B\"\r\nSET_UI unSelect, \"turbo\"\r\nSET_UI unRadio, \"AZUL\"\r\nSET_UI unCheck, 1\r\n\r\nTERMINATE 1\r\n\r\n;========== PROCEDIMIENTOS ==========\r\nPROCEDURE lanzarA\r\n  NEW GEN_A\r\n  TERMINATE\r\nENDPROCEDURE 1\r\n\r\nPROCEDURE lanzarB\r\n  NEW GEN_B\r\n  TERMINATE\r\nENDPROCEDURE 1\r\n\r\nPROCEDURE capturarInput\r\n  MOVE {NAME:infoTexto, TEXT:\"ID = PP$A\"}\r\n  MOVE {NAME:infoDato, TEXT:\"VALUE = PP$B\"}\r\n\r\n  ; Si es el input del nombre, actualiza UI desde GPSS\r\n  IF \"botonA\",==,\"PP$A\"\r\n    SET_UI velSlider, 1.5\r\n  ENDIF\r\n\r\n  TERMINATE\r\nENDPROCEDURE 1\r\n\r\n\r\n",
                        "descripcion": "Este ejemplo muestra el uso de todos los tipos de elementos UI disponibles en GPSS-Plus.\r\n\r\nAl 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.\r\n\r\nAdemá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.\r\nSolo necesitas declararles un ID para poder referenciarlos.\r\n\r\nEsta funcionalidad resulta especialmente útil para ajustar parámetros durante la simulación, probar escenarios, activar eventos manuales o construir entornos educativos interactivos.\r\n\r\nTodo el control es declarativo, y la lógica se mantiene en los procedimientos.",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "226",
                "nombre": "Funciones de comportamiento",
                "texto": "",
                "descripcion": "",
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "255",
                        "nombre": "Qué son las Behavior Functions",
                        "texto": "",
                        "descripcion": "<p>El concepto parte de la abstracci&oacute;n de un recurso m&aacute;s grande que una simple FACILITY o STORAGE.<\/p>\r\n<p>Imagina que cada taller puede ser representado como una funci&oacute;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&oacute;n micro para un taller de 6 empleados.<\/p>\r\n<p>GPSS-Plus toma la decisi&oacute;n <b>interpolada<\/b> de los resultados previsibles:<\/p>\r\n<code>ADVANCE BF$(BFtalleres, 4, 2, 4)<\/code>\r\n<p>Este bloque consultar&aacute; la funci&oacute;n de comportamiento m&aacute;s cercana, interpolar&aacute; los valores y crear&aacute; din&aacute;micamente una nueva funci&oacute;n adaptada.<\/p>\r\n<p>Esta es la filosof&iacute;a de las Behavior Functions: reutilizar conocimiento previo para simular infinitos escenarios nuevos a partir de la interpolaci&oacute;n de datos.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "257",
                        "nombre": "Una primera aplicación",
                        "texto": "SYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nGraphic {NAME:Text1,Type:TEXT,X:230,Y:496,Text:\"FN$gName1: b=20 \/ sigma1=2.5 \/ sigma2=2.5\"}\r\n\r\nGraphic {NAME:Text2,Type:TEXT,X:349,Y:100,Text:\"BF$(tramo1D 30): b=40 \/ sigma1=3.5 \/ sigma2=4.5 (interpolado)\"}\r\n\r\nGraphic {NAME:Text3,Type:TEXT,X:523,Y:397,Text:\"BF$(tramo1D 15): b=30 \/ sigma1=2.75 \/ sigma2=3 (interpolado)\"}\r\n\r\n\r\nFacility {NAME:VENTANILLA1,X:233,Y:444,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}\r\nFacility {NAME:VENTANILLA2,X:352,Y:152,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}\r\nFacility {NAME:VENTANILLA3,X:523,Y:354,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:80,capacity:30}\r\n\r\nPOSITION {NAME:POS1,X:708,Y:268}\r\n\r\nFunction {behavior: tramo1D, type: GAUSS, p0:10, Name:gName1, a:1, b:20, Sigma1:2.5, Sigma2:2.5}\r\nFunction {behavior: tramo1D, type: GAUSS, p0:20, Name:gName2, a:1, b:40, Sigma1:3.0, Sigma2:3.5}\r\n\r\nSTART 2000\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,0 {NAME:GEN1,X:56,Y:319}\r\n\r\nADVANCE 30,10 {TO:VENTANILLA1}\r\n\r\nSEIZE VENTANILLA1\r\nADVANCE FN$gName1\r\n;ADVANCE BF$(tramo1D,10)\r\nRELEASE VENTANILLA1\r\n\r\nADVANCE 20,10 {TO:VENTANILLA2}\r\n\r\nSEIZE VENTANILLA2\r\nADVANCE BF$(tramo1D,30)\r\nRELEASE VENTANILLA2\r\n\r\nADVANCE 20,10 {TO:VENTANILLA3}\r\n\r\nSEIZE VENTANILLA3\r\nADVANCE BF$(tramo1D,15)\r\nRELEASE VENTANILLA3\r\n\r\n\r\nADVANCE 20,0 {TO:POS1}\r\n\r\n\r\nTERMINATE 1\r\n\r\n\r\n",
                        "descripcion": "<p>En este primer ejemplo pr&aacute;ctico, vamos a aplicar el concepto de Behavior Functions (BF$) de forma sencilla.<\/p>\r\n<p>Hasta ahora, cuando necesit&aacute;bamos modelar un tiempo aleatorio, us&aacute;bamos funciones normales (FN$) previamente definidas. <br>\r\nCon las Behavior Functions, podemos interpolar autom&aacute;ticamente los par&aacute;metros adecuados a partir de unos pocos ejemplos, sin tener que definir funciones exactas para cada caso.<\/p>\r\n<p>En este caso partimos del dos casos ya estudiados que hemos plasmado en las siguientes funciones de comportamiento que hemos llamado<\/p>\r\n<p>&quot;tramo1D&quot;:<\/p>\r\n<pre>\r\nGFunction {behavior: tramo1D, p0:10, b:20, Sigma1:2.5, Sigma2:2.5}\r\nGFunction {behavior: tramo1D, p0:20, b:40, Sigma1:3.0, Sigma2:3.5}\r\n<\/pre>\r\n<p>Son dos funciones de distribuci&oacute;n gaussianas, ambas con un &uacute;nico par&aacute;metro que las defini&oacute;. <br>\r\nPara la primera P0=10 y la segunda P0=20. <br>\r\nImaginemos que son, por ejemplo, el n&uacute;mero de empleados en un taller.  Con ese dato como par&aacute;metro, los datos recogidos en el sistema daban las variables de la funci&oacute;n gaussiana b:20, Sigma1:2.5, Sigma2:2.5 y  b:40, Sigma1:3.0, Sigma2:3.5 respectivamente.<\/p>\r\n<p>Es decir: <br>\r\nP0=10  -&gt; b:20, Sigma1:2.5, Sigma2:2.5 <br>\r\nP0=20 -&gt; b:40, Sigma1:3.0, Sigma2:3.5<\/p>\r\n<p>Estas son nuestras Funciones de comportamiento.<\/p>\r\n<p>Por supuesto , puede que se recojan m&aacute;s posibilidades que har&aacute;n que el c&aacute;lculo gane en eficacia.<\/p>\r\n<p>Pues bien, ahora que tenemos como se comportan estos talleres, podremos predecir c&oacute;mo se comportar&aacute; cualquier otro del que conozcamos su par&aacute;metro configurador del comportamiento.<\/p>\r\n<p>Su P0.  As&iacute;, para el caso de P0=15, es inmediato pensar que, para una sola dimensi&oacute;n de interpolaci&oacute;n (un par&aacute;metro de configuraci&oacute;n equivale a una interpolaci&oacute;n lineal) la salida de la funci&oacute;n gaussiana resultante ser&aacute; exactamente la media de cada uno de los par&aacute;metros estudiados, puesto que 15 es la mitad entre 10 y 20.<\/p>\r\n<p>As&iacute;: P0=15 -&gt; b=30 , sigma1=2.75 , sigma2=3<\/p>\r\n<p>Para el caso de P0=30 los resultados ser&aacute;n: P0= 30 -&gt; b=60 ,  sigma1=3.5 , sigma2=4.5<\/p>\r\n<p>Las Behavior Functions van a hacer este c&aacute;lculo autom&aacute;ticamente.<\/p>\r\n<p>Para este ejemplo, simularemos el recorrido de entidades a trav&eacute;s de tres recursos:<\/p>\r\n<p>El primer tramo utilizar&aacute; una funci&oacute;n tradicional (FN$), como hasta ahora.<\/p>\r\n<p>Una gaussiana con B=20 se centrar&aacute; en ese tiempo.<\/p>\r\n<p>El segundo y tercer tramo utilizar&aacute;n funciones generadas din&aacute;micamente (BF$) a partir de un comportamiento interpolado.<\/p>\r\n<p>Los resultados en el informe deber&aacute;n ser curvas centradas en 20, 60 y 30 respectivamente.<\/p>\r\n<p>Ahora podr&aacute;s entender porqu&eacute;, en los informes, tienes una aproximaci&oacute;n de la f&oacute;rmula de la funci&oacute;n resultante del los datos estad&iacute;sticos recogidos. Cuando hagas una simulaci&oacute;n de un sistema complejo, estas f&oacute;rmulas se convertir&aacute;n en las funciones de comportamiento futuras.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "259",
                        "nombre": "Interpolación con múltiples parámetros",
                        "texto": "SYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\nQUEUER {NAME:TRAMO1,X:66,Y:350,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\nQUEUER {NAME:TRAMO2,X:276,Y:554,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\nQUEUER {NAME:TRAMO3,X:664,Y:360,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\n\r\nPOSITION {NAME:POS1,X:115,Y:92}\r\nPOSITION {NAME:POS2,X:146,Y:449}\r\nPOSITION {NAME:POS3,X:432,Y:506}\r\nPOSITION {NAME:POS4,X:756,Y:33}\r\n\r\n\r\nFunction {behavior: tramo2D, type:GAUSS, p0:50, p1:0, Name:gName1, a:1, b:40, Sigma1:3.0, Sigma2:3.0}\r\nFunction {behavior: tramo2D, type:GAUSS, p0:50, p1:1, Name:gName2, a:1, b:60, Sigma1:5.0, Sigma2:5.0}\r\nFunction {behavior: tramo2D, type:GAUSS, p0:100, p1:0, Name:gName3, a:1, b:80, Sigma1:5.0, Sigma2:5.0}\r\nFunction {behavior: tramo2D, type:GAUSS, p0:100, p1:1, Name:gName4, a:1, b:120, Sigma1:10.0, Sigma2:10.0}\r\n\r\nSTART 2000\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,0 {NAME:GEN1,X:55,Y:41}\r\n\r\nADVANCE 30,10 {TO:POS1}\r\n\r\n\r\nqueue TRAMO1\r\nADVANCE BF$(tramo2D,60,0.5) {TO:POS2}\r\ndepart TRAMO1\r\n\r\nqueue TRAMO2\r\nADVANCE BF$(tramo2D,55,0.5) {TO:POS3}\r\ndepart TRAMO2\r\n\r\nqueue TRAMO3\r\nADVANCE BF$(tramo2D,90,0.5) {TO:POS4}\r\ndepart TRAMO3\r\n\r\n\r\n\r\nTERMINATE 1\r\n\r\n\r\n",
                        "descripcion": "<p>Damos un paso m&aacute;s: usamos dos par&aacute;metros de entrada para modelar situaciones m&aacute;s complejas (Longitud y Meteorolog&iacute;a).<\/p>\r\n<p>La interpolaci&oacute;n se realiza mediante t&eacute;cnicas <b>IDW (Inverse Distance Weighting)<\/b>. Esto permite que, dadas combinaciones intermedias de longitud y clima, GPSS-Plus genere de forma autom&aacute;tica una funci&oacute;n de tiempo adaptada.<\/p>\r\n<p>Este ejemplo muestra c&oacute;mo construir sistemas de simulaci&oacute;n vers&aacute;tiles y realistas con muy poca informaci&oacute;n de base, simplemente interpolando el espacio entre casos conocidos.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    },
                    {
                        "id": "256",
                        "nombre": "Los Behavior Procedures",
                        "texto": ";SYSTEM {TYPE:OPTIONS,Speed:5}\r\n\r\ninitial tramos, [\r\n\t{\r\n\torigen:1,\r\n\tdestino:2,\r\n    Km:90,\r\n    Meteo:0.6\r\n    },\r\n\t{\r\n\torigen:2,\r\n\tdestino:3,\r\n    Km:100,\r\n    Meteo:0.8\r\n    },\r\n\t{\r\n\torigen:3,\r\n\tdestino:4,\r\n    Km:50,\r\n    Meteo:1.0\r\n    }\r\n    ]\r\n\r\nGraphic {NAME:Text_0,Type:TEXT,X:219,Y:178,Text:\"Text_0\"}\r\nGraphic {NAME:Text_1,Type:TEXT,X:246,Y:454,Text:\"Text_1\"}\r\nGraphic {NAME:Text_2,Type:TEXT,X:429,Y:354,Text:\"Text_2\"}\r\n\r\n\r\nQUEUER {NAME:TRAMO_0,X:66,Y:350,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\nQUEUER {NAME:TRAMO_1,X:276,Y:554,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\nQUEUER {NAME:TRAMO_2,X:664,Y:360,E_BIN_start:0,E_BIN_SIZE:1,E_BIN_COUNT:180}\r\n\r\nFacility {NAME:Gas_0,X:215,Y:125,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}\r\nFacility {NAME:Gas_1,X:245,Y:406,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}\r\nFacility {NAME:Gas_2,X:434,Y:405,capacity:10,R_BIN_start:0,R_BIN_SIZE:1,R_BIN_COUNT:10}\r\n\r\n\r\nPOSITION {NAME:POS_0,X:115,Y:92}\r\nPOSITION {NAME:POS_1,X:146,Y:449}\r\nPOSITION {NAME:POS_2,X:432,Y:506}\r\nPOSITION {NAME:POS_3,X:756,Y:33}\r\n\r\n\r\nFunction {behavior: bTramoTiempo, type:GAUSS, p0:50, p1:0, Name:gName1, a:1, b:40, Sigma1:3.0, Sigma2:3.0}\r\nFunction {behavior: bTramoTiempo, type:GAUSS, p0:50, p1:1, Name:gName2, a:1, b:60, Sigma1:5.0, Sigma2:5.0}\r\nFunction {behavior: bTramoTiempo, type:GAUSS, p0:100, p1:0, Name:gName3, a:1, b:80, Sigma1:5.0, Sigma2:5.0}\r\nFunction {behavior: bTramoTiempo, type:GAUSS, p0:100, p1:1, Name:gName4, a:1, b:120, Sigma1:10.0, Sigma2:10.0}\r\n\r\nFunction {behavior: bTramoGas, type:POISSON, p0:100, Name:fPoisson1, LAMBDA:1\/8}\r\nFunction {behavior: bTramoGas, type:POISSON, p0:50, Name:fPoisson2, LAMBDA:1\/20}\r\n\r\n\r\nSTART 2000\r\n\r\n\r\n;*****************************************************\r\nPROCEDURE TRAMO_BEHAVIOR\r\nassign nQueuer, \"TRAMO_P$PARAM_A\"\r\nassign nText, \"Text_P$PARAM_A\"\r\nassign nGas, \"Gas_P$PARAM_A\"\r\n\r\nassign Km,X$(tramos.P$PARAM_A.Km)\r\nassign Meteo,X$(tramos.P$PARAM_A.Meteo)\r\n\r\nassign forceGas,BF$(bTramoGas,P$Km) \r\nif (P$forceGas>=1)\r\n\tmove {name:P$nText,text:\"GAS P$Km \/ P$Meteo\"}\r\n\tADVANCE 10 {TO:P$nGas}\r\n    seize P$nGas\r\n    ADVANCE 30,0\r\n    release P$nGas\r\n    ADVANCE 10 {TO:\"POS_P$PARAM_A\"}\r\n\tmove {name:P$nText,text:\" \"}\r\nendif\r\n\r\n\r\nqueue P$nQueuer\r\nADVANCE BF$(bTramoTiempo,P$Km,P$Meteo) {TO:\"POS_P$PARAM_B\"}\r\ndepart P$nQueuer\r\n\r\nENDPROCEDURE 1\r\n\r\n;*****************************************************\r\nGENERATE 2,0,0,0 {NAME:GEN1,X:55,Y:41,enumber:0}\r\n\r\nADVANCE 10,2 {TO:POS_0}\r\n\r\nCALL TRAMO_BEHAVIOR,0,1\r\nCALL TRAMO_BEHAVIOR,1,2\r\nCALL TRAMO_BEHAVIOR,2,3\r\n\r\nTERMINATE 1\r\n\r\n\r\n",
                        "descripcion": "<p>Las funciones de comportamiento definen tanto el comportamiento hacia el interior de la entidad como hacia el exterior.<\/p>\r\n<p>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&aacute;quinas. En general, &eacute;stas ser&aacute;n una funci&oacute;n de Poisson.<\/p>\r\n<p>As&iacute; que lo f&aacute;cil para interpretar tanto las de entrada como las de salida es sintetizar todas ellas en un &uacute;nico BEHAVIOR PROCEDURE.<\/p>\r\n<p>Vamos a observar un ejemplo de tramos de carretera en los que los veh&iacute;culos tardar&aacute;n un determinado tiempo en recorrerlo y eventualmente tendr&aacute;n que ir a repostar.  Usaremos el <code>SAVEVALUE<\/code>&nbsp;&quot;tramos&quot; para sintetizar los par&aacute;metros de cada tramo del 0 al 2. Designaremos un n&uacute;mero para el origen, destino, la distancia y la meteorolog&iacute;a de cada tramo.<\/p>\r\n<p>Adem&aacute;s de definir las BEHAVIOR FUNCTIONS tanto Gaussiana para el tiempo de trayecto dependiendo de la distancia y meteorolog&iacute;a, a&ntilde;adimos una Poisson para definir el lambda respecto de la distancia que haga que las entidades entren a repostar. Recordemos que en una distribuci&oacute;n de Poisson, un lambda de 1\/10 indica que regularmente, uno de cada 10 veh&iacute;culos repostar&aacute;n.<\/p>\r\n<p>El BEHAVIOR PROCEDURE se encarga de todo y solo recibe dos par&aacute;metros, origen y destino.<\/p>\r\n<p>Obtiene los nombres de los recursos y par&aacute;metros basados en esos n&uacute;meros. Calcula si es necesario repostar y cu&aacute;nto tiempo va a tardar en recorrer el tramo.  Las estad&iacute;sticas finales permiten observar el tiempo total de recorrido por tramo (acumulado en los QUEUERs), as&iacute; como el uso y ocupaci&oacute;n de las gasolineras (v&iacute;a R_BIN_*).<\/p>\r\n<p>Gracias a la flexibilidad de los Behavior Procedures, podemos encapsular tanto la l&oacute;gica de simulaci&oacute;n como las variaciones estad&iacute;sticas y los eventos adicionales en un &uacute;nico bloque modular.<\/p>\r\n<p>Esto permite replicar f&aacute;cilmente comportamientos realistas en sistemas m&aacute;s grandes como cadenas log&iacute;sticas, rutas de transporte o procesos industriales con condiciones variables.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "408",
                "nombre": "Exploración de configuraciones optimas",
                "texto": "",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "409",
                        "nombre": "TRIAL_STATE y RESTART",
                        "texto": "; *******************************************************************\r\n; EXPERIMENT: Find minimum capacity without queue overflow\r\n; We increase capacity until the queue never exceeds 2\r\n; *******************************************************************\r\n\r\nSYSTEM {TYPE:OPTIONS,TIME_DECIMALS:0,SPEED:7}; \r\nSYSTEM {TYPE:PRE_RUN,TRIGGER:PRE_RUN}\r\n\r\n\r\nINITIAL capacity1,( (X$(TRIAL_STATE.capacity1.EXIST)==1) ? X$(TRIAL_STATE.capacity1) : 1)\r\nINITIAL capacity2,( (X$(TRIAL_STATE.capacity2.EXIST)==1) ? X$(TRIAL_STATE.capacity2) : 1)\r\nINITIAL capacity3,( (X$(TRIAL_STATE.capacity3.EXIST)==1) ? X$(TRIAL_STATE.capacity3) : 1)\r\n\r\nFACILITY {name:Fac1, CAPACITY:X$capacity1, X:255,Y:431, on_queue:check_queue1}\r\nFACILITY {name:Fac2, CAPACITY:X$capacity2, X:412,Y:318, on_queue:check_queue2}\r\nFACILITY {name:Fac3, CAPACITY:X$capacity3, X:605,Y:430, on_queue:check_queue3}\r\n\r\n\r\nPOSITION {NAME:POSEND,X:727,Y:352}\r\n\r\nFUNCTION {NAME:funFac1,TYPE:TRIANGULAR,MIN:25,MAX:60,MODE:45,INTERVALS:500} \r\nFUNCTION {NAME:funFac2,TYPE:TRIANGULAR,MIN:25,MAX:260,MODE:45,INTERVALS:500} \r\nFUNCTION {NAME:funFac3,TYPE:TRIANGULAR,MIN:25,MAX:160,MODE:45,INTERVALS:500} \r\n\r\nGRAPHIC {name:Tprocess,type:TEXT,X:403,Y:190,\r\n         text:\"Trial X$(TRIAL_STATE.count)\\n Capacity1 X$(capacity1) \\nCapacity2 X$(capacity2) \\nCapacity3 X$(capacity3) \\n\"}\r\n\r\nSTART 100\r\n\r\n\r\nPROCEDURE PRE_RUN\r\n    IF (X$(TRIAL_STATE.count,EXIST)==0)\r\n        SAVEVALUE TRIAL_STATE,{\r\n            count:0,\r\n            capacity1:1,\r\n            capacity2:1,\r\n            capacity3:1\r\n            }\r\n    ENDIF\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n;----------------------------------------------------------\r\n\r\nGENERATE 9,0 {NAME:GEN1,X:82,Y:356}\r\n    \r\n    ASSIGN Estado, 0 \r\n\r\n    ADVANCE 10,0 {TO:Fac1,flow:1}\r\n    SEIZE Fac1\r\n        ADVANCE FN$funFac1\r\n    RELEASE Fac1\r\n\r\n    ADVANCE 10,0 {TO:Fac2,flow:1}\r\n    SEIZE Fac2\r\n        ADVANCE FN$funFac2\r\n    RELEASE Fac2\r\n\r\n    ADVANCE 10,0 {TO:Fac3,flow:1}\r\n    SEIZE Fac3\r\n        ADVANCE FN$funFac3\r\n    RELEASE Fac3\r\n\r\n    ADVANCE 10,0 {TO:POSEND,flow:1}\r\n\r\nENDGENERATE 1\r\n\r\n\r\n;----------------------------------------------------------\r\n; Queue control: if queue exceeds limit → fail trial\r\n;----------------------------------------------------------\r\nPROCEDURE check_queue1\r\n    IF (R$(Fac1,QUEUE) > 2)\r\n        SAVEVALUE TRIAL_STATE.capacity1 , X$(TRIAL_STATE.capacity1) + 1\r\n        CALL next_trial\r\n    ENDIF\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nPROCEDURE check_queue2\r\n    IF (R$(Fac2,QUEUE) > 2)\r\n        SAVEVALUE TRIAL_STATE.capacity2 , X$(TRIAL_STATE.capacity2) + 1\r\n        CALL next_trial\r\n    ENDIF\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\nPROCEDURE check_queue3\r\n    IF (R$(Fac3,QUEUE) > 2)\r\n        SAVEVALUE TRIAL_STATE.capacity3 , X$(TRIAL_STATE.capacity3) + 1\r\n        CALL next_trial\r\n    ENDIF\r\n    TERMINATE_VE\r\nENDPROCEDURE\r\n\r\n\r\n;----------------------------------------------------------\r\n; Experiment control: increase capacity and retry\r\n;----------------------------------------------------------\r\nPROCEDURE next_trial \r\n\r\n    SAVEVALUE TRIAL_STATE.count , X$(TRIAL_STATE.count) + 1\r\n\r\n    IF (X$(TRIAL_STATE.count) < 30)\r\n        RESTART\r\n    ELSE\r\n        STOP\r\n    ENDIF\r\n\r\n    TERMINATE_VE\r\nENDPROCEDURE",
                        "descripcion": "<p data-start=\"287\" data-end=\"375\">Hay veces que queremos probar diferentes configuraciones para encontrar la m&aacute;s adecuada.<\/p>\r\n<p data-start=\"377\" data-end=\"487\">Por ejemplo, queremos saber cu&aacute;l es el n&uacute;mero m&iacute;nimo de operarios necesario para sacar adelante la producci&oacute;n.<\/p>\r\n<p data-start=\"489\" data-end=\"625\">Esta b&uacute;squeda puede realizarse de forma secuencial o mediante estrategias m&aacute;s avanzadas, dependiendo del n&uacute;mero de variables implicadas.<\/p>\r\n<p data-start=\"627\" data-end=\"774\">Con independencia del algoritmo utilizado, en GPSS-Plus este tipo de experimentos se apoyan en un <code data-start=\"725\" data-end=\"736\">SAVEVALUE<\/code> persistente llamado&nbsp;<code data-start=\"832\" data-end=\"845\">TRIAL_STATE<\/code>&nbsp;y en el bloque <code data-start=\"764\" data-end=\"773\">RESTART<\/code>.<\/p>\r\n<p data-start=\"776\" data-end=\"927\">En este contexto,&nbsp;<code data-start=\"832\" data-end=\"845\">TRIAL_STATE<\/code> tiene la propiedad de mantener su valor entre ejecuciones incluso cuando se produce un <code data-start=\"917\" data-end=\"926\">RESTART<\/code>.<\/p>\r\n<p data-start=\"929\" data-end=\"1049\">Esto permite que el modelo conserve informaci&oacute;n entre intentos y pueda modificar sus par&aacute;metros en cada nueva ejecuci&oacute;n.<\/p>\r\n<p data-start=\"1056\" data-end=\"1182\">&nbsp;<\/p>\r\n<p data-start=\"1056\" data-end=\"1182\">En el siguiente ejemplo, queremos determinar la capacidad m&iacute;nima de varias&nbsp;<code data-start=\"1128\" data-end=\"1138\">FACILITY<\/code> tal que la cola nunca supere dos entidades.<\/p>\r\n<p data-start=\"1184\" data-end=\"1269\">Como en la primera ejecuci&oacute;n <code data-start=\"1213\" data-end=\"1226\">TRIAL_STATE<\/code> no existe, inicializamos la capacidad a 1:<\/p>\r\n<pre>\r\nINITIAL capacity,( (X$(TRIAL_STATE.capacity.EXIST)==1) ? X$(TRIAL_STATE.capacity) : 1)<\/pre>\r\n<p>Durante la simulaci&oacute;n, monitorizamos el tama&ntilde;o de la cola.<br data-start=\"1429\" data-end=\"1432\">\r\nSi en alg&uacute;n momento se supera el l&iacute;mite establecido, consideramos el trial inv&aacute;lido y lanzamos un nuevo intento con mayor capacidad:<\/p>\r\n<pre>\r\nIF (R$(Fac1,QUEUE) &gt; 2)\r\n    SAVEVALUE TRIAL_STATE.capacity1 , X$(TRIAL_STATE.capacity1) + 1\r\n    CALL next_trial\r\nENDIF<\/pre>\r\n<p>Si la simulaci&oacute;n alcanza las 100 entidades sin superar el l&iacute;mite, la capacidad actual se considera v&aacute;lida.<\/p>",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": []
                    }
                ]
            },
            {
                "id": "430",
                "nombre": "Modelado Cinemático",
                "texto": "<p class=\"isSelectedEnd\"><span>Los sistemas de movimiento en simulaci&oacute;n de eventos discretos (DES) suelen clasificarse por dominio industrial:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>conveyors,<\/span><\/li>\r\n    <li><span>tr&aacute;fico vial,<\/span><\/li>\r\n    <li><span>ferrocarriles,<\/span><\/li>\r\n    <li><span>AGVs,<\/span><\/li>\r\n    <li><span>robots,<\/span><\/li>\r\n    <li><span>etc.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Sin embargo, desde el punto de vista computacional, muchos de estos sistemas comparten estructuras internas similares.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>La diferencia real no reside en el objeto f&iacute;sico que se mueve, sino en d&oacute;nde reside la causalidad del movimiento.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Es decir:<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>&iquest;Qui&eacute;n posee realmente el r&eacute;gimen cinem&aacute;tico del sistema?<\/span><\/p>\r\n<p><span>A partir de esta idea, los modelos cinem&aacute;ticos implementados pueden separarse en tres grandes categor&iacute;as fundamentales.<\/span><\/p>\r\n<h3><span>1. Cinem&aacute;tica impuesta por el recurso (Conveyor)<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En los sistemas Conveyor (cinta transportadora), el movimiento pertenece a la infraestructura.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>La cinta:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>impone la velocidad,<\/span><\/li>\r\n    <li><span>define la direcci&oacute;n,<\/span><\/li>\r\n    <li><span>y gobierna completamente el r&eacute;gimen de movimiento.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Las entidades transportadas no poseen cinem&aacute;tica propia.<\/span><br>\r\n<span>Simplemente heredan el estado din&aacute;mico del recurso.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>La causalidad del sistema es geom&eacute;trica:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>el espacio disponible,<\/span><\/li>\r\n    <li><span>los l&iacute;mites,<\/span><\/li>\r\n    <li><span>y las restricciones f&iacute;sicas,<\/span><br>\r\n    <span>son quienes determinan cu&aacute;ndo un estado deja de ser v&aacute;lido.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Por ello, el algoritmo no simula movimiento continuo:<\/span><br>\r\n<span>simula invalidaciones geom&eacute;tricas futuras.<\/span><\/p>\r\n<h3><span>2. Cinem&aacute;tica impuesta por la entidad (Road)<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En los sistemas Road (Carretera), la cinem&aacute;tica deja de pertenecer a la infraestructura y pasa a residir en cada entidad.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Cada veh&iacute;culo posee:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>velocidad,<\/span><\/li>\r\n    <li><span>aceleraci&oacute;n,<\/span><\/li>\r\n    <li><span>r&eacute;gimen de movimiento,<\/span><\/li>\r\n    <li><span>y predicciones temporales propias.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>La carretera ya no mueve entidades.<\/span><br>\r\n<span>&Uacute;nicamente define:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>conectividad,<\/span><\/li>\r\n    <li><span>topolog&iacute;a,<\/span><\/li>\r\n    <li><span>y restricciones espaciales.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>La causalidad deja de ser puramente geom&eacute;trica y pasa a ser relacional.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Los eventos emergen de la interacci&oacute;n cinem&aacute;tica entre entidades:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>aproximaciones,<\/span><\/li>\r\n    <li><span>alcance de distancias de seguridad,<\/span><\/li>\r\n    <li><span>cambios de r&eacute;gimen,<\/span><\/li>\r\n    <li><span>o invalidaciones de predicciones futuras.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>El sistema no integra trayectorias paso a paso.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Calcula cu&aacute;ndo un r&eacute;gimen cinem&aacute;tico dejar&aacute; de ser v&aacute;lido.<\/span><\/p>\r\n<h3><span>3. Cinem&aacute;tica por impuesta por Autoridad Exterior (Railway)<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En los sistemas ferroviarios cl&aacute;sicos, la cinem&aacute;tica local del tren existe, pero queda subordinada a una autoridad externa de ocupaci&oacute;n.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>La infraestructura no mueve el tren.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>La infraestructura autoriza el movimiento.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>El sistema se basa en:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>permisos,<\/span><\/li>\r\n    <li><span>reservas futuras,<\/span><\/li>\r\n    <li><span>telemetr&iacute;a as&iacute;ncrona,<\/span><\/li>\r\n    <li><span>y exclusi&oacute;n mutua distribuida.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>La causalidad principal ya no es geom&eacute;trica ni relacional:<\/span><br>\r\n<span>es contractual.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>El estado v&aacute;lido del sistema depende de la validez temporal de las autorizaciones de circulaci&oacute;n.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>El motor no simula conducci&oacute;n continua.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Simula cambios de validez sobre permisos de ocupaci&oacute;n.<\/span><\/p>\r\n<h3><span>Sistemas h&iacute;bridos<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>Muchos sistemas modernos no pertenecen de forma pura a una sola categor&iacute;a.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Por ejemplo:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>trenes CBTC,<\/span><\/li>\r\n    <li><span>ETCS Moving Block,<\/span><\/li>\r\n    <li><span>AGVs,<\/span><\/li>\r\n    <li><span>robots aut&oacute;nomos,<\/span><\/li>\r\n    <li><span>tr&aacute;fico a&eacute;reo,<\/span><\/li>\r\n    <li><span>o flotas AMR,<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>combinan simult&aacute;neamente:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>cinem&aacute;tica local,<\/span><\/li>\r\n    <li><span>restricciones geom&eacute;tricas,<\/span><\/li>\r\n    <li><span>e infraestructuras de autorizaci&oacute;n distribuida.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Estos sistemas h&iacute;bridos emergen naturalmente como combinaciones de los tres modelos fundamentales anteriores.<\/span><\/p>\r\n<h3><span>Principio general de los modelos DES<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En todos los casos se evita resolver el movimiento mediante:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>integraci&oacute;n continua,<\/span><\/li>\r\n    <li><span>polling,<\/span><\/li>\r\n    <li><span>stepping,<\/span><\/li>\r\n    <li><span>discretizaci&oacute;n espacial,<\/span><\/li>\r\n    <li><span>o rec&aacute;lculo permanente.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>La filosof&iacute;a del motor DES es distinta:<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>El sistema no procesa movimiento continuo.<\/span><\/p>\r\n<p><b><span>Procesa &uacute;nicamente los eventos que invalidan el estado anterior del sistema.<\/span><\/b><\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                "descripcion": null,
                "parametros": null,
                "parametros_json": null,
                "ejemplo": null,
                "hijos": [
                    {
                        "id": "410",
                        "nombre": "La cinta transportadora",
                        "texto": "",
                        "descripcion": "",
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": [
                            {
                                "id": "411",
                                "nombre": "El Conveyor: El alma de la logística en DES",
                                "texto": "",
                                "descripcion": "<p>Las librer&iacute;as de manejo de materiales en motores de eventos discretos tienen un elemento nativo fundamental del que parten todos los dem&aacute;s componentes:<\/p>\r\n<h3><b data-path-to-node=\"5\" data-index-in-node=\"152\">La cinta transportadora (Conveyor)<\/b>.<\/h3>\r\n<p>Seg&uacute;n el algoritmo utilizado para resolver su movimiento, los motores de simulaci&oacute;n se clasifican hist&oacute;ricamente en tres grandes enfoques:<\/p>\r\n<ul>\r\n    <li><b>Enfoque de &quot;Sistema de Celdas&quot; (Cell-Based):<\/b> Reduce el problema a un conjunto de celdas (ocupadas\/libres). El estado de la cinta se actualiza transitando el contenido de celda en celda, lo que genera una carga computacional proporcional a la resoluci&oacute;n espacial de la cinta.<\/li>\r\n    <li><b data-path-to-node=\"6,1,0\" data-index-in-node=\"0\">Enfoque de &quot;Acumulaci&oacute;n&quot; (Basado en Agentes):<\/b> Cada entidad es consciente de sus antecesores y est&aacute; atenta a los cambios en su cinem&aacute;tica. Requiere que cada caja recalcule su estado constantemente ante interrupciones, consumiendo recursos de CPU por cada objeto en la cinta.<\/li>\r\n    <li><b data-path-to-node=\"6,2,0\" data-index-in-node=\"0\">Algoritmo de &quot;Evento Pr&oacute;ximo&quot; (Scan-Line\/Next Event):<\/b> Se programan c&oacute;mputos a futuro para cambiar la cinem&aacute;tica. Es m&aacute;s eficiente, pero tradicionalmente r&iacute;gido ante paradas (Stop) o cambios de velocidad inesperados.<br>\r\n    <\/li>\r\n<\/ul>\r\n<p data-path-to-node=\"8\">En <b data-path-to-node=\"8\" data-index-in-node=\"3\">GPSS-Plus<\/b>, aprovechamos la potencia declarativa de las <b data-path-to-node=\"8\" data-index-in-node=\"58\">Entidades Virtuales (VE)<\/b> y los bloques de control temporal como <code data-path-to-node=\"8\" data-index-in-node=\"122\">TIMEOUT<\/code>. Esto nos permite llevar la resoluci&oacute;n del algoritmo a una <b data-path-to-node=\"8\" data-index-in-node=\"198\">abstracci&oacute;n pura de eventos<\/b>, logrando una mejora disruptiva sobre el m&eacute;todo de &quot;Evento Futuro&quot; tradicional.<\/p>\r\n<p data-path-to-node=\"9\">La arquitectura de <b data-path-to-node=\"9\" data-index-in-node=\"24\">Fronteras Mutables<\/b> logra una mejora disruptiva sobre el m&eacute;todo de &quot;Evento Futuro&quot; tradicional al desacoplar por completo la visualizaci&oacute;n de la l&oacute;gica.<\/p>\r\n<blockquote data-path-to-node=\"10\">\r\n<p data-path-to-node=\"10,0\"><b data-path-to-node=\"10,0\" data-index-in-node=\"0\">El Principio Maestro:<\/b> Reducir la computaci&oacute;n al m&iacute;nimo imprescindible. El motor no procesa &quot;el movimiento&quot;; procesa <b data-path-to-node=\"10,0\" data-index-in-node=\"116\">&quot;el cambio&quot;<\/b>.<\/p>\r\n<\/blockquote>\r\n<p data-path-to-node=\"9\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "412",
                                "nombre": "Definición del Problema",
                                "texto": "",
                                "descripcion": "<p data-path-to-node=\"4\">Una cinta transportadora es un recurso que mueve material entre dos puntos y que definimos con estas premisas:<\/p>\r\n<ul>\r\n    <li><b data-path-to-node=\"5,0,0\" data-index-in-node=\"0\">Gesti&oacute;n de Entrada:<\/b> Un &quot;embudo&quot; previo acumula las cajas para que ingresen de una en una.&nbsp;Es simplemente una cola FIFO que regula el acceso al punto de carga.<\/li>\r\n    <li><b data-path-to-node=\"5,1,0\" data-index-in-node=\"0\">Dimensiones:<\/b> La cinta tiene una <b data-path-to-node=\"5,1,0\" data-index-in-node=\"32\">longitud<\/b> y una <b data-path-to-node=\"5,1,0\" data-index-in-node=\"47\">velocidad<\/b> definidas.<\/li>\r\n    <li><b data-path-to-node=\"5,2,0\" data-index-in-node=\"0\">Ocupaci&oacute;n F&iacute;sica:<\/b> Las cajas entran y salen tomando como referencia su <b data-path-to-node=\"5,2,0\" data-index-in-node=\"70\">centro<\/b>, pero ocupan su <b data-path-to-node=\"5,2,0\" data-index-in-node=\"93\">largo total<\/b> en todo momento.<\/li>\r\n    <li><b data-path-to-node=\"5,3,0\" data-index-in-node=\"0\">Atasco Din&aacute;mico:<\/b> Una caja puede detenerse en cualquier punto del recorrido (desde el inicio hasta que el &uacute;ltimo mil&iacute;metro despeja la cinta). Si hay una parada, las dem&aacute;s cajas seguir&aacute;n hasta toparse con la detenida.<\/li>\r\n    <li>No se permite la entrada de una caja si no hay, al menos, la mitad de su largo libre en el punto de carga.<\/li>\r\n<\/ul>\r\n<p>Nuestra labor ser&aacute; crear un recurso que admita la llegada de entidades (cajas de diferente tama&ntilde;o), pasen al embudo, de &eacute;l a la cinta y la abandonen con los tiempos, velocidad y atascos que se puedan producir.<\/p>\r\n<p>El desaf&iacute;o es modelar este comportamiento sin recurrir a celdas ni a rec&aacute;lculos continuos, sino mediante eventos discretos que representen &uacute;nicamente los cambios relevantes.<\/p>",
                                "parametros": "",
                                "parametros_json": "",
                                "ejemplo": "",
                                "hijos": []
                            },
                            {
                                "id": "413",
                                "nombre": "Arquitectura de la Solución: El Modelo Desacoplado",
                                "texto": "",
                                "descripcion": "<p>Para lograr la soluci&oacute;n, dividimos el recurso Conveyor en dos capas con responsabilidades radicalmente distintas. Este enfoque simplifica enormemente el problema.<\/p>\r\n<ul>\r\n    <li>La capa visual<\/li>\r\n    <li>La capa l&oacute;gica<\/li>\r\n<\/ul>\r\n<p>Aunque no lo parezca, toda la soluci&oacute;n nos va a venir de olvidarlos completamente de la capa visual: El renderizado.<\/p>\r\n<h3>La capa visual:<\/h3>\r\n<p>Cada caja tiene su longitud, si est&aacute; movi&eacute;ndose o no y la &uacute;ltima [posici&oacute;n,tiempo] conocida.<br>\r\nNada m&aacute;s.<br>\r\nEl renderizado en cada instante hace el resto.<\/p>\r\n<ul>\r\n    <li>Si la caja est&aacute; parada: Se dibuja en la posici&oacute;n guardada.<\/li>\r\n    <li>Si est&aacute; movi&eacute;ndose: Posici&oacute;n_Visual = Posici&oacute;n + Velocidad * (AC1 - Tiempo).<\/li>\r\n<\/ul>\r\n<p>Se trata por tanto de emitir un estado vectorial actual perfecto de todas las cajas y el render hace lo que sea necesario para mostrarlo en ese instante.<br>\r\n<br>\r\nSi nos ce&ntilde;imos a estas necesidades de renderizado, la l&oacute;gica se simplifica en su filosof&iacute;a.<\/p>\r\n<h3>La capa l&oacute;gica:<\/h3>\r\n<p data-path-to-node=\"9\">La l&oacute;gica se despoja de la carga de simular el movimiento continuo. Se reduce exclusivamente a gestionar <b data-path-to-node=\"9\" data-index-in-node=\"105\">qui&eacute;n se mueve y qui&eacute;n no<\/b>, y en qu&eacute; momento exacto cambia ese estado.<\/p>\r\n<ul>\r\n    <li><b data-path-to-node=\"10,0,0\" data-index-in-node=\"0\">Inexistencia de integraci&oacute;n:<\/b> El sistema no calcula la posici&oacute;n paso a paso. Calcula el vector de la posici&oacute;n.<\/li>\r\n    <li><b data-path-to-node=\"10,1,0\" data-index-in-node=\"0\">Eventos:<\/b> En este algoritmo de <b data-path-to-node=\"10,1,0\" data-index-in-node=\"43\">Evento Pr&oacute;ximo<\/b>, una caja no &quot;avanza hasta encontrar&quot; una restricci&oacute;n. En su lugar, el sistema calcula en qu&eacute; momento futuro matem&aacute;ticamente viable donde&nbsp;<b>podr&iacute;a <\/b>aparecer una restricci&oacute;n (como el choque con la caja anterior o el fin de la cinta).<\/li>\r\n    <li><b data-path-to-node=\"10,2,0\" data-index-in-node=\"0\">El Cambio de Perspectiva:<\/b> No es la caja la que busca el obst&aacute;culo; es la restricci&oacute;n la que &quot;encuentra&quot; a la caja en el tiempo.<\/li>\r\n<\/ul>\r\n<p>Dicho de otra forma:<br>\r\nel tiempo determina cu&aacute;ndo un estado deja de ser v&aacute;lido.<br>\r\nLa posici&oacute;n no se busca ni se integra paso a paso.<\/p>\r\n<p>El desaf&iacute;o es modelar este comportamiento sin recurrir a celdas ni a rec&aacute;lculos continuos, sino mediante eventos discretos que representen &uacute;nicamente los cambios relevantes. Para ello construiremos una topolog&iacute;a causal expl&iacute;cita entre las entidades presentes en la cinta, permitiendo resolver todas las decisiones de control sin b&uacute;squedas sobre el conjunto de cajas.<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "414",
                                "nombre": "La capa visual",
                                "texto": "",
                                "descripcion": "<p data-path-to-node=\"4\">Aunque GPSS-Plus permite crear animaciones mediante agentes y gr&aacute;ficos nativos, el algoritmo para el Conveyor delega la responsabilidad visual en un recurso externo de tipo <b data-path-to-node=\"4\" data-index-in-node=\"193\">MEDIA<\/b>.<\/p>\r\n<p data-path-to-node=\"5\">De esta forma, la l&oacute;gica DES solo se preocupa de emitir estados vectoriales, permitiendo que el motor de simulaci&oacute;n corra mientras el renderizado se gestiona de forma independiente.<\/p>\r\n<p>&nbsp;<\/p>\r\n<pre>\r\nLOAD_MEDIA https:\/\/gpss-plus.com\/imports_js\/Conveyor_medio.js\r\nINSTANCE_MEDIA {name:conveyor, class:Conveyor}<\/pre>\r\n<p>De esta forma se expresa mejor la idea de &quot;fire &amp; forget&quot; despreocup&aacute;ndonos completamente de la visual.<\/p>\r\n<p>Este script inyectado en GPSS-Plus recibe los datos vectoriales de las cajas y se encarga de todo.<\/p>\r\n<p>Por un lado tendremos este <code>PROCEDURE <\/code>en el c&oacute;digo que lanzar&aacute; los datos al MEDIA&nbsp;&uacute;nicamente cuando hay un cambio relevante en la l&oacute;gica (un nuevo evento), cumpliendo la filosof&iacute;a de ahorro de computaci&oacute;n. El estado enviado es completamente declarativo: la l&oacute;gica no informa de movimientos, sino de estados cinem&aacute;ticos y relaciones causales:<\/p>\r\n<pre>\r\nPROCEDURE setStatusDisplay\r\n    assign stateData,[]\r\n    FOREACH entityNumber,IN_RESOURCE,RR_Conveyor\r\n        assign.push stateData,{\r\n                    id: P$entityNumber,\r\n                    position: P$(RR_Position,P$entityNumber),\r\n                    time:   P$(RR_Time,P$entityNumber),\r\n                    length: P$(RR_Length,P$entityNumber),\r\n                    height: P$(RR_Height,P$entityNumber),\r\n                    width:  P$(RR_Width,P$entityNumber),\r\n                    moving: P$(RR_Moving,P$entityNumber),\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color: &quot;P$(RR_Color,P$entityNumber)&quot;\r\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }\r\n&nbsp; &nbsp; ENDFOREACH\r\n    ASSIGN msg,{operation:&quot;set&quot;,state:V$stateData}\r\n    EXEC_MEDIA {name:conveyor,in:V$msg}\r\nENDPROCEDURE<\/pre>\r\n<p>Por otro lado, el script inyectado implementa el m&eacute;todo <code data-path-to-node=\"14\" data-index-in-node=\"41\">onRenderFrame<\/code>, que es invocado autom&aacute;ticamente por el motor de visualizaci&oacute;n en cada ciclo. Internamente, el m&eacute;todo <code data-path-to-node=\"14\" data-index-in-node=\"157\">moveBox<\/code> se encarga de la <b data-path-to-node=\"14\" data-index-in-node=\"182\">interpolaci&oacute;n cinem&aacute;tica<\/b> bas&aacute;ndose en el tiempo actual (<code data-path-to-node=\"14\" data-index-in-node=\"238\">AC1<\/code>)::<\/p>\r\n<pre>\r\n moveBox({ AC1, id, position }) {\r\n\r\n  const obj = this.boxes.get(id);\r\n  if (!obj) return;\r\n\r\n  const { mesh, length, cube } = obj;\r\n\r\n  \/\/ ----------------------------------------\r\n  \/\/ position = CENTRO lengthinal\r\n  \/\/ ----------------------------------------\r\n\r\n  \/\/ distancia del centro sobre la cinta\r\n  const centerDistance = position * this.scale;\r\n\r\n  \/\/ media length real\r\n  const half = length * this.scale * 0.5;\r\n\r\n  \/\/ centro real en coordenadas mundo\r\n  const cx = this.belt_x1 + this.dir_x * centerDistance;\r\n  const cy = this.belt_y1 + this.dir_y * centerDistance;\r\n\r\n  \/\/ cola\r\n  const x1 = cx - this.dir_x * half;\r\n  const y1 = cy - this.dir_y * half;\r\n\r\n  \/\/ cabeza\r\n  const x2 = cx + this.dir_x * half;\r\n  const y2 = cy + this.dir_y * half;\r\n\r\n  \/\/ ----------------------------------------\r\n  \/\/ actualizar l&iacute;nea\r\n  \/\/ ----------------------------------------\r\n\r\n  const positions = mesh.geometry.attributes.position.array;\r\n\r\n  \/\/ cola\r\n  positions[0] = x1;\r\n  positions[1] = y1;\r\n  positions[2] = 0;\r\n\r\n  \/\/ cabeza\r\n  positions[3] = x2;\r\n  positions[4] = y2;\r\n  positions[5] = 0;\r\n\r\n  mesh.geometry.attributes.position.needsUpdate = true;\r\n\r\n  \/\/ ----------------------------------------\r\n  \/\/ actualizar visual derivado\r\n  \/\/ ----------------------------------------\r\n\r\n  cube.onRenderFrame(AC1,cx,cy,0, true,&quot;&quot;);\r\n      \r\n}\r\n<\/pre>\r\n<p><b data-path-to-node=\"16,0\" data-index-in-node=\"0\">Conclusi&oacute;n visual:<\/b> Al ce&ntilde;irnos a estas necesidades de renderizado, la l&oacute;gica se simplifica dr&aacute;sticamente. El sistema no busca colisiones visuales; simplemente emite un estado actual perfecto y el render hace lo necesario para mostrarlo en cada instante.<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "415",
                                "nombre": "La Utillería del Motor: Componentes y Variables",
                                "texto": "",
                                "descripcion": "<p>Para implementar el algoritmo de fronteras mutables, el recurso Conveyor utiliza una infraestructura basada en <b data-path-to-node=\"5\" data-index-in-node=\"111\">Entidades Virtuales (VE)<\/b> y almacenes pasivos (<b data-path-to-node=\"5\" data-index-in-node=\"157\">RESTROOM<\/b>). Toda la l&oacute;gica ocurre mientras las cajas est&aacute;n &quot;dormidas&quot;, despertando solo para transitar entre estados.<\/p>\r\n<h4 data-path-to-node=\"6\">A.&nbsp;Implementaci&oacute;n del Flujo Principal:<\/h4>\r\n<pre>\r\nRESTROOM {NAME:<b>RR_InputBuffer<\/b> ,X:348,Y:467,<b>on_rest:RR_InputBuffer<\/b> ,visible:0}\r\nRESTROOM {NAME:<b>RR_Conveyor<\/b>    ,X:581,Y:470,<b>on_rest:RR_Conveyor<\/b>    ,visible:0}\r\n\r\n\r\nGENERATE ...\r\n    REST <b>RR_InputBuffer<\/b> ; La caja entra al embudo y se duerme hasta que haya sitio\r\n    REST <b>RR_Conveyor<\/b>    ; Una vez despertada, entra a la cinta y se vuelve a dormir hasta salir\r\nTERMINATE 1   <\/pre>\r\n<p>Utilizamos dos recursos de tipo <code data-path-to-node=\"7\" data-index-in-node=\"32\">RESTROOM<\/code> que act&uacute;an como estados l&oacute;gicos que disponen de un hook <code data-path-to-node=\"9\" data-index-in-node=\"26\">ON_REST<\/code> que dispara procedimientos al recibir una entidad.<\/p>\r\n<h4 data-path-to-node=\"6\">B. Variables del recurso<\/h4>\r\n<p><u>F&iacute;sicas del conveyor:<\/u><\/p>\r\n<ul>\r\n    <li>RR_Length: Longitud total de la cinta.<\/li>\r\n    <li>RR_Speed: Velocidad de desplazamiento.<\/li>\r\n    <li>RR_Gap: Espacio que debe dejar RR_InputBuffer entre caja y caja.<\/li>\r\n    <li>RR_decimals: Decimales del tiempo.<\/li>\r\n<\/ul>\r\n<p><u>L&iacute;mites din&aacute;micos (&ldquo;fronteras mutables&rdquo;)<\/u><\/p>\r\n<ul>\r\n    <li>RR_StopLimit: La posici&oacute;n del &quot;tap&oacute;n&quot; o atasco (inicialmente al final: -RR_Length).<\/li>\r\n    <li>RR_EntryLimit: El l&iacute;mite de proximidad para nuevas entradas.<\/li>\r\n<\/ul>\r\n<p><u>Entidades relevantes para la l&oacute;gica<\/u><\/p>\r\n<ul>\r\n    <li>RR_EnteringEntity: Caja en RR_InputBuffer que est&aacute; intentando entrar.<\/li>\r\n    <li>RR_LeavingEntity: Caja en RR_Conveyor que est&aacute; intentando salir.<\/li>\r\n    <li>RR_LastStopedEntity: &Uacute;ltima caja en RR_Conveyor que ha sido detenida.<\/li>\r\n<\/ul>\r\n<p><u>Topolog&iacute;a causal<\/u><\/p>\r\n<ul>\r\n    <li>Para el recurso:<br>\r\n    RR_EntityLeader: Caja m&aacute;s pr&oacute;xima a la salida de la cinta.<br>\r\n    RR_EntityTail: Caja m&aacute;s pr&oacute;xima a la entrada de la cinta.<\/li>\r\n    <li>Cada caja mantiene referencias directas a sus vecinos inmediatos:<br>\r\n    RR_PackFront: Caja situada inmediatamente delante.<br>\r\n    RR_PackRear: Caja situada inmediatamente detr&aacute;s.<\/li>\r\n<\/ul>\r\n<h4 data-path-to-node=\"6\">C. Los orquestadores temporales TimeOut:<\/h4>\r\n<ul>\r\n    <li><b data-path-to-node=\"8,0,0\" data-index-in-node=\"0\">RR_InputBuffer y RR_EntryTimeout<\/b>: Eval&uacute;an la posible entrada.<\/li>\r\n    <li><b>RR_Conveyor y RR_ExitTimeout<\/b>: Eval&uacute;an la posible salida.<\/li>\r\n    <li><b>RR_StopTimeout y RR_ResumeTimeout<\/b>: Eval&uacute;an los atascos y continuidad.<\/li>\r\n<\/ul>\r\n<h4 data-path-to-node=\"6\">D.&nbsp;Versionado de r&eacute;gimen:<\/h4>\r\n<p>Los eventos diferidos pueden quedar obsoletos tras cambios de estado como una parada (Stop) o una reanudaci&oacute;n (Resume).<\/p>\r\n<ul>\r\n    <li>RR_StopRegimeVersion<\/li>\r\n    <li>RR_ExitRegimeVersion<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "416",
                                "nombre": "RR_InputBuffer: El embudo de entrada",
                                "texto": "PROCEDURE RR_InputBuffer\r\n\r\n    if (R$(RR_InputBuffer,IN)>1)\r\n        terminate_ve\r\n    endif\r\n\r\n    ; entity may enter conveyor immediately\r\n    if (X$RR_EntryLimit >= P$(RR_Length,P$ENTITYNUMBER) * 0.5 + X$RR_Gap) \r\n        ; input ownership consumed atomically\r\n        savevalue RR_EntryLimit,0\r\n        ;timeout RR_Admission,0,P$ENTITYNUMBER\r\n        WAKE RR_InputBuffer,-1,P$ENTITYNUMBER\r\n        terminate_ve\r\n    endif\r\n\r\n    timeout RR_EntryTimeout,0,P$ENTITYNUMBER\r\n\r\n    TERMINATE_VE\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p>Cuando una caja llega al sistema, su &uacute;nica acci&oacute;n posible es ingresar en el <b data-path-to-node=\"4\" data-index-in-node=\"76\">Embudo de Entrada<\/b> (<code data-path-to-node=\"4\" data-index-in-node=\"95\">RR_InputBuffer<\/code>). En este instante, se dispara el procedimiento <code data-path-to-node=\"4\" data-index-in-node=\"158\">ON_REST<\/code>, que eval&uacute;a la situaci&oacute;n bajo tres escenarios l&oacute;gicos:<\/p>\r\n<h4 data-path-to-node=\"5\">Escenarios de Admisi&oacute;n<\/h4>\r\n<ol start=\"1\" data-path-to-node=\"6\">\r\n    <li>\r\n    <p data-path-to-node=\"6,0,0\"><b data-path-to-node=\"6,0,0\" data-index-in-node=\"0\">Espera en Cola:<\/b> Si la caja no es la &uacute;nica en el embudo (<code data-path-to-node=\"6,0,0\" data-index-in-node=\"56\">IN &gt; 1<\/code>), se queda dormida. No hace nada m&aacute;s; la caja que tiene delante ser&aacute; la encargada de gestionar el flujo cuando ella misma avance.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"6,1,0\"><b data-path-to-node=\"6,1,0\" data-index-in-node=\"0\">Entrada Inmediata:<\/b> Si hay espacio libre suficiente en la cinta (validado contra <code data-path-to-node=\"6,1,0\" data-index-in-node=\"80\">RR_EntryLimit<\/code>), la caja despierta inmediatamente, consume el espacio de forma at&oacute;mica y transita hacia el siguiente estado (<code data-path-to-node=\"6,1,0\" data-index-in-node=\"204\">RR_Conveyor<\/code>).<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"6,2,0\"><b data-path-to-node=\"6,2,0\" data-index-in-node=\"0\">C&aacute;lculo de Restricci&oacute;n:<\/b> Si no hay espacio suficiente, se lanza la Entidad Virtual <code data-path-to-node=\"6,2,0\" data-index-in-node=\"82\">RR_EntryTimeout<\/code>. Este evento representa la <b data-path-to-node=\"6,2,0\" data-index-in-node=\"125\">pr&oacute;xima reevaluaci&oacute;n conocida<\/b> de la restricci&oacute;n geom&eacute;trica. No busca la entrada, sino el instante exacto en que la restricci&oacute;n podr&iacute;a dejar de ser v&aacute;lida.<br>\r\n    Es decir: &iquest;cuando vuelve a intentar entrar? Dejando ese c&aacute;lculo para el timeout.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p>&nbsp;<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "417",
                                "nombre": "RR_EntryTimeout: Orquestación de Entrada",
                                "texto": "PROCEDURE RR_EntryTimeout\r\n\r\n        \r\n    assign ENTITYNUMBER,P$PARAM_A\r\n\r\n    if (P$ENTITYNUMBER == 0 )\r\n        FOREACH entityNumber,IN_RESOURCE,RR_InputBuffer\r\n            if (P$ENTITYNUMBER == 0 )\r\n                assign ENTITYNUMBER, P$entityNumber\r\n            endif\r\n        ENDFOREACH\r\n    endif\r\n\r\n    if (P$ENTITYNUMBER == 0 )\r\n        terminate_ve\r\n    endif\r\n\r\n\r\n    ; find entity closest to conveyor init\r\n    assign tailEntity,X$RR_EntityTail\r\n\r\n    if (P$tailEntity == 0)\r\n        savevalue RR_EntryLimit,0\r\n        WAKE RR_InputBuffer,-1,P$ENTITYNUMBER\r\n        terminate_ve\r\n    endif\r\n\r\n\r\n    ; calculate current position\r\n    assign advance, P$(RR_Position,P$tailEntity) + (AC1$ - P$(RR_Time,P$tailEntity)) * X$RR_Speed * P$(RR_Moving,P$tailEntity)\r\n    savevalue RR_EntryLimit,P$advance - P$(RR_Length,P$tailEntity) *0.5\r\n    \r\n    ; entity fully entered conveyor\r\n    IF (P$advance - P$(RR_Length,P$tailEntity) *0.5 >= P$(RR_Length,P$ENTITYNUMBER) * 0.5 + X$RR_Gap)\r\n            savevalue RR_EntryLimit,0\r\n            WAKE RR_InputBuffer,-1,P$ENTITYNUMBER\r\n            terminate_ve\r\n    ENDIF\r\n\r\n    ; entity stopped moving, timeout loses validity\r\n    if (P$(RR_Moving,P$tailEntity)==0)\r\n\r\n        ;ASSIGN msg,{operation:\"write\",value:\"RR_EnteringEntity Guardando P$ENTITYNUMBER\"}\r\n        ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n        savevalue RR_EnteringEntity,P$ENTITYNUMBER\r\n        TERMINATE_VE\r\n    endif\r\n\r\n    ; entity has not fully entered yet\r\n    assign remainingToEnter, ceil( (P$(RR_Length,P$ENTITYNUMBER) * 0.5 +  (P$(RR_Length,P$tailEntity) *0.5 + X$RR_Gap) - P$advance) \/ X$RR_Speed, X$RR_decimals)\r\n    timeout RR_EntryTimeout, P$remainingToEnter, P$ENTITYNUMBER\r\n    \r\n    TERMINATE_VE\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p>Este procedimiento, ejecutado por una <b>VE<\/b>, es el encargado de decidir si una caja puede abandonar el embudo para entrar en la cinta o si debe re-agendar su intento.<\/p>\r\n<p><b>Mec&aacute;nica de Evaluaci&oacute;n:<\/b><\/p>\r\n<p>Primero identifica qu&eacute; caja est&aacute; intentando entrar, bien mediante el par&aacute;metro recibido o, en caso de reactivaci&oacute;n del proceso, mediante la primera entidad pendiente en RR_InputBuffer.<br>\r\n<br>\r\nA continuaci&oacute;n identifica la Caja de cola (la &uacute;ltima que entr&oacute; en RR_Conveyor), ya que su posici&oacute;n f&iacute;sica trasera define el l&iacute;mite real de admisi&oacute;n. Gracias a la topolog&iacute;a causal mantenida por el recurso, esta referencia se obtiene directamente mediante RR_EntityTail, sin necesidad de recorrer la cinta.<\/p>\r\n<p>A partir de ah&iacute;, se eval&uacute;an cuatro escenarios:<\/p>\r\n<ul>\r\n    <li><b>Cinta Vac&iacute;a:<\/b> Si no hay nadie en la cinta, la entrada es inmediata.<\/li>\r\n    <li><b>Espacio Suficiente: <\/b>Si la distancia entre la entrada y la cola de la &uacute;ltima caja es suficiente, la caja entra.<\/li>\r\n    <li><b>Bloqueo por Parada:<\/b> Si no hay espacio y la caja limitante est&aacute; parada (por un atasco), el tiempo pierde su validez. La VE guarda el ID de la caja que espera en RR_EnteringEntity y finaliza.&nbsp;El proceso se reanudar&aacute; solo cuando la cinta vuelva a moverse.<\/li>\r\n    <li><b>Bloqueo en Movimiento: <\/b>Si la caja limitante se mueve pero a&uacute;n no deja hueco, se calcula el instante exacto de la reevaluaci&oacute;n bas&aacute;ndose en la velocidad.<\/li>\r\n<\/ul>\r\n<p>Obs&eacute;rvese que RR_EntryTimeout no garantiza la entrada en el instante programado: solo garantiza que ese es el pr&oacute;ximo momento en que la geometr&iacute;a podr&iacute;a permitirla. Si la caja limitante se detiene antes, el timeout se invalida.<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "418",
                                "nombre": "RR_Conveyor: Gestión de la Cinta",
                                "texto": "PROCEDURE RR_Conveyor\r\n\r\n    assign RR_Time,AC1$\r\n\r\n    assign RR_Time,AC1$,P$ENTITYNUMBER\r\n    assign RR_Position,0,P$ENTITYNUMBER\r\n    assign RR_Moving,1,P$ENTITYNUMBER\r\n\r\n    assign RR_PackFront,0,P$ENTITYNUMBER\r\n    assign RR_PackRear,0,P$ENTITYNUMBER\r\n\r\n    savevalue RR_EntryLimit,0\r\n\r\n    ; double linked list\r\n    if (X$RR_EntityTail == 0)\r\n        savevalue RR_EntityLeader, P$ENTITYNUMBER\r\n    else\r\n        assign RR_PackFront, X$RR_EntityTail, P$ENTITYNUMBER\r\n        assign RR_PackRear, P$ENTITYNUMBER, X$RR_EntityTail\r\n    endif\r\n    savevalue RR_EntityTail, P$ENTITYNUMBER\r\n\r\n\r\n    if (R$(RR_InputBuffer,IN)>0)\r\n        timeout RR_EntryTimeout,0\r\n    endif\r\n\r\n    call setStatusDisplay\r\n\r\n    ; check active stop limit against new entity\r\n    assign front,P$(RR_PackFront,P$ENTITYNUMBER)\r\n    if (X$RR_StopLimit > X$RR_Length * -1 && P$(RR_Moving,P$front)==0 && P$front!=0)\r\n\r\n        ;ASSIGN msg,{operation:\"write\",value:\"RR_Conveyor Orphan stop detected. Front entity P$front is stopped. Re-triggering stop propagation.\"}\r\n        ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n        savevalue RR_StopRegimeVersion, X$RR_StopRegimeVersion + 1\r\n        timeout RR_StopTimeout,0,X$RR_StopRegimeVersion\r\n    endif\r\n\r\n    ; only first entity schedules exit ownership\r\n    if (R$(RR_Conveyor,IN)>1)\r\n        terminate_ve\r\n    endif\r\n\r\n\r\n    assign time, ceil( X$RR_Length  \/ X$RR_Speed , X$RR_decimals)\r\n\r\n    savevalue RR_LeavingEntity,P$ENTITYNUMBER\r\n    \r\n    savevalue RR_ExitRegimeVersion, X$RR_ExitRegimeVersion + 1\r\n    timeout RR_ExitTimeout,P$time,X$RR_ExitRegimeVersion,44444\r\n\r\n    TERMINATE_VE\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p>Cuando una caja despierta del embudo e ingresa en la cinta, se dispara el procedimiento <code data-path-to-node=\"4\" data-index-in-node=\"88\">RR_Conveyor<\/code>.&nbsp;Su misi&oacute;n es inicializar el estado cinem&aacute;tico de la entidad, integrarla en la topolog&iacute;a causal del conveyor y determinar si debe asumir alg&uacute;n rol de control.<\/p>\r\n<h4 data-path-to-node=\"5\">A. Inicializaci&oacute;n Cinem&aacute;tica<\/h4>\r\n<p data-path-to-node=\"6\">Al entrar, la caja deja de ser una entidad est&aacute;tica para convertirse en un vector&nbsp;cinem&aacute;tico. Se le asignan tres atributos cr&iacute;ticos mediante <code data-path-to-node=\"6\" data-index-in-node=\"130\">ASSIGN<\/code>:<\/p>\r\n<ul>\r\n    <li><b data-path-to-node=\"7,0,0\" data-index-in-node=\"0\">RR_Time:<\/b> El instante exacto de entrada (<code data-path-to-node=\"7,0,0\" data-index-in-node=\"40\">AC1$<\/code>).<\/li>\r\n    <li><b data-path-to-node=\"7,1,0\" data-index-in-node=\"0\">RR_Position:<\/b> Posici&oacute;n inicial (0, la entrada).<\/li>\r\n    <li><b data-path-to-node=\"7,2,0\" data-index-in-node=\"0\">RR_Moving:<\/b> Estado activo (1).<\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Adem&aacute;s, se inicializan sus referencias topol&oacute;gicas&nbsp;<\/span>que describen las cajas vecinas dentro del conveyor<span>:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><strong><span>RR_PackFront<\/span><\/strong><\/li>\r\n    <li><strong><span>RR_PackRear<\/span><\/strong><\/li>\r\n<\/ul>\r\n<p data-path-to-node=\"8\">Inmediatamente, el sistema fuerza una reevaluaci&oacute;n de la admisi&oacute;n (<code data-path-to-node=\"8\" data-index-in-node=\"67\">RR_EntryTimeout<\/code>) por si hubiera cajas esperando en el&nbsp;RR_InputBuffer.<\/p>\r\n<h4><span>B. Inserci&oacute;n en la Topolog&iacute;a<\/span><\/h4>\r\n<p class=\"isSelectedEnd\"><span>El conveyor mantiene una topolog&iacute;a causal expl&iacute;cita mediante una lista doblemente enlazada.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Cada nueva caja se inserta siempre detr&aacute;s de la actual cola del sistema:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>Si la cinta est&aacute; vac&iacute;a, la entidad se convierte simult&aacute;neamente en RR_EntityLeader y RR_EntityTail.<\/span><\/li>\r\n    <li><span>Si ya existen cajas, la nueva entidad se enlaza con la cola actual mediante RR_PackFront y RR_PackRear.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>Tras la inserci&oacute;n, RR_EntityTail pasa a apuntar a la nueva caja.<\/span><\/p>\r\n<p><span>Gracias a esta estructura, el recurso conoce en todo momento qu&eacute; entidad est&aacute; m&aacute;s pr&oacute;xima a la salida y cu&aacute;l est&aacute; m&aacute;s pr&oacute;xima a la entrada sin necesidad de recorrer la cinta.<\/span><\/p>\r\n<h4><span>C. Recuperaci&oacute;n de Propagaciones de Stop<\/span><\/h4>\r\n<p class=\"isSelectedEnd\"><span>La entrada de una nueva caja puede revelar una situaci&oacute;n especial.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Si existe una propagaci&oacute;n de parada activa y la entidad situada inmediatamente delante se encuentra detenida, significa que existe un stop pendiente cuya cadena temporal ha desaparecido.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Este escenario puede producirse cuando una nueva caja entra detr&aacute;s de una entidad ya detenida.<\/span><\/p>\r\n<p><span>En ese caso, la propia entidad reci&eacute;n llegada reactiva la propagaci&oacute;n mediante un nuevo RR_StopTimeout, permitiendo que la l&oacute;gica contin&uacute;e exactamente donde se hab&iacute;a interrumpido.<\/span><\/p>\r\n<h4><span>D. Ownership de Salida<\/span><\/h4>\r\n<p class=\"isSelectedEnd\"><span>La salida del conveyor sigue una pol&iacute;tica de ownership &uacute;nico.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Solamente la entidad m&aacute;s pr&oacute;xima a la salida puede poseer la predicci&oacute;n de abandono de la cinta.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Por ello:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>Si ya existen entidades dentro del conveyor, la nueva caja entra en estado pasivo y no programa ning&uacute;n evento adicional.<\/span><\/li>\r\n    <li><span>Si es la &uacute;nica entidad presente, asume el rol de RR_LeavingEntity.<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>En este &uacute;ltimo caso calcula el tiempo de recorrido hasta el final de la cinta y programa un RR_ExitTimeout.<\/span><\/p>\r\n<p><span>Obs&eacute;rvese que la entidad no agenda su salida como una acci&oacute;n futura garantizada. Lo que agenda es la pr&oacute;xima reevaluaci&oacute;n conocida del estado de salida bajo el r&eacute;gimen actual del sistema.<\/span><\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "419",
                                "nombre": "RR_ExitTimeout: Orquestación de Salida",
                                "texto": "PROCEDURE RR_ExitTimeout\r\n\r\n    assign TOKEN,P$PARAM_A\r\n\r\n    if ( P$TOKEN != X$RR_ExitRegimeVersion)\r\n        terminate_ve\r\n    endif\r\n\r\n    assign ENTITYNUMBER,X$RR_LeavingEntity\r\n\r\n\r\n    ;ASSIGN msg,{operation:\"write\",value:\"RR_ExitTimeout : P$ENTITYNUMBER  (P$PARAM_B)\"}\r\n    ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n    ; entity stopped moving, timeout loses validity\r\n    if (P$(RR_Moving,P$ENTITYNUMBER) == 0)\r\n        ;ASSIGN msg,{operation:\"write\",value:\"RR_ExitTimeout Is stoped: P$ENTITYNUMBER\"}\r\n        ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n        TERMINATE_VE\r\n    endif\r\n\r\n    ; calculate current position\r\n    assign advance, P$(RR_Position,P$ENTITYNUMBER) + (AC1$ - P$(RR_Time,P$ENTITYNUMBER)) * X$RR_Speed\r\n\r\n    ; entity fully exited conveyor\r\n    IF (ceil(P$advance,X$RR_decimals) >= X$RR_Length)\r\n\r\n        wake RR_Conveyor,-1,P$ENTITYNUMBER\r\n        call setStatusDisplay\r\n\r\n    ELSE\r\n\r\n        ; entity has not fully exited yet\r\n        assign remainingToExit, ceil((X$RR_Length - P$advance) \/ X$RR_Speed,X$RR_decimals)\r\n\r\n        savevalue RR_ExitRegimeVersion, X$RR_ExitRegimeVersion + 1\r\n        timeout RR_ExitTimeout, P$remainingToExit , X$RR_ExitRegimeVersion, 22222\r\n        terminate_ve\r\n\r\n    ENDIF\r\n\r\n\r\n    ; find entity closest to conveyor exit\r\n    assign leadingEntity,P$(RR_PackRear,P$ENTITYNUMBER)\r\n    \r\n    savevalue RR_LeavingEntity,P$leadingEntity\r\n\r\n    savevalue RR_EntityLeader,P$leadingEntity\r\n    \r\n\r\n    ;ASSIGN msg,{operation:\"write\",value:\"RR_ExitTimeout Exit: P$ENTITYNUMBER next P$leadingEntity\"}\r\n    ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n    ; no more entities remaining\r\n    if (P$leadingEntity == 0)\r\n        savevalue RR_EntityLeader,0\r\n        savevalue RR_EntityTail,0\r\n        \r\n        savevalue RR_LeavingEntity,0\r\n        terminate_ve\r\n    endif\r\n\r\n    assign RR_PackFront,0,P$leadingEntity\r\n\r\n    assign advance, P$(RR_Position,P$leadingEntity)+(AC1$ - P$(RR_Time,P$leadingEntity)) * X$RR_Speed * P$(RR_Moving,P$leadingEntity)\r\n    assign remaining, X$RR_Length - P$advance\r\n    assign time, ceil(P$remaining \/ X$RR_Speed , X$RR_decimals)\r\n\r\n    savevalue RR_ExitRegimeVersion, X$RR_ExitRegimeVersion + 1\r\n    timeout RR_ExitTimeout,P$time,X$RR_ExitRegimeVersion, 11111\r\n\r\n    TERMINATE_VE\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p>El procedimiento RR_ExitTimeout es el encargado de materializar la salida de las cajas. Es fundamental entender que el instante programado no representa una garant&iacute;a de salida, sino el primer momento f&iacute;sicamente posible en el que la caja podr&iacute;a haber abandonado la cinta si no hubiese sufrido interrupciones.<\/p>\r\n<p>La filosof&iacute;a es id&eacute;ntica a la utilizada por RR_EntryTimeout: el evento no agenda una acci&oacute;n, agenda la pr&oacute;xima reevaluaci&oacute;n conocida de una frontera.<\/p>\r\n<p><br>\r\n<b>Mec&aacute;nica de Validaci&oacute;n y Relevo<\/b>:<br>\r\n<br>\r\nCuando el evento se dispara se realizan las comprobaciones necesarias para determinar si la VE debe finalizar, reprogramarse o transferir el control de la frontera:<\/p>\r\n<ul>\r\n    <li>\r\n    <p data-start=\"1103\" data-end=\"1128\"><strong data-start=\"1103\" data-end=\"1128\">Validaci&oacute;n de R&eacute;gimen<\/strong>:&nbsp;Lo primero que se comprueba es que el timeout sigue perteneciendo al r&eacute;gimen actual de salida. El valor recibido debe coincidir con RR_ExitRegimeVersion.<\/p>\r\n    <\/li>\r\n    <li><b>Validaci&oacute;n de Movimiento:<\/b> Si la caja est&aacute; parada (por un atasco), el timeout pierde su validez. Cuando vuelva a moverse se programar&aacute; de nuevo.<\/li>\r\n    <li><b>Validaci&oacute;n de Posici&oacute;n:<\/b> Se calcula la posici&oacute;n vectorial actual. Si por redondeos o cambios de velocidad la caja a&uacute;n no ha cruzado el l&iacute;mite (RR_Length), se calcula el tiempo restante y se reprograma un TIMEOUT residual.<\/li>\r\n    <li><b>Ejecuci&oacute;n de Salida:<\/b> Si la caja ha llegado al final, se ejecuta un WAKE para sacarla f&iacute;sicamente de RR_Conveyor programando, si existe, la salida de la siguiente caja.<\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>",
                                "parametros": "",
                                "parametros_json": "",
                                "ejemplo": "",
                                "hijos": []
                            },
                            {
                                "id": "420",
                                "nombre": "RR_StopTimeout: Gestión de Atascos y Acumulación",
                                "texto": "PROCEDURE RR_StopTimeout\r\n\r\n    ; stop system disabled\r\n    if (X$RR_StopLimit == X$RR_Length * -1)\r\n        terminate_ve\r\n    endif\r\n\r\n    ; PARAM_A = requested stop limit\r\n    ;savevalue RR_StopLimit,P$PARAM_A\r\n\r\n    if ( P$TOKEN != X$RR_SotpRegimeVersion)\r\n        terminate_ve\r\n    endif   \r\n    ;-------------------------------------------------\r\n    ; \r\n    ; Find entity currently invading stop limit\r\n    ; else \r\n    ; when invading\r\n    ; \r\n    ;-------------------------------------------------\r\n\r\n    assign entityNumber,0\r\n\r\n    if (X$RR_LastStopedEntity == 0)\r\n        assign entityNumber,X$RR_EntityLeader\r\n    else\r\n        assign entityNumber,P$(RR_PackRear,X$RR_LastStopedEntity)\r\n    endif\r\n\r\n    if (P$entityNumber==0)\r\n        terminate_ve\r\n    endif\r\n\r\n\r\n\r\n    assign currentHalf, P$(RR_Length,P$entityNumber) * 0.5\r\n    assign currentPosition, P$(RR_Position,P$entityNumber) +  (AC1$ - P$(RR_Time,P$entityNumber)) * X$RR_Speed\r\n\r\n    assign currentFront, P$currentPosition + P$currentHalf\r\n    assign currentTail,  P$currentPosition - P$currentHalf\r\n\r\n    ; entity intersects stop limit\r\n    if ( P$currentFront >= (X$RR_Length - X$RR_StopLimit) && P$currentTail <= (X$RR_Length - X$RR_StopLimit) && P$(RR_Moving,P$entityNumber) == 1)\r\n\r\n        ;ASSIGN msg,{operation:\"write\",value:\"RR_StopTimeout Stop in P$entityNumber\"}\r\n        ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n        ; freeze entity state\r\n        assign RR_Position, P$currentPosition, P$entityNumber\r\n        assign RR_Time,  AC1$, P$entityNumber\r\n        assign RR_Moving,  0,  P$entityNumber\r\n        savevalue RR_LastStopedEntity, P$entityNumber\r\n        ; propagate stop limit backward\r\n\r\n        savevalue RR_StopLimit,(X$RR_Length - P$currentTail)\r\n        savevalue RR_StopRegimeVersion, X$RR_StopRegimeVersion + 1\r\n        timeout RR_StopTimeout,  0, X$RR_StopRegimeVersion\r\n        call setStatusDisplay\r\n        terminate_ve\r\n\r\n    else\r\n\r\n\r\n        assign target,(X$RR_Length - X$RR_StopLimit)\r\n        assign remaining, P$target - P$currentFront\r\n        assign requiredTime, P$remaining \/ X$RR_Speed\r\n\r\n        ;ASSIGN msg,{operation:\"write\",value:\"RR_StopTimeout future stop de P$entityNumber en P$requiredTime\"}\r\n        ;EXEC_MEDIA {name:logUI,in:V$msg}\r\n\r\n\r\n        savevalue RR_StopRegimeVersion, X$RR_StopRegimeVersion + 1\r\n        timeout RR_StopTimeout, ceil(P$requiredTime,X$RR_decimals), X$RR_StopRegimeVersion\r\n    endif\r\n\r\n\r\n    TERMINATE_VE\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p data-path-to-node=\"4\">El procedimiento <code data-path-to-node=\"4\" data-index-in-node=\"17\">RR_StopTimeout<\/code> gestiona la detenci&oacute;n de las entidades cuando encuentran una restricci&oacute;n en la cinta (el &quot;tap&oacute;n&quot;). Por defecto, el l&iacute;mite de parada (<code data-path-to-node=\"4\" data-index-in-node=\"165\">RR_StopLimit<\/code>) est&aacute; fuera de la cinta (<code data-path-to-node=\"4\" data-index-in-node=\"203\">-RR_Length<\/code>), pero cuando se activa un atasco, el sistema inicia una cadena de eventos.<\/p>\r\n<h4 data-path-to-node=\"5\">Mec&aacute;nica de Propagaci&oacute;n (Backpressure)<\/h4>\r\n<p data-path-to-node=\"6\">El orquestador funciona mediante una b&uacute;squeda recursiva dividida en dos escenarios fundamentales:<\/p>\r\n<p data-path-to-node=\"7\"><b data-path-to-node=\"7\" data-index-in-node=\"0\">Escenario A: Colisi&oacute;n Inmediata<\/b> Si una caja est&aacute; invadiendo actualmente el l&iacute;mite de parada (su cuerpo intersecta la coordenada del tap&oacute;n):<\/p>\r\n<ol start=\"1\" data-path-to-node=\"8\">\r\n    <li>\r\n    <p data-path-to-node=\"8,0,0\"><b data-path-to-node=\"8,0,0\" data-index-in-node=\"0\">Clavado Cinem&aacute;tico:<\/b> La caja se detiene (<code data-path-to-node=\"8,0,0\" data-index-in-node=\"40\">moving: 0<\/code>) y se registra su posici&oacute;n y tiempo exactos en ese instante (<code data-path-to-node=\"8,0,0\" data-index-in-node=\"111\">AC1$<\/code>).<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"8,1,0\"><b data-path-to-node=\"8,1,0\" data-index-in-node=\"0\">Mutabilidad del L&iacute;mite:<\/b> El <code data-path-to-node=\"8,1,0\" data-index-in-node=\"27\">RR_StopLimit<\/code> se actualiza autom&aacute;ticamente a la <b data-path-to-node=\"8,1,0\" data-index-in-node=\"74\">cola f&iacute;sica<\/b> de la caja que acaba de chocar.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"8,2,0\"><b data-path-to-node=\"8,2,0\" data-index-in-node=\"0\">Propagaci&oacute;n Instant&aacute;nea:<\/b> Se rellama al <code data-path-to-node=\"8,2,0\" data-index-in-node=\"39\">RR_StopTimeout<\/code> a tiempo 0 para verificar si la siguiente caja ya est&aacute; tocando a la que se acaba de detener.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p data-path-to-node=\"9\"><b data-path-to-node=\"9\" data-index-in-node=\"0\">Escenario B: Colisi&oacute;n Futura<\/b> Si no hay ninguna caja tocando el l&iacute;mite actual, el sistema busca a la candidata m&aacute;s cercana que venga de camino:<\/p>\r\n<ol start=\"1\" data-path-to-node=\"10\">\r\n    <li>\r\n    <p data-path-to-node=\"10,0,0\"><b data-path-to-node=\"10,0,0\" data-index-in-node=\"0\">C&aacute;lculo Predictivo:<\/b> Determina cu&aacute;nto tiempo tardar&aacute; el frente de la pr&oacute;xima caja en alcanzar el nuevo l&iacute;mite.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"10,1,0\"><b data-path-to-node=\"10,1,0\" data-index-in-node=\"0\">Agendamiento:<\/b> Programa un <code data-path-to-node=\"10,1,0\" data-index-in-node=\"26\">TIMEOUT<\/code> para ese instante preciso.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<blockquote data-path-to-node=\"11\">\r\n<p data-path-to-node=\"11,0\"><b data-path-to-node=\"11,0\" data-index-in-node=\"0\">Efecto Domin&oacute;:<\/b> Este algoritmo permite que la acumulaci&oacute;n ocurra de forma natural. Las cajas siguen movi&eacute;ndose hasta que &quot;rellenan&quot; el hueco vac&iacute;o, optimizando la ocupaci&oacute;n de la cinta sin iteraciones innecesarias.<\/p>\r\n<\/blockquote>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "421",
                                "nombre": "RR_ResumeTimeout: Reanudación del Flujo",
                                "texto": "PROCEDURE RR_ResumeTimeout\r\n\r\n    ; remove active stop limit\r\n    savevalue RR_StopLimit,X$RR_Length * -1\r\n\r\n    assign stoppedEntity,0\r\n    ; resume all stopped entities\r\n    FOREACH entityNumber,IN_RESOURCE,RR_Conveyor\r\n\r\n        if (P$(RR_Moving,P$entityNumber) == 0)\r\n            assign stoppedEntity, P$entityNumber\r\n            assign RR_Time, AC1$, P$entityNumber\r\n            assign RR_Moving, 1, P$entityNumber\r\n        endif\r\n\r\n    ENDFOREACH\r\n\r\n    if (P$stoppedEntity == 0)\r\n        terminate_ve\r\n    endif\r\n\r\n    savevalue RR_LastStopedEntity, 0\r\n\r\n    call setStatusDisplay\r\n\r\n    ; restore pending entry verification\r\n    IF (X$RR_EnteringEntity!=0)\r\n        timeout RR_EntryTimeout, 0, X$RR_EnteringEntity\r\n    ENDIF\r\n\r\n    ; restore pending exit verification\r\n    IF (X$RR_LeavingEntity!=0)\r\n        timeout RR_ExitTimeout, 0, X$RR_LeavingEntity\r\n    ENDIF\r\n\r\n    savevalue RR_EnteringEntity,0\r\n\r\n    terminate_ve\r\n\r\nENDPROCEDURE",
                                "descripcion": "<p data-path-to-node=\"4\">El procedimiento <code data-path-to-node=\"4\" data-index-in-node=\"17\">RR_ResumeTimeout<\/code> se encarga de restaurar la cinem&aacute;tica del sistema una vez que la causa del atasco ha desaparecido. Su funci&oacute;n es &quot;limpiar&quot; el estado de bloqueo y volver a programar los eventos de las fronteras (Entrada y Salida).<\/p>\r\n<h4 data-path-to-node=\"5\">Mec&aacute;nica de Rehidrataci&oacute;n<\/h4>\r\n<p data-path-to-node=\"6\">A diferencia del atasco, que es progresivo, la reanudaci&oacute;n es <b data-path-to-node=\"6\" data-index-in-node=\"62\">at&oacute;mica<\/b> para todos los vectores de la cinta:<\/p>\r\n<ol start=\"1\" data-path-to-node=\"7\">\r\n    <li>\r\n    <p data-path-to-node=\"7,0,0\"><b data-path-to-node=\"7,0,0\" data-index-in-node=\"0\">Eliminaci&oacute;n del L&iacute;mite:<\/b> Se resetea <code data-path-to-node=\"7,0,0\" data-index-in-node=\"35\">RR_StopLimit<\/code> a su valor neutro (<code data-path-to-node=\"7,0,0\" data-index-in-node=\"67\">-RR_Length<\/code>), eliminando cualquier barrera l&oacute;gica.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"7,1,0\"><b data-path-to-node=\"7,1,0\" data-index-in-node=\"0\">Sincronizaci&oacute;n de Vectores:<\/b> Se recorre la cinta y, para cada caja parada, se establece su nuevo instante de movimiento (<code data-path-to-node=\"7,1,0\" data-index-in-node=\"120\">RR_Time = AC1$<\/code>) y se activa su estado (<code data-path-to-node=\"7,1,0\" data-index-in-node=\"159\">RR_Moving = 1<\/code>). Esto permite que el renderizador reinicie la interpolaci&oacute;n desde la posici&oacute;n actual.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"7,2,0\"><b data-path-to-node=\"7,2,0\" data-index-in-node=\"0\">Despertar de Orquestadores:<\/b><\/p>\r\n    <ul data-path-to-node=\"7,2,1\">\r\n        <li>\r\n        <p data-path-to-node=\"7,2,1,0,0\"><b data-path-to-node=\"7,2,1,0,0\" data-index-in-node=\"0\">Entrada:<\/b> Si hab&iacute;a una caja esperando sitio en el buffer (<code data-path-to-node=\"7,2,1,0,0\" data-index-in-node=\"57\">RR_EnteringEntity<\/code>), se relanza su <code data-path-to-node=\"7,2,1,0,0\" data-index-in-node=\"91\">RR_EntryTimeout<\/code> a tiempo 0.<\/p>\r\n        <\/li>\r\n        <li>\r\n        <p data-path-to-node=\"7,2,1,1,0\"><b data-path-to-node=\"7,2,1,1,0\" data-index-in-node=\"0\">Salida:<\/b> Si hab&iacute;a una caja l&iacute;der esperando para salir (<code data-path-to-node=\"7,2,1,1,0\" data-index-in-node=\"54\">RR_LeavingEntity<\/code>), se relanza su <code data-path-to-node=\"7,2,1,1,0\" data-index-in-node=\"87\">RR_ExitTimeout<\/code> a tiempo 0 para que recalcule su llegada al final.<\/p>\r\n        <\/li>\r\n    <\/ul>\r\n    <\/li>\r\n<\/ol>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "422",
                                "nombre": "Conclusión del Algoritmo",
                                "texto": "",
                                "descripcion": "<p data-path-to-node=\"12\">Este sistema de <b data-path-to-node=\"12\" data-index-in-node=\"16\">Gesti&oacute;n de Fronteras Mutables<\/b> implementado en <b data-path-to-node=\"12\" data-index-in-node=\"62\">GPSS-Plus<\/b> demuestra que es posible modelar sistemas de manejo de materiales de alta complejidad con una carga computacional m&iacute;nima.<\/p>\r\n<p data-path-to-node=\"12\">El algoritmo de Fronteras Mutables convierte un conveyor &mdash;tradicionalmente uno de los elementos m&aacute;s costosos de simular&mdash; en un sistema:<\/p>\r\n<ul>\r\n    <li>determinista<\/li>\r\n    <li>matem&aacute;ticamente limpio<\/li>\r\n    <li>reactivo<\/li>\r\n    <li>O(1) por evento<\/li>\r\n    <li>sin integraci&oacute;n<\/li>\r\n    <li>sin discretizaci&oacute;n<\/li>\r\n    <li>sin rec&aacute;lculos masivos<\/li>\r\n<\/ul>\r\n<p data-path-to-node=\"12\">Es, en esencia, un conveyor resuelto por geometr&iacute;a y eventos, no por movimiento.<\/p>\r\n<p>&nbsp;<\/p>",
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            }
                        ]
                    },
                    {
                        "id": "431",
                        "nombre": "Road",
                        "texto": "",
                        "descripcion": null,
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": [
                            {
                                "id": "432",
                                "nombre": "Road: Cinemática Distribuida sobre Entidades",
                                "texto": "<p data-start=\"48\" data-end=\"228\">El algoritmo <strong data-start=\"61\" data-end=\"69\">Road<\/strong> representa una ruptura fundamental respecto al modelo Conveyor, aunque ambos compartan la misma filosof&iacute;a DES de &ldquo;resolver &uacute;nicamente los cambios relevantes&rdquo;.<\/p>\r\n<p data-start=\"230\" data-end=\"325\">En Conveyor, la cinem&aacute;tica pertenece al recurso.<br>\r\nEn Road, la cinem&aacute;tica pertenece a la entidad.<\/p>\r\n<p data-start=\"327\" data-end=\"410\">Ese cambio parece peque&ntilde;o, pero transforma completamente el problema computacional.<\/p>\r\n<h3>El cambio de paradigma<\/h3>\r\n<p><b>Conveyor: el recurso mueve<\/b><\/p>\r\n<p data-start=\"474\" data-end=\"502\" style=\"margin-left: 40px;\">En una cinta transportadora&nbsp;existe una &uacute;nica velocidad global en el que el&nbsp;&nbsp;movimiento es homog&eacute;neo&nbsp;y las cajas simplemente heredan el r&eacute;gimen cinem&aacute;tico de la cinta.<\/p>\r\n<p data-start=\"641\" data-end=\"709\" style=\"margin-left: 40px;\">Las entidades no &ldquo;deciden&rdquo; moverse.&nbsp;La infraestructura las desplaza.<\/p>\r\n<p style=\"margin-left: 40px;\">&nbsp;<\/p>\r\n<p data-start=\"711\" data-end=\"779\" style=\"margin-left: 40px;\">Por eso el problema puede resolverse desde la geometr&iacute;a del recurso, porque la causalidad nace en la infraestructura.<\/p>\r\n<p><b>Road: la entidad se mueve<\/b><\/p>\r\n<p data-start=\"942\" data-end=\"992\" style=\"margin-left: 40px;\">En una carretera, ocurre exactamente lo contrario.<\/p>\r\n<p data-start=\"994\" data-end=\"1008\" style=\"margin-left: 40px;\">Cada veh&iacute;culo&nbsp;posee su propia velocidad,&nbsp;su propia aceleraci&oacute;n,&nbsp;su propio r&eacute;gimen temporal&nbsp;y su propia predicci&oacute;n futura.<\/p>\r\n<p data-start=\"1127\" data-end=\"1227\" style=\"margin-left: 40px;\">La carretera ya no &ldquo;arrastra&rdquo;, simplemente define el espacio f&iacute;sico donde los veh&iacute;culos evolucionan.<\/p>\r\n<p data-start=\"1229\" data-end=\"1293\" style=\"margin-left: 40px;\">La causalidad deja de pertenecer al recurso y pasa a la entidad.<\/p>\r\n<p data-start=\"1295\" data-end=\"1305\">Por tanto&nbsp;el conveyor es un problema geom&eacute;trico&nbsp;mientras que road es un problema cinem&aacute;tico distribuido. De ser la cinta la que comprueba quien se atasca a ser un veh&iacute;culo a saber qu&eacute; otro veh&iacute;culo le atasca.<\/p>\r\n<p data-start=\"327\" data-end=\"410\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "433",
                                "nombre": "Definición del Problema",
                                "texto": "<h3>Definici&oacute;n del Problema<\/h3>\r\n<p>Una carretera de un &uacute;nico carril es un recurso que permite el desplazamiento de veh&iacute;culos entre dos puntos y que definimos con estas premisas:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>Gesti&oacute;n de Entrada: Un embudo previo acumula los veh&iacute;culos para que ingresen de uno en uno. Es simplemente una cola FIFO que regula el acceso al punto de incorporaci&oacute;n.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>Dimensiones: La carretera tiene una longitud definida.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>Cinem&aacute;tica Individual: Cada veh&iacute;culo posee su propio r&eacute;gimen cinem&aacute;tico definido por: posici&oacute;n, velocidad, aceleraci&oacute;n&nbsp;y tiempo de referencia.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>Ocupaci&oacute;n F&iacute;sica: Los veh&iacute;culos ocupan espacio f&iacute;sico y deben mantener una distancia m&iacute;nima de seguridad (gap) respecto al veh&iacute;culo precedente.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>Propagaci&oacute;n Din&aacute;mica: Un veh&iacute;culo cambia su r&eacute;gimen si alcanza al veh&iacute;culo delantero, y deber&aacute; adaptar autom&aacute;ticamente su cinem&aacute;tica para conservar la separaci&oacute;n m&iacute;nima.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>Nuestra labor ser&aacute; crear un recurso que admita la llegada de entidades (veh&iacute;culos con distintos reg&iacute;menes cinem&aacute;ticos), pasen del embudo a la carretera, evolucionen din&aacute;micamente interactuando entre s&iacute; y finalmente abandonen el sistema.<\/p>\r\n<p>El algoritmo consiste en modelar este comportamiento sin discretizar el espacio, sin integrar movimiento paso a paso y sin realizar rec&aacute;lculos continuos, utilizando &uacute;nicamente eventos discretos que representen cambios relevantes en la validez de los reg&iacute;menes cinem&aacute;ticos.<\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "434",
                                "nombre": "Arquitectura y utillería",
                                "texto": "<p class=\"isSelectedEnd\"><span>La arquitectura del recurso <\/span><code dir=\"ltr\"><span>Road<\/span><\/code><span> es una evoluci&oacute;n directa del algoritmo utilizado en el Conveyor. Ambos comparten la misma filosof&iacute;a fundamental:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>desacoplar completamente la visualizaci&oacute;n de la l&oacute;gica,<\/span><\/li>\r\n    <li><span>representar el movimiento mediante reg&iacute;menes matem&aacute;ticos,<\/span><\/li>\r\n    <li><span>y resolver &uacute;nicamente los cambios relevantes mediante eventos discretos.<\/span><\/li>\r\n<\/ul>\r\n<p><span>Sin embargo, el problema cinem&aacute;tico de veh&iacute;culos introduce dos diferencias fundamentales que transforman profundamente la arquitectura interna del sistema.<\/span><\/p>\r\n<p>&nbsp;<\/p>\r\n<h3><span>1. Topolog&iacute;a causal expl&iacute;cita<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En el Conveyor, y para ser m&aacute;s explicativo, la l&oacute;gica buscaba qu&eacute; entidad era relevante para cada c&aacute;lculo geom&eacute;trico. En el recurso <\/span><code dir=\"ltr\"><span>Road<\/span><\/code><span>&nbsp;ya introducimos la lista doblemente enlazada en cada veh&iacute;culo para saber qui&eacute;n tiene delante y qui&eacute;n tiene detr&aacute;s.<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>el veh&iacute;culo precedente (<\/span><code dir=\"ltr\"><span>RR_CarFront<\/span><\/code><span>)<\/span><\/li>\r\n    <li><span>el veh&iacute;culo posterior (<\/span><code dir=\"ltr\"><span>RR_CarRear<\/span><\/code><span>)<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>y el sistema conserva adem&aacute;s:<\/span><\/p>\r\n<ul data-spread=\"false\">\r\n    <li><span>el l&iacute;der actual (<\/span><code dir=\"ltr\"><span>RR_CarLeader<\/span><\/code><span>)<\/span><\/li>\r\n    <li><span>y el &uacute;ltimo veh&iacute;culo de la carretera (<\/span><code dir=\"ltr\"><span>RR_CarTail<\/span><\/code><span>)<\/span><\/li>\r\n<\/ul>\r\n<p class=\"isSelectedEnd\"><span>De este modo, las operaciones fundamentales dejan de requerir b&uacute;squedas globales sobre el recurso y pasan a resolverse mediante acceso directo.<\/span><\/p>\r\n<p>&nbsp;<\/p>\r\n<h3><span>2. Los Timeouts dejan de representar posibilidades&nbsp;<\/span><\/h3>\r\n<p class=\"isSelectedEnd\"><span>En el Conveyor, un <\/span><code dir=\"ltr\"><span>TIMEOUT<\/span><\/code><span> representaba el pr&oacute;ximo instante geom&eacute;tricamente posible donde una restricci&oacute;n pod&iacute;a cambiar. &Eacute;l mismo se recalculaba si en ese momento, no se hab&iacute;an cumplido las condiciones previstas. Nunca pod&iacute;a ser antes.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>En <\/span><code dir=\"ltr\"><span>Road<\/span><\/code><span>, esto no es as&iacute;.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Cada veh&iacute;culo posee un r&eacute;gimen cinem&aacute;tico propio<\/span>&nbsp;y p<span>or tanto, cualquier predicci&oacute;n futura depende completamente de que dicho r&eacute;gimen permanezca v&aacute;lido. Sin saber si la modificaci&oacute;n ser&aacute; a un tiempo posterior, lo que obliga a deshechar esa previsi&oacute;n.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Esto introduce un concepto nuevo en el motor:<\/span><\/p>\r\n<blockquote>\r\n<p class=\"isSelectedEnd\"><span>Un evento futuro no representa una verdad futura, sino una hip&oacute;tesis matem&aacute;tica v&aacute;lida bajo un r&eacute;gimen concreto.<\/span><\/p>\r\n<\/blockquote>\r\n<p class=\"isSelectedEnd\"><span>Cuando un veh&iacute;culo alcanza al precedente, cambia autom&aacute;ticamente su r&eacute;gimen din&aacute;mico:<\/span>&nbsp;<span>hereda velocidad,<\/span>&nbsp;<span>aceleraci&oacute;n<\/span>&nbsp;<span>y nueva referencia temporal.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>En ese instante, todas las predicciones anteriores dejan de tener sentido f&iacute;sico.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Para resolver este problema, cada entidad mantiene un identificador de versi&oacute;n cinem&aacute;tica (<\/span><code dir=\"ltr\"><span>RR_RegimeVersion<\/span><\/code><span>).<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Cada vez que cambia el r&eacute;gimen<\/span>&nbsp;<span>la versi&oacute;n se incrementa<\/span>&nbsp;<span>y todos los eventos futuros programados con versiones antiguas se autodestruyen autom&aacute;ticamente al ejecutarse.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Esto convierte los <\/span><code dir=\"ltr\"><span>TIMEOUT<\/span><\/code><span> en predicciones autoconsistentes y desechables.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>El motor no necesita<\/span>&nbsp;<span>cancelar eventos globales, se invalidan solos.<\/span><\/p>\r\n<p class=\"isSelectedEnd\"><span>Como nota, tambi&eacute;n se podr&iacute;a resolver invalidando directamente el timeout almacenandoo su D$N y lanz&aacute;ndole un signal.&nbsp;<\/span><span>Sin embargo, el mecanismo basado en versiones cinem&aacute;ticas (<\/span><code dir=\"ltr\"><span>RR_RegimeVersion<\/span><\/code><span>) resulta m&aacute;s desacoplado y robusto, ya que evita gestionar expl&iacute;citamente el ciclo de vida de las entidades virtuales pendientes y convierte cada predicci&oacute;n en autocontenida y autoinvalidable.<\/span><\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            }
                        ]
                    },
                    {
                        "id": "423",
                        "nombre": "Train",
                        "texto": "",
                        "descripcion": null,
                        "parametros": null,
                        "parametros_json": null,
                        "ejemplo": null,
                        "hijos": [
                            {
                                "id": "424",
                                "nombre": "Introducción",
                                "texto": "<h3>Los trenes<\/h3>\r\n<p>El algoritmo de gesti&oacute;n de trenes por tramos (cantones) es un problema cl&aacute;sico de exclusi&oacute;n mutua m&uacute;ltiple que dif&iacute;cilmente se puede observar y modelar con tanta claridad como en GPSS-Plus.<br>\r\n<br>\r\nPara abordar la l&oacute;gica de simulaci&oacute;n de este entorno, se hace indispensable comprender algo del vocabulario y la naturaleza del modelo l&oacute;gico.<br>\r\n<br>\r\n<br>\r\n<b>Topolog&iacute;a r&iacute;gida (Infraestructura Est&aacute;tica):<\/b><\/p>\r\n<ul>\r\n    <li>Los cantones -&gt; Tramo de v&iacute;a delimitado entre un punto A y un punto B. Act&uacute;a como la unidad m&iacute;nima de ocupaci&oacute;n y el recurso base de exclusi&oacute;n mutua.<\/li>\r\n    <li>Las conexiones -&gt; Bifurcaciones l&oacute;gicas que enlazan los cantones, dise&ntilde;adas bien para converger (unir dos v&iacute;as en una) o para divergir (separar una v&iacute;a en dos).<\/li>\r\n    <li>Cruce -&gt; Intersecci&oacute;n f&iacute;sica de dos cantones con exclusi&oacute;n mutua at&oacute;mica. No hay transferencia de trenes entre ellos, pero sus trayectorias son incompatibles simult&aacute;neamente.<\/li>\r\n<\/ul>\r\n<p><b>Topolog&iacute;a activa (Elementos de Control Din&aacute;mico):<\/b><\/p>\r\n<ul>\r\n    <li>Las agujas -&gt; Act&uacute;an como un conmutador (switch) din&aacute;mico entre tres cantones (un origen com&uacute;n y dos alternativas de salida).<\/li>\r\n    <li>Las se&ntilde;ales -&gt; indicaciones situadas en los cantones gestionadas por el CTC<\/li>\r\n<\/ul>\r\n<p><br>\r\n---------------------------------------------<br>\r\n<b>Elementos activos (Entidades):&nbsp;<\/b><\/p>\r\n<ul>\r\n    <li>CTC -&gt; Control de tr&aacute;fico centralizado. El n&uacute;cleo del modelo de simulaci&oacute;n. Es el sistema supervisor encargado de procesar la telemetr&iacute;a, gobernar el estado de las se&ntilde;ales y alinear las agujas en base a las reglas de seguridad y explotaci&oacute;n.<\/li>\r\n    <li>Trenes -&gt; Fluyen por la red de cantones. Se mueven gestionando sus propios atributos locales de velocidad, aceleraci&oacute;n y objetivo de velocidad en base a las se&ntilde;ales que encuentran.<\/li>\r\n<\/ul>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "425",
                                "nombre": "Descomposición del Problema Ferroviario. Planificador vs. Operador",
                                "texto": "<h3 data-path-to-node=\"7\">La Separaci&oacute;n de Poderes: Scheduler vs. Dispatcher<\/h3>\r\n<p class=\"isSelectedEnd\"><span>En una red ferroviaria existen dos problemas distintos:<\/span><\/p>\r\n<ol data-spread=\"false\" start=\"1\">\r\n    <li><span>decidir por d&oacute;nde debe circular un tren,<\/span><\/li>\r\n    <li><span>y gestionar de forma segura los trenes que ya est&aacute;n circulando.<\/span><\/li>\r\n<\/ol>\r\n<p><span>Aunque ambos problemas est&aacute;n relacionados, en la simulaci&oacute;n se resuelven por separado.<\/span><\/p>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<p data-path-to-node=\"8\"><b>El Scheduler (planificaci&oacute;n de rutas):<\/b><\/p>\r\n<p data-path-to-node=\"8\" style=\"margin-left: 40px;\">El <i>Scheduler <\/i>es el encargado de calcular el itinerario de cada tren antes de iniciar la marcha.<br>\r\n<br>\r\nSu funci&oacute;n es responder preguntas como:<br>\r\n&iquest;qu&eacute; camino debe seguir?<br>\r\n&iquest;qu&eacute; desv&iacute;os debe atravesar?<br>\r\n&iquest;cu&aacute;l es la mejor ruta disponible?<br>\r\n<br>\r\nEl resultado es una lista ordenada de cantones que define el recorrido previsto del tren.<br>\r\n<br>\r\nEn sistemas reales este problema suele resolverse mediante algoritmos de b&uacute;squeda de caminos como Dijkstra o A*.<\/p>\r\n<p data-path-to-node=\"8\"><b data-path-to-node=\"10,1,0\" data-index-in-node=\"0\">Dispatcher: control de circulaci&oacute;n:<\/b><\/p>\r\n<p data-path-to-node=\"8\" style=\"margin-left: 40px;\">El Dispatcher (CTC) no calcula rutas.<br>\r\n<br>\r\nParte de la base de que cada tren ya conoce su itinerario y &uacute;nicamente se ocupa de gestionar la circulaci&oacute;n en tiempo real.<br>\r\n<br>\r\nSus responsabilidades son:<br>\r\n<br>\r\nreservar cantones,<br>\r\nproteger cruces y conflictos,<br>\r\nmover agujas,<br>\r\ny autorizar o detener trenes seg&uacute;n el estado de la red.<br>\r\n<br>\r\nSu objetivo principal es garantizar que dos trenes incompatibles no ocupen simult&aacute;neamente la misma infraestructura.<\/p>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<p><b><span>Alcance del modelo<\/span><\/b><\/p>\r\n<p class=\"isSelectedEnd\" style=\"margin-left: 40px;\"><span>En esta simulaci&oacute;n s&oacute;lo se implementa el Dispatcher.<\/span><\/p>\r\n<p style=\"margin-left: 40px;\"><span>El Scheduler queda fuera del modelo porque precisamente requiere una simulaci&oacute;n previa capaz de determinar si una nueva circulaci&oacute;n puede incorporarse a la red sin generar conflictos.<\/span><\/p>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "426",
                                "nombre": "Arquitectura Asíncrona",
                                "texto": "<h3 data-path-to-node=\"7\">El Tren frente a Conveyor y Road: La obligaci&oacute;n de informar<\/h3>\r\n<p data-path-to-node=\"8\">Es crucial entender la ruptura de paradigma que supone el entorno ferroviario respecto a los algoritmos de <i data-path-to-node=\"8\" data-index-in-node=\"183\">Conveyor<\/i> (Cintas) o <i data-path-to-node=\"8\" data-index-in-node=\"203\">Road<\/i> (Carreteras):<\/p>\r\n<p data-path-to-node=\"9\">El tren se comportar&aacute; como suced&iacute;a en los anteriores algoritmos de <i data-path-to-node=\"9\" data-index-in-node=\"100\">conveyor<\/i> y <i data-path-to-node=\"9\" data-index-in-node=\"111\">road<\/i>, al estilo <b data-path-to-node=\"9\" data-index-in-node=\"127\">fire &amp; forget<\/b> (disparar y olvidar), ya que con su itinerario, estado y permisos, tienen todo su vector de movimiento.<\/p>\r\n<p data-path-to-node=\"10\">Sin embargo, la diferencia radical con los otros dos algoritmos es que aqu&iacute;, el tren, s&iacute; debe informar al sistema de su estado.&nbsp;Mientras que un coche o un paquete son arrastrados de forma pasiva por  las leyes f&iacute;sicas locales de su carril o su cinta, el tren act&uacute;a como un  agente activo distribuido. Cada tren es responsable de emitir su  telemetr&iacute;a; si el tren no informa de que ha cruzado un l&iacute;mite, el CTC no  puede liberar la infraestructura y el modelo se bloquea.<\/p>\r\n<h3>Las comunicaciones:<\/h3>\r\n<p data-path-to-node=\"6\">Una vez acotado el campo de juego al operador (<i data-path-to-node=\"6\" data-index-in-node=\"47\">Dispatcher<\/i>), debemos abordar el dise&ntilde;o del algoritmo. Para entenderlo, es fundamental separar la <b data-path-to-node=\"6\" data-index-in-node=\"144\">descripci&oacute;n te&oacute;rica de las comunicaciones<\/b> (el lenguaje que hablan entre ellos) de la <b data-path-to-node=\"6\" data-index-in-node=\"229\">descripci&oacute;n te&oacute;rica de la computaci&oacute;n<\/b> (c&oacute;mo piensa cada entidad por separado).<\/p>\r\n<p data-path-to-node=\"7\">El sistema se basa en un modelo <b data-path-to-node=\"7\" data-index-in-node=\"32\">Maestro-Esclavo<\/b> compuesto por dos entes pensantes diferenciados que trabajan de forma colaborativa y as&iacute;ncrona:<\/p>\r\n<pre>\r\n┌────────────────────────────────────────────────────────┐\r\n│ CTC (Maestro - Agente)                                 │\r\n│ Permanece est&aacute;tico. Gestiona la matriz global.         │ \r\n└───────────────────────────▲────┬───────────────────────┘ \r\n                            │    │\r\n            Telemetr&iacute;a      ▲    ▼\r\n                            │    │  Permisos     \r\n                            ▲    ▼\r\n                            │    │\r\n┌───────────────────────────┴────▼───────────────────────┐ \r\n│ TRENES (Esclavos - Entidades F&iacute;sicas)                  │\r\n│ Fluyen por el mapa. Mutan su cinem&aacute;tica y posici&oacute;n.    │ \r\n└────────────────────────────────────────────────────────┘<\/pre>\r\n<p data-path-to-node=\"7\"><b>1. El CTC (El Maestro Est&aacute;tico)<\/b><\/p>\r\n<p data-path-to-node=\"12\">Est&aacute; representado por una transacci&oacute;n de control siempre activa &quot;mentalmente&quot; pero sin representaci&oacute;n f&iacute;sica en las v&iacute;as. Su &uacute;nica misi&oacute;n es evaluar el estado de la red cuando ocurre un evento. Su ciclo de vida es un bucle infinito de escucha:<\/p>\r\n<pre>\r\nwhile (1==1)\r\n    advance 1000\r\nendwhile<\/pre>\r\n<p><b>2. Los Trenes (Los Esclavos F&iacute;sicos)<\/b><\/p>\r\n<p>Son entidades con representaci&oacute;n en el simulador. Viven encerrados en su propio bucle infinito, pero, a diferencia del CTC, ellos s&iacute; ejecutan trabajo f&iacute;sico y cinem&aacute;tico. Su motor interno es una <b data-path-to-node=\"15\" data-index-in-node=\"225\">m&aacute;quina de estados<\/b> que se actualiza continuamente:<\/p>\r\n<pre>\r\nwhile (1==1)\r\n   assign state, P$stateNext\r\n   ...\r\n   ...\r\nendwhile<\/pre>\r\n<p><b>3. Las Comunicaciones: El Protocolo de Intercambio<\/b><\/p>\r\n<p data-path-to-node=\"19\">Con las dos mentes funcionando en paralelo, la l&oacute;gica general de la simulaci&oacute;n se reduce a un ping-pong constante de mensajes distribuidos en cuatro pasos estrictos:<\/p>\r\n<ol start=\"1\" data-path-to-node=\"20\">\r\n    <li>\r\n    <p data-path-to-node=\"20,0,0\"><b data-path-to-node=\"20,0,0\" data-index-in-node=\"0\">Tren ──&gt; CTC (Telemetr&iacute;a):<\/b> El tren alcanza un punto de control y emite un paquete con su situaci&oacute;n actual e intenciones futuras.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"20,1,0\"><b data-path-to-node=\"20,1,0\" data-index-in-node=\"0\">CTC ──&gt; Tren (Despacho):<\/b> El CTC recibe el mensaje, consulta la base de datos global de cantones y despacha los permisos necesarios si est&aacute;n disponibles.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"20,2,0\"><b data-path-to-node=\"20,2,0\" data-index-in-node=\"0\">Tren ──&gt; Actuaci&oacute;n:<\/b> El tren recibe la orden, altera su m&aacute;quina de estados local y modifica su velocidad y su posici&oacute;n en consecuencia.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"20,3,0\"><b data-path-to-node=\"20,3,0\" data-index-in-node=\"0\">Tren ──&gt; CTC:<\/b> Tras completar su actuaci&oacute;n f&iacute;sica, el tren vuelve a emitir telemetr&iacute;a, reiniciando el ciclo.<\/p>\r\n    <\/li>\r\n<\/ol>\r\n<p>&nbsp;<\/p>\r\n<p><b>La M&aacute;quina de Estados del Tren<\/b><\/p>\r\n<p data-path-to-node=\"23\">Mientras que el CTC no tiene &quot;estados&quot; propios (el CTC es un administrador que custodia y gestiona la matriz global de permisos de todos los cantones), el tren reacciona estrictamente a tres &oacute;rdenes cinem&aacute;ticas (estados):<\/p>\r\n<ul data-path-to-node=\"24\">\r\n    <li>\r\n    <p data-path-to-node=\"24,0,0\"><b data-path-to-node=\"24,0,0\" data-index-in-node=\"0\">Estado 0 (DETENERSE):<\/b> Denegaci&oacute;n de permiso. El tren calcula su rampa de frenado para detenerse por completo antes de invadir el siguiente cant&oacute;n.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"24,1,0\"><b data-path-to-node=\"24,1,0\" data-index-in-node=\"0\">Estado 1 (INICIAR LA MARCHA):<\/b> Permiso concedido desde parado. El tren aplica su tasa de aceleraci&oacute;n para vencer la inercia y salir del cant&oacute;n actual.<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p data-path-to-node=\"24,2,0\"><b data-path-to-node=\"24,2,0\" data-index-in-node=\"0\">Estado 2 (CONTINUAR LA MARCHA):<\/b> Permiso concedido en velocidad. El tren mantiene su r&eacute;gimen de velocidad crucero y avanza de forma fluida hacia el siguiente cant&oacute;n.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>&nbsp;<\/p>\r\n<p>Mientras que el CTC no tiene un estado, sino que conserva y gestiona los permisos de todos los cantones.<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "427",
                                "nombre": "La utillería",
                                "texto": "<p>Si bien en conveyor y road lo importante era el estado de cada elemento que define su vector y el control de ese vector, en train el elemento principal es el estado de los cantones. Si est&aacute;n libres o reservados por un tren en particular.<br>\r\n<br>\r\n<b>Los tres tipos de Cant&oacute;n en la Base de Datos (JSON):<\/b><\/p>\r\n<p>Por simplificar, en ese mismo estado de un cant&oacute;n, tambi&eacute;n introducimos qu&eacute; otros cantones deben ser reservados a la vez que ese mismo y si tiene la caractar&iacute;stica de contener una aguja para tener 3 coordenadas con un origen, un destino y un destino alternativo:<\/p>\r\n<pre>\r\n; cant&oacute;n normal:\r\n\r\n&nbsp; &quot;37&quot;: { &quot;data&quot;: { &quot;x1&quot;: 617, &quot;y1&quot;: 59, \r\n                    &quot;x2&quot;: 573, &quot;y2&quot;: 100,\r\n                    &quot;next&quot;: 38,\r\n                    &quot;prev&quot;: 36 } },\r\n\r\n; cant&oacute;n que al ser reservado implica reservar otros dos para preservar los flancos:\r\n\r\n&nbsp; &quot;38&quot;: { &quot;data&quot;: { &quot;x1&quot;: 573, &quot;y1&quot;: 100,\r\n                    &quot;x2&quot;: 532, &quot;y2&quot;: 149,\r\n                     &quot;next&quot;: 39, \r\n                     &quot;prev&quot;: 37 , \r\n                     &quot;bloq&quot;: [17, 16]} },\r\n\r\n; cant&oacute;n con bifurcaci&oacute;n:\r\n\r\n&nbsp; &quot;40&quot;: { &quot;data&quot;: { &quot;x1&quot;: 468, &quot;y1&quot;: 251,\r\n                    &quot;x2&quot;: 427, &quot;y2&quot;: 300,\r\n                    &quot;next&quot;: 41, \r\n                    &quot;prev&quot;: 39 , \r\n                    &quot;alt&quot;:300, \r\n                        &quot;x3&quot;: 468, &quot;y3&quot;: 251, \r\n                        &quot;position&quot;:0} },\r\n  <\/pre>\r\n<p>al que el CTC le a&ntilde;ade un atributo m&aacute;s: &quot;occupiedBy&quot;, para indicar si est&aacute; libre (0) o el tren que lo tiene ocupado o reservado.<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "428",
                                "nombre": "La Ventana Asíncrona y la Reserva N+2",
                                "texto": "<p>Con la estructura de datos simplificada, el algoritmo de control se reduce a resolver tres preguntas cr&iacute;ticas: <b data-path-to-node=\"7\" data-index-in-node=\"110\">&iquest;Cu&aacute;ndo habla el tren con el CTC?<\/b>&nbsp;<b data-path-to-node=\"7\" data-index-in-node=\"146\">&iquest;Cu&aacute;nto espacio necesita reservar para no chocar? y &iquest;Cuanto tarda el CTC en preparar las agujas?<\/b><\/p>\r\n<h3 data-path-to-node=\"7\">1. El Intervalo Inexacto de la Comunicaci&oacute;n (La Ventana de Telemetr&iacute;a)<\/h3>\r\n<p data-path-to-node=\"8\">La comunicaci&oacute;n entre el tren y el CTC no ocurre en un momento exacto. Un tren llega al final de un cant&oacute;n a una velocidad determinada y, debido a su masa, no puede frenar de manera brusca. Necesita espacio. En nuestro algoritmo, por simplificaci&oacute;n, el tren requiere 1\/2 de la longitud de un cant&oacute;n completo para detenerse.<\/p>\r\n<p data-path-to-node=\"8\">El proceso se desencadena en el instante en que el tren llega al final de su cant&oacute;n actual e invade el siguiente. Sin embargo, la respuesta del CTC no es instant&aacute;nea ni s&iacute;ncrona; llegar&aacute; en alg&uacute;n momento posterior, a lo largo del tr&aacute;nsito por el nuevo cant&oacute;n, y siempre antes de alcanzar el siguiente fin de cant&oacute;n. Si la respuesta no llegase a tiempo, simplemente el tren no tendr&iacute;a permisos para continuar y se detendr&iacute;a.<\/p>\r\n<p data-path-to-node=\"8\">Para emular de forma realista que este proceso ocurre en un punto intermedio cualquiera, posponemos el env&iacute;o mediante un timeout y lo transmitimos al CTC a trav&eacute;s de un signal as&iacute;ncrono:<\/p>\r\n<pre><b>timeout sendTelemetry<\/b>,2,D$N\r\n...\r\n...\r\n\r\nprocedure sendTelemetry\r\n        assign train,P$PARAM_A\r\n\r\n        assign status,{\r\n                train:P$train,\r\n                state:P$(state,P$train),\r\n                route:P$(route,P$train),\r\n                occupiedBlock:P$(block,P$train),\r\n                releasedBlock:P$(releasedBlock,P$train)\r\n                }\r\n        signal processTelemetry,X$ctc,V$status\r\n\r\n    terminate_ve\r\nendprocedure\r\n<\/pre>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<h3 data-path-to-node=\"14\">2. La Regla de Reserva N+2 y la Devoluci&oacute;n de Permisos<\/h3>\r\n<p data-path-to-node=\"15\">El CTC, por su parte, debe garantizar que el tren disponga de ese cant&oacute;n de seguridad para frenar si la v&iacute;a estuviera cortada m&aacute;s adelante. Por tanto, el algoritmo computa con <b data-path-to-node=\"15\" data-index-in-node=\"176\">dos cantones de vista al frente (N+2)<\/b>: eval&uacute;a el cant&oacute;n subsiguiente (<code data-path-to-node=\"15\" data-index-in-node=\"246\">nextNext<\/code>), reserva los cantones extra necesarios seg&uacute;n las restricciones de la base de datos (como los flancos de protecci&oacute;n <code data-path-to-node=\"15\" data-index-in-node=\"371\">&quot;bloq&quot;<\/code>) y libera los cantones que el tren ya ha abandonado.<\/p>\r\n<p data-path-to-node=\"16\">Tras procesar la solicitud y asignar los cantones posibles a los requerimientos del tren, el CTC genera un extracto de los permisos de ese tren y se los env&iacute;a de nuevo por un <code data-path-to-node=\"16\" data-index-in-node=\"220\">signal<\/code> as&iacute;ncrono:<\/p>\r\n<pre>\r\ncall getTrainPermissions,P$train\r\n<b>signal trainReceiveOrders,P$train,V$getTrainPermissions<\/b>\r\n...\r\n...\r\n\r\nprocedure getTrainPermissions\r\n    assign permissions,[]\r\n    FOREACH nblock, IN_OBJECT, V$(blocks)\r\n        assign occupiedBy,X$(blocks.P$nblock.data.occupiedBy)\r\n        if (P$occupiedBy == P$PARAM_A)\r\n            assign.push permissions, P$nblock\r\n        endif\r\n    ENDFOREACH\r\n    return V$permissions\r\nendprocedure\r\n<\/pre>\r\n<h3 data-path-to-node=\"19\">3. El Enclavamiento Automatizado de las Agujas<\/h3>\r\n<p data-path-to-node=\"20\">En este flujo as&iacute;ncrono, el movimiento de la infraestructura est&aacute; ligado directamente a la autorizaci&oacute;n del espacio. <b data-path-to-node=\"20\" data-index-in-node=\"117\">En el mismo instante en el que se otorga un permiso de ocupaci&oacute;n, el CTC iniciar&aacute; el cambio las agujas necesarias<\/b> para el tren al que le acaba de conceder la v&iacute;a.<\/p>\r\n<p data-path-to-node=\"20\">La aguja se mueve como consecuencia l&oacute;gica de la firma de la reserva en el CTC. Una vez que el tren recibe su array de cantones autorizados a trav&eacute;s de <code data-path-to-node=\"21\" data-index-in-node=\"232\">trainRecibeOrdenes<\/code>, la aguja ya estar&aacute; alineada en la posici&oacute;n correcta (<code data-path-to-node=\"21\" data-index-in-node=\"305\">position: 0<\/code> o <code data-path-to-node=\"21\" data-index-in-node=\"319\">1<\/code>), garantizando que el tren realice su actualizaci&oacute;n f&iacute;sica (<code data-path-to-node=\"21\" data-index-in-node=\"381\">trainUpdate<\/code>) y visual sin saltos ni descuadres geom&eacute;tricos.<\/p>\r\n<p data-path-to-node=\"21\">Ese es el principal motivo por el que la comunicaci&oacute;n es as&iacute;ncrona. No  se sabe cuanto tiempo tardar&aacute; el CTC en mover las agujas desde que  recibe la solicitud.<\/p>\r\n<p data-path-to-node=\"8\">&nbsp;<\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            },
                            {
                                "id": "429",
                                "nombre": "Filosofía DES del modelo ferroviario",
                                "texto": "<h3>Filosof&iacute;a DES del modelo ferroviario<\/h3>\r\n<p>El modelo ferroviario de GPSS-Plus no simula el movimiento continuo de los trenes.<\/p>\r\n<p>Simula &uacute;nicamente la validez temporal de las autorizaciones de circulaci&oacute;n.<\/p>\r\n<p>Esta diferencia es fundamental.<\/p>\r\n<p>En un motor imperativo cl&aacute;sico, el sistema normalmente:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>consulta posiciones continuamente,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>detecta colisiones,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>eval&uacute;a distancias,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>y recalcula estados en cada ciclo.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>En este modelo no existe ning&uacute;n escaneo global de la red.<\/p>\r\n<p>El CTC no &quot;vigila&quot; trenes.<\/p>\r\n<p>Los trenes tampoco preguntan constantemente si pueden avanzar.<\/p>\r\n<p>Toda la l&oacute;gica se basa exclusivamente en eventos discretos que invalidan el estado anterior del sistema.<\/p>\r\n<h2>El estado s&oacute;lo cambia cuando ocurre un evento<\/h2>\r\n<p>Mientras nada cambie:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>ning&uacute;n c&aacute;lculo es necesario,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>ning&uacute;n cant&oacute;n se reeval&uacute;a,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>y ning&uacute;n tren consume CPU adicional.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>El sistema permanece estable hasta que ocurre uno de estos eventos:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>un tren invade un nuevo cant&oacute;n,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>una reserva deja de ser v&aacute;lida,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>una aguja debe cambiar,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>o una telemetr&iacute;a modifica el estado global de permisos.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>S&oacute;lo entonces el CTC vuelve a computar.<\/p>\r\n<h2>Telemetr&iacute;a como mecanismo de causalidad<\/h2>\r\n<p>La telemetr&iacute;a no es un mecanismo visual ni estad&iacute;stico.<\/p>\r\n<p>Es el disparador causal del algoritmo.<\/p>\r\n<p>El CTC &uacute;nicamente conoce el estado de la red a trav&eacute;s de los eventos emitidos por los propios trenes.<\/p>\r\n<p>Si un tren no informa:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>el CTC no libera cantones,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>las reservas permanecen activas,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>y el sistema conserva el &uacute;ltimo estado v&aacute;lido conocido.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>Por tanto, la consistencia de la red emerge del intercambio de eventos y no de una supervisi&oacute;n continua.<\/p>\r\n<h3>Reserva predictiva en lugar de control continuo<\/h3>\r\n<p>El sistema tampoco controla f&iacute;sicamente la conducci&oacute;n del tren.<\/p>\r\n<p>El CTC &uacute;nicamente concede o deniega ventanas futuras de ocupaci&oacute;n.<\/p>\r\n<p>Cada tren:<\/p>\r\n<ul>\r\n    <li>\r\n    <p>recibe permisos,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>actualiza su m&aacute;quina de estados,<\/p>\r\n    <\/li>\r\n    <li>\r\n    <p>y resuelve localmente su comportamiento cinem&aacute;tico.<\/p>\r\n    <\/li>\r\n<\/ul>\r\n<p>El algoritmo no calcula trayectorias paso a paso.<\/p>\r\n<p>Calcula cu&aacute;ndo una autorizaci&oacute;n deja de ser v&aacute;lida.<\/p>\r\n<h3>Principio fundamental<\/h3>\r\n<p>El motor no simula conducci&oacute;n.<\/p>\r\n<p>Simula cambios de validez sobre permisos de ocupaci&oacute;n.<\/p>\r\n<p>&nbsp;<\/p>",
                                "descripcion": null,
                                "parametros": null,
                                "parametros_json": null,
                                "ejemplo": null,
                                "hijos": []
                            }
                        ]
                    }
                ]
            }
        ]
    },
    {
        "id": "363",
        "nombre": "Agradecimientos",
        "texto": "<h3 data-path-to-node=\"3\"><b data-path-to-node=\"3\" data-index-in-node=\"0\">Agradecimientos<\/b><\/h3>\r\n<p data-path-to-node=\"4\">Este proyecto no habr&iacute;a sido posible sin un sinfin de colaboradores. Mi m&aacute;s sincero agradecimiento a:<\/p>\r\n<ul>\r\n    <li>Geoffrey Gordon (GPSS Cl&aacute;sico): El genio detr&aacute;s del General Purpose Simulation System. Gracias por sentar las bases de la simulaci&oacute;n de eventos discretos y por ense&ntilde;arnos que el mundo se puede entender a trav&eacute;s de bloques, transacciones y l&oacute;gica pura. Esta evoluci&oacute;n se ha realizado con todo respeto y admiraci&oacute;n.<\/li>\r\n    <li>Math.js: El motor dentro del motor. Sin su capacidad de c&aacute;lculo, no habr&iacute;a ni solver, ni traductor matem&aacute;tico, ni derivadas, ni SNA... Impresiona ver tanta muestra gratuita de ingenio. Es el alma matem&aacute;tica de todo este proyecto.<\/li>\r\n    <li>Bootstrap, Monaco Editor, three.js: Por formar la interfaz; aportando la estructura visual y una experiencia intuitiva y legible.<\/li>\r\n    <li>A las IA: Por actuar como esos &quot;becarios&quot; de capacidad inagotable y precisi&oacute;n asombrosa. Han sido el copiloto perfecto en el todo el desarrollo, demostrando que la inteligencia artificial es un multiplicador de fuerzas impresionante.<\/li>\r\n    <li>Los que nos aguantan: Por estar siempre a nuestro lado cuando nos encerremos delante de la pantalla o les contamos teor&iacute;as que no entienden como si les interesase.<\/li>\r\n<\/ul>\r\n<p data-path-to-node=\"5,3,0\">Antonio S&aacute;nchez y resto del equipo. Enero de 2026<\/p>",
        "descripcion": "",
        "parametros": "",
        "parametros_json": "",
        "ejemplo": "",
        "hijos": []
    }
]