{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GPSS2DEVS Example: Simple Store\n",
    "\n",
    "In order to show a transformation from `GPSS` to `DEVS`, let's take a look at a simple example.\n",
    "\n",
    "Assume we have a store with a single cash registry. Every minute, a person enters the store. Each person takes 15 minutes to buy their groceries. The checkout takes another two minutes. Let's say we're interested in the waiting line that will be formed at the cash registry. In `GPSS`, this can be modelled as follows:\n",
    "\n",
    "```\n",
    "       GENERATE   1,,,,1   ; People entering the store.\n",
    "       ADVANCE    15       ; Shopping takes 15 minutes.\n",
    "       QUEUE      LINE     ; Wait in line before the registry.\n",
    "       SEIZE      CASH     ; There is but one cash registry.\n",
    "       DEPART     LINE     ; The person is not in the queue anymore!\n",
    "       ADVANCE    2        ; Checkout takes 2 minutes.\n",
    "       RELEASE    CASH     ; No need to keep the store clerk busy.\n",
    "       TERMINATE           ; People exit the store.\n",
    "       \n",
    "       \n",
    "       GENERATE   1        ; Make sure the simulation ends after a\n",
    "       TERMINATE  1        ;  certain time period.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Following the `GPSS2DEVS` transformation rules, each block is replaced with a structure containing their `Python(P)DEVS` counterpart and an additional helper block that is placed between each connection (after the block, except in the case of the `TERMINATE` block).\n",
    "\n",
    "This gives us the following code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/red/Documents/2019-2020/Thesis/src/pypdevsbbl/extra/rngstreams/__init__.py:148: UserWarning: /home/red/Documents/2019-2020/Thesis/src/dependencies/RngStreams/c/librngstreams.so: cannot open shared object file: No such file or directory. Cannot load RngStreams. Using 'random' module instead.\n",
      "  warnings.warn(\"%s. Cannot load RngStreams. Using 'random' module instead.\" % str(e))\n"
     ]
    }
   ],
   "source": [
    "from pypdevsbbl.domain.gpss import GPSS2DEVS, PREFIX_DLY\n",
    "\n",
    "class Model(GPSS2DEVS):\n",
    "    def __init__(self):\n",
    "        super().__init__(\"Store\", 2)\n",
    "        self.createFacility(\"CASH\")\n",
    "        self.createQueue(\"LINE\")\n",
    "        \n",
    "        self.GENERATE(\"L0\", dist=lambda x: 1, priority=1)\n",
    "        self.ADVANCE(\"L1\", \"L0\", dist=lambda x: 15)\n",
    "        self.QUEUE(\"L2\", \"LINE\")\n",
    "        self.SEIZE(\"L3\", \"CASH\")\n",
    "        self.DEPART(\"L4\", \"LINE\")\n",
    "        self.ADVANCE(\"L5\", \"L4\", dist=lambda x: 2)\n",
    "        self.RELEASE(\"L6\", \"CASH\")\n",
    "        self.TERMINATE(\"L7\")\n",
    "        \n",
    "        self.GENERATE(\"L8\", dist=lambda x: 1)\n",
    "        self.TERMINATE(\"L9\", \"1\")\n",
    "        \n",
    "        self.connect(\"L0\", \"L1\", True)\n",
    "        self.connect(\"L1\", \"L2\")\n",
    "        self.connect(\"L2\", \"L3\")\n",
    "        self.connect(\"L3\", \"L4\")\n",
    "        self.connect(\"L4\", \"L5\", True)\n",
    "        self.connect(\"L5\", \"L6\")\n",
    "        self.connect(\"L6\", \"L7\")\n",
    "        \n",
    "        self.connect(\"L8\", \"L9\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quite quaint, ain't it?\n",
    "\n",
    "Now, let's simulate the store for 2 hours (= 120 minutes). Note that we were interested in the queue `LINE`, so we'll output its values after the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "       name:  CASH\n",
      "      entry:  53\n",
      "       util:  0.8666666666666667\n",
      "    transit:  1.9622641509433962\n",
      "      owner:  Transaction(104, {'M1': 53.0, 'PR': 1})\n",
      "      inter:  False\n",
      "       busy:  True\n",
      "--------------------\n",
      "       name:  LINE\n",
      "        max:  52\n",
      "       avgc:  22.533333333333335\n",
      "      entry:  105\n",
      "      zeros:  1\n",
      "    zeropct:  0.9523809523809524\n",
      "    transit:  25.752380952380953\n",
      "   ztransit:  26.0\n",
      "    current:  52\n"
     ]
    }
   ],
   "source": [
    "from pypdevs.simulator import Simulator\n",
    "\n",
    "time = 120\n",
    "\n",
    "model = Model()\n",
    "sim = Simulator(model)\n",
    "sim.setClassicDEVS()\n",
    "sim.setTerminationCondition(lambda _, m: m.controller().finished(time))\n",
    "# sim.setVerbose(None)\n",
    "sim.simulate()\n",
    "\n",
    "for k, v in model.storage(\"CASH\").summary(time).items():\n",
    "    print(k.rjust(11) + \":\", v, sep='  ')\n",
    "print(\"--------------------\")\n",
    "for k, v in model.queue(\"LINE\").summary(time).items():\n",
    "    print(k.rjust(11) + \":\", v, sep='  ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's compare this to the results we obtain from _GPSS World_ :\n",
    "```\n",
    "FACILITY         ENTRIES  UTIL.   AVE. TIME AVAIL. OWNER PEND INTER RETRY DELAY\n",
    " CASH                53    0.867       1.962  1      105    0    0     0     52\n",
    "\n",
    "\n",
    "QUEUE              MAX CONT. ENTRY ENTRY(0) AVE.CONT. AVE.TIME   AVE.(-0) RETRY\n",
    " LINE               53   52    105      1    22.533     25.752     26.000   0\n",
    "```\n",
    "\n",
    "As you can see, we obtain the exact same values for this example. Note, `Python(P)DEVS` **includes** the termination time, hence the above values were obtained by running the `GPSS` code until the end of the time instance (i.e. by setting a higher priority to the transactions in the store compared to the counter).\n",
    "\n",
    "If we take a look at the `CASH` facility, we notice that only the `owner`'s identifier yields a different result. The values correspond as follows:\n",
    "\n",
    "| summary key  | _GPSS World_ key | description |\n",
    "|:---:|:---:|:---|\n",
    "| `name` | `FACILITY` | The name of the facility that is summarized. |\n",
    "| `entry` | `ENTRIES` | The total amount of items that entered the facility. |\n",
    "| `util` | `UTIL.` | The utilization of the facility. |\n",
    "| `transit` | `AVE.TIME` | The average transit time in the facility. |\n",
    "| `owner` | `OWNER` | The transaction that currently has access to the facility. |\n",
    "| `inter` | `INTER` | Whether or not the facility was interrupted. |\n",
    "| `busy` | -- | Whether or not the facility is currently claimed by an owner. |\n",
    "\n",
    "Looking at the `LINE`'s summary, its values follow the following correlation:\n",
    "\n",
    "| summary key  | _GPSS World_ key | description |\n",
    "|:---:|:---:|:---|\n",
    "| `name` | `QUEUE` | The name of the `QUEUE` that is summarized. |\n",
    "| `max` | `MAX` | The maximal amount of contents in the `QUEUE`. |\n",
    "| `avgc` | `AVE.CONT.` | The average content of the queue.<br/>This is the ratio of the total queueing time to the total simulation time. |\n",
    "| `entry` | `ENTRY` | The total amount of items that entered the `QUEUE` |\n",
    "| `zeros` | `ENTRY(0)` | The total amount of items that entered and left the `QUEUE` at the same time. |\n",
    "| `zeropct` | -- | `zeros`, expressed as a percent of `entry`. |\n",
    "| `transit` | `AVE.TIME` | The average transit time in the `QUEUE`. |\n",
    "| `ztransit` | `AVE.(-0)` | The average transit time in the `QUEUE` without the items that spend no time. |\n",
    "| `current` | `CONT.` | The current `QUEUE` contents, i.e. the amount of items there currently inside. |\n",
    "| -- | `RETRY` | The amount of retries. |"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
