{"id":1017,"date":"2016-09-28T21:17:16","date_gmt":"2016-09-29T02:17:16","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1017"},"modified":"2017-02-03T14:42:59","modified_gmt":"2017-02-03T19:42:59","slug":"a-simple-discrete-event-simulation-part-17","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/09\/28\/a-simple-discrete-event-simulation-part-17\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 17"},"content":{"rendered":"<p>Today I added an animation to the simulation.  There are a <em>lot<\/em> of things that could be said about this, and I&#8217;ll go into just a few of them today.  At this point I wanted to get some kind of hack working that would give you something to look at; we can adjust from there.  Here it is.  You have to click on the step button to get it to step through the simulation.  The internal activities are listed in the text area below the animation while most of the same information is being shown on the canvas.  When the generated entities are activated for the first time I show them being &#8220;spit&#8221; into and out of view.  The simulation ends at time 360.0, so keep clicking! <\/p>\n<p><iframe loading=\"lazy\" width=\"400px\" height=\"700px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20160928.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>By the way, you&#8217;ll have to refresh your browser to start over.<\/p>\n<p>So here are a few comments.<\/p>\n<p>I&#8217;m trying to interleave an animation mechanism with pseudo real-time features with a simulation where individual steps can be accomplished very, very quickly.  This turns out to be a bit of a problem.  In previous lives I didn&#8217;t encounter this issue.<\/p>\n<p>When I was doing continuous simulations I just did all the calculations for each time step, drew or updated the screen as needed, sometimes with page-flipping to keep it clean, and went about my business.  I always made my simulations interactive; the user could always pause (or freeze) at any point, make modifications as desired, and continue on.  Alternatively, real-time inputs from users and physical devices in the field changed things, but the system would just react and move on.  The time steps in the systems I worked on ranged from a quarter of a second up to forty seconds, depending on the amount of calculation that had to be performed, the size of the simulation being run, and the speed of the computer (and to a lesser degree the speed of the code both as written by the author and compiled by the compiler).<\/p>\n<p>All of the discrete-event simulations I did were fire-and-forget.  That is, you started them, let them run to completion, and then you looked at the results.  If an animation was generated it was (usually) created from a log file that was written out as the simulation ran.  As such there was (generally) no ability to change the simulation while it was running.<\/p>\n<p>The first discrete-event simulation system I wrote was built with <a href=\"http:\/\/www.wolverinesoftware.com\/SLXOverview.htm\">SLX<\/a> (Simulation Language with eXtensibility).  It is a C-based language with built-in discrete event capabilities.  (It hides all the ugly stuff I have to expose when doing the same things in JavaScript, which is a silly language for such an exercise except that it&#8217;s so easy to make live demos for the web!)  The SLX code was made to write statements out to a log file that could be read by a companion product called Proof, which has the virtue of being simple, terse, and stupidly fast.  (These products have the vice of being somewhat expensive and protected by hardware dongle.)<\/p>\n<p>If you want to do a discrete-event simulation that generates an ongoing display as it runs you have a couple of options.  One is that you simply generate or update frames after some number of operations.  If you choose a small number of operations you may end up generating frames more quickly than the graphic system can draw them.  If you choose a large number of operations you may skip frames or otherwise get out of sync with the graphics system.  That might not actually be a problem, but it&#8217;s definitely something you want to be aware of and decide to do as a conscious choice.  Like Vladimir Nabokov said about his characters being galley slaves, meaning he exercised total control over what they did and what their actions represented while other writers spoke of characters taking on lives of their own in their imaginations, the goal is to understand and be purposeful, always.<\/p>\n<p>That discussion applies whether you are running a simulation (or animation) slower than real time, faster than real time, or in sync with real time.  If you want to run in sync with real time, you essentially put wait states into the future events queue that, when pulled off the queue, cause the simulation to wait until the computer&#8217;s clock reaches the specified time.  In order to make this work, obviously, you have to make sure that the calculations being done in each time span reach completion before the end of the time span.<\/p>\n<p>What I will ultimately end up doing is embedding the movement of the entities (the green circles) into the same discrete-event mechanism that everything else is in.<\/p>\n<p>Last but not least I haven&#8217;t shared the whole project for a while, so here it is in all its ugly-but-working glory.  The point of these exercises isn&#8217;t to show off polished, web-ready production code, but to show the thought processes associated with these projects.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n<!doctype html>\r\n<html>\r\n<head>\r\n    <title>Animation Demo<\/title>\r\n\r\n    <meta charset=\"utf-8\" \/>\r\n    <meta http-equiv=\"Content-type\" content=\"text\/html; charset=utf-8\" \/>\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\r\n\r\n  <style>\r\n    body {\r\n      background-color: #FFFFFF;\r\n    }\r\n    #simProgress {\r\n      width: 378px;\r\n      height: 300px;\r\n      font-size: 0.8em;\r\n    }\r\n    #simClock {\r\n      width: 384px;\r\n      height: 30px;\r\n      background-color: #EEEEEE;    \r\n    }\r\n  <\/style> \r\n<\/head>\r\n\r\n<body>\r\n  <div id=\"wrapper\">\r\n    <canvas id=\"bcanimation\" height=\"250px\" width=\"380px\">The HTML5 Canvas object is not supported on your browser.<\/canvas>\r\n    <br \/>\r\n    <button id=\"step\" onmouseup=\"stepClick()\" style=\"height: 50px; width: 384px\">Step<\/button>\r\n  <\/div>\r\n  <!--p id=\"stuff\"><\/p-->\r\n  <hr \/>\r\n  <div id=\"simClock\"><\/div>\r\n  <textarea id=\"simProgress\"><\/textarea>\r\n  \r\n  <script>\r\n\/*  if (!Array.prototype.forEach)\r\n   {\r\n      console.log(\"array prototype needed to be defined\");\r\n      Array.prototype.forEach = function(fun \/*, thisp*\/ \/*)\r\n      {\r\n         var len = this.length;\r\n         \r\n         if (typeof fun != \"function\")\r\n         throw new TypeError();\r\n         \r\n         var thisp = arguments[1];\r\n         for (var i = 0; i < len; i++)\r\n         {\r\n            if (i in this)\r\n            fun.call(thisp, this[i], i, this);\r\n         }\r\n      };\r\n   }  \r\n*\/\r\n\r\n\/\/***************************************************************************************\r\n\/\/*** utility and statistical functions for Discrete-Event Simulation Framework\r\n\/\/***************************************************************************************    \r\n    function factorial(inputValue) {\r\n      var input = Math.floor(inputValue);\r\n      if (input < 0) {\r\n        return -1;  \/\/error condition\r\n      } else if (input <= 1) {\r\n        return 1;\r\n      } else {\r\n        var result = 1;\r\n        for (i=2; i<=input; i++) {\r\n          result *= i;\r\n        }\r\n      }\r\n      return result;\r\n    };  \/\/factorial\r\n        \r\n    function poissonSingle(arrivalsPerInterval,interval,occurrences) {\r\n      \/\/sanity check the input\r\n      if (arrivalsPerInterval <= 0.0) {arrivalsPerInterval = 1.0;}\r\n      if (interval <= 0.0) {interval = 1.0;}\r\n      if (occurrences <= 0) {occurrences = 0;}\r\n      \r\n\/*      \/\/the clear way\r\n      var lambda = arrivalsPerInterval \/ interval;\r\n      var k = occurrences;\r\n      var lambda_K = Math.pow(lambda,k);\r\n      var e_minusLambda = Math.exp(-lambda);\r\n      var kFact = factorial(k);\r\n      var result = lambda_K * e_minusLambda \/ kFact;\r\n      return result;  *\/\r\n      \r\n      \/\/the faster way\r\n      lambda = arrivalsPerInterval \/ interval;\r\n      return ((Math.pow(lambda,occurrences)) * (Math.exp(-lambda)) \/ factorial(occurrences));\r\n    }  \/\/poissonSingle\r\n\r\n    function poissonCumulative(arrivalsPerInterval,interval) {\r\n      \/\/both numbers should be positive, do not need to be integers\r\n      if (arrivalsPerInterval <= 0.0) {arrivalsPerInterval = 1.0;}\r\n      if (interval <= 0.0) {interval = 1.0;}\r\n      var cumulative = 0.0;\r\n      var count = 0;\r\n      var cumulations = [];\r\n      while (cumulative < 0.9999999999) {\r\n        var next = poissonSingle(arrivalsPerInterval,interval,count);\r\n        cumulative += next;\r\n        cumulations[count] = cumulative;\r\n        count++;\r\n      }\r\n      cumulations[count] = 1.0;\r\n      return cumulations;\r\n    }  \/\/poissonCumulative\r\n    \r\n    function poissonArrivals(randomValue,cumulation) {\r\n      \/\/expect 0.0 <= randomValue < 1.0, hack to condition input\r\n      var input = randomValue;\r\n      if (input < 0.0) {\r\n        input = 0.0;\r\n      } else if (input >= 1.0) {\r\n        input = 0.9999999999;\r\n      }\r\n      var index = 0;\r\n      while (cumulation[index] < input) {\r\n        index++;\r\n      }\r\n      return index;\r\n    }\r\n    \r\n\/*    var c = poissonCumulative(3,1);\r\n    var r = 0.999999999999999;\r\n    var a = poissonArrivals(r,c);\r\n    alert(a);  \/\/result is 20\r\n    r = 0.5;\r\n    a = poissonArrivals(r,c);\r\n    alert(a);  \/\/result is 3\r\n    r = Math.random();\r\n    a = poissonArrivals(r,c);\r\n    alert(a);  \/\/result is ?\r\n*\/    \r\n    \r\n\/\/    alert(poissonCumulative(6,1));\r\n\/*    alert(poissonSingle(6,1,0));\r\n    alert(poissonSingle(6,1,1));\r\n    alert(poissonSingle(6,1,2));\r\n    alert(poissonSingle(6,1,3));\r\n    alert(poissonSingle(6,1,4));\r\n    alert(poissonSingle(6,1,5));\r\n    alert(poissonSingle(6,1,6));\r\n    alert(poissonSingle(6,1,7));\r\n    alert(poissonSingle(6,1,8));\r\n    alert(poissonSingle(6,1,9));\r\n*\/\r\n\r\n\/\/***************************************************************************************\r\n\/\/*** some global variables (Ick, I know...)\r\n\/\/***************************************************************************************\r\n    var displayStartTime = 0.0;\r\n    var displayCurrentTime = 0.0;\r\n    var displayEndTime = 0.0;\r\n    var displayEntitiesScheduled = 0;\r\n    var displayEntitiesGenerated = 0;\r\n    var displayEntityID = 0;\r\n    var initBallFlag = false;\r\n    var displayActivity = \"\";\r\n\/\/***************************************************************************************\r\n\/\/*** start Discrete-Event Simulation Framework\r\n\/\/***************************************************************************************\r\n    \/\/global simulation clock\r\n    var globalSimClock = 0.0;  \/\/starts at zero, units to be specified\r\n    var globalSimUnits = \"minutes\";\r\n    var globalInsertTime = 0.0;  \/\/TODO: review whether this is needed or useful\r\n\r\n    var simClockElement = document.getElementById(\"simClock\");\r\n    function updateSimClock(time) {\r\n      globalSimClock = time;\r\n      simClockElement.innerHTML = time.toFixed(6) + \" \" + globalSimUnits;\r\n    };\r\n    updateSimClock(0.0);\r\n    \r\n    var simProgressText = \"\";\r\n    var simProgressElement = document.getElementById(\"simProgress\");\r\n    function displayProgressText(textAdd) {\r\n      simProgressText += textAdd + \"\\n\";  \/\/+ \"<br \/>\";\r\n      simProgressElement.innerHTML = simProgressText;\r\n      simProgressElement.scrollTop = simProgressElement.scrollHeight;\r\n    };\r\n    function clearProgressText() {\r\n      simProgressText = \"\";\r\n      simProgressElement.innerHTML = simProgressText;\r\n    };\r\n    clearProgressText();\r\n    \r\n    var globalIDCounter = 0;\r\n    function getNewID() {\r\n      return ++globalIDCounter;\r\n    };\r\n\r\n    var globalExecutionCount = 0;\r\n    function bumpGlobalExecutionCount() {\r\n      globalExecutionCount++;\r\n    }\r\n    \r\n    \/\/current events queue item\r\n    function currentEventItem(entity) {\r\n      this.entity = entity;\r\n      this.checkBlockingCondition = function() {\r\n        return this.entity.checkBlockingCondition(); \/\/true if blocking condition met\r\n      }\r\n    };  \/\/currentEventItem\r\n\r\n    \/\/the current events queue definition, a list of current events queue items\r\n    function currentEventsQueue() {\r\n      this.ceq = new Array();\r\n      this.ceqSize = 0;\r\n      this.addCeqItem = function(ceqItem) {\r\n        this.ceq.push(ceqItem);\r\n        this.ceqSize++;\r\n      }\r\n      \/\/this.removeCeqItem = function(ceqItem) {\r\n      \/\/  var index = this.ceq.indexOf(ceqItem);\r\n      \/\/  this.ceq.splice(index,1);\r\n      \/\/  this.ceqSize--;\r\n      \/\/}\r\n      this.processCeq = function() {\r\n        var trueBlockFound;\r\n        do {\r\n          var i = 0;\r\n          trueBlockFound = false;\r\n          while ((i < this.ceqSize) &#038;&#038; (!trueBlockFound)) {\r\n            if (this.ceq[i].checkBlockingCondition()) {\r\n              this.ceq.splice(i,1);\r\n              this.ceqSize--;\r\n              trueBlockFound = true;\r\n            } else {\r\n              i++;\r\n            }\r\n          }\r\n        } while (trueBlockFound);\r\n      }\r\n    };\r\n    \r\n    \/\/the current events queue instantiation\r\n    ceq = new currentEventsQueue();\r\n    \r\n    \/\/future events queue item\r\n    function futureEventItem(time,type,entity) {\r\n      this.activationTime = 0.0;\r\n      if (type == \"advance\") {\r\n        this.activationTime = globalSimClock + time;  \/\/activate 'time' from now\r\n      } else if (type == \"absolute\") {\r\n        if (time >= globalSimClock) {\r\n          this.activationTime = time;                 \/\/activate at specified absolute time, if in future\r\n        } else {\r\n          displayProgressText(\"Invalid time past time supplied A.  Entity ID: \"+entity.entityID+\" Current Time: \"+globalSimClock+\" Specified Time: \"+time);  \/\/alert on invalid time\r\n          \/\/alert(\"Invalid time past time supplied.  Entity ID: \"+entity.entityID+\" Current Time: \"+globalSimClock+\" Speficied Time: \"+time);  \/\/alert on invalid time\r\n        }\r\n      }\r\n      this.entity = entity;\r\n      this.getActivationTime = function() {\r\n        return this.activationTime;\r\n      };\r\n      this.update = function(time,type) {\r\n        if (type == \"advance\") {\r\n          this.activationTime = globalSimClock + time;  \/\/activate 'time' from now\r\n        } else if (type == \"absolute\") {\r\n          if (time >= globalSimClock) {\r\n            this.activationTime = time;                 \/\/activate at specified absolute time, if in future\r\n          } else {\r\n            displayProgressText(\"Invalid time past time supplied B.  Entity ID: \"+entity.entityID+\" Current Time: \"+globalSimClock+\" Specified Time: \"+time);  \/\/alert on invalid time\r\n            \/\/alert(\"Invalid time past time supplied.  Entity ID: \"+entity.entityID+\" Current Time: \"+globalSimClock+\" Speficied Time: \"+time);  \/\/alert on invalid time\r\n          }\r\n        }\r\n      };\r\n      this.reportItem = function() {\r\n        console.log(\"Time: \"+this.activationTime+\" ID: \"+this.entity.entityID);\r\n      };\r\n    };  \/\/futureEventItem\r\n    \r\n    var feqCurrent;  \/\/global feqItem\r\n    \r\n    function futureEventsQueue() {\r\n      this.feq = new Array();\r\n      this.feqSize = 0;\r\n      this.insertTime = 0.0;\r\n      this.findLaterTime = function(item) {\r\n        \/\/updateSimClock(10.0);\r\n        var a = item.getActivationTime();\r\n        var b = globalInsertTime; \/\/this.insertTime;  why is the scope of \"this\" messed up here?  window and not queue\r\n        var result = a > b;\r\n        return result;\r\n      };\r\n      this.insertItem = function(time,type,entity) {\r\n        \/\/create futureEventItem\r\n        var feqItem = new futureEventItem(time,type,entity);\r\n        this.insertTime = feqItem.getActivationTime();\r\n        globalInsertTime = this.insertTime;\r\n        if (this.feqSize == 0) {  \r\n          this.feq[0] = feqItem;\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        } else {\r\n          \/\/find index of feq item to insert before\r\n          var insertIndex = this.feq.findIndex(this.findLaterTime);\r\n          \/\/insert the element\r\n          if (insertIndex < 0) {\r\n            insertIndex = this.feq.length;\r\n          }\r\n          this.feq.splice(insertIndex,0,feqItem);\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        }\r\n      };\r\n      this.newItem = function(time,entity) {\r\n        \/\/create futureEventItem\r\n        var feqItem = new futureEventItem(time,\"absolute\",entity);\r\n        this.insertTime = feqItem.getActivationTime();\r\n        globalInsertTime = this.insertTime;\r\n        if (this.feqSize == 0) {  \r\n          this.feq[0] = feqItem;\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        } else {\r\n          \/\/find index of feq item to insert before\r\n          var insertIndex = this.feq.findIndex(this.findLaterTime);\r\n          \/\/insert the element\r\n          if (insertIndex < 0) {\r\n            insertIndex = this.feq.length;\r\n          }\r\n          this.feq.splice(insertIndex,0,feqItem);\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        }\r\n      };\r\n      this.getFirstItem = function() {\r\n        var feqItem = this.feq.splice(0,1);\r\n        if (feqItem.length) {\r\n          var t = feqItem[0].getActivationTime();\r\n          updateSimClock(t);\r\n          this.feqSize--;\r\n          return feqItem;\r\n        } else {\r\n          console.log(\"no items in FEQ to retrieve \"+this.feqSize)\r\n        }\r\n      };\r\n      this.insertExistingItem = function(feqItem) {\r\n        this.insertTime = feqItem.getActivationTime();\r\n        globalInsertTime = this.insertTime;\r\n        if (this.feqSize == 0) {  \r\n          this.feq[0] = feqItem;\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        } else {\r\n          \/\/find index of feq item to insert before\r\n          var insertIndex = this.feq.findIndex(this.findLaterTime);\r\n          \/\/insert the element\r\n          if (insertIndex < 0) {\r\n            insertIndex = this.feq.length;\r\n          }\r\n          this.feq.splice(insertIndex,0,feqItem);\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        }\r\n      };\r\n      this.insertCurrent = function() {\r\n        this.insertTime = feqCurrent.getActivationTime();\r\n        globalInsertTime = this.insertTime;\r\n        if (this.feqSize == 0) {  \r\n          this.feq[0] = feqCurrent;\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        } else {\r\n          \/\/find index of feq item to insert before\r\n          var insertIndex = this.feq.findIndex(this.findLaterTime);\r\n          \/\/insert the element\r\n          if (insertIndex < 0) {\r\n            insertIndex = this.feq.length;\r\n          }\r\n          this.feq.splice(insertIndex,0,feqCurrent);\r\n          this.feqSize++;\r\n          \/\/\/console.log(\"Array size: \"+this.feq.length);\r\n        }\r\n      };\r\n      this.reportSize = function() {\r\n        \/\/\/console.log(\"Number of events in queue: \"+this.feqSize);\r\n      };\r\n\r\n    };  \/\/futureEventsQueue\r\n    \r\n    function reportItemInfo(element, index, arr) {\r\n      \/\/globalSimClock = 10.0;    \r\n      console.log(\"reportItemInfo: element: \"+element+\" index: \"+index+\" arr: \"+arr);\r\n      element.reportItem();\r\n    };\r\n    \r\n    \/\/initialize future events queue\r\n    feq = new futureEventsQueue();\r\n    feq.reportSize();  \/\/should be zero;\r\n\r\n    \/\/function advance(feqItem,byTime) {\r\n    function advance(byTime) {\r\n      feqCurrent.update(byTime,\"advance\");\r\n      \/\/feq.insertExistingItem(feqCurrent);\r\n      feq.insertCurrent();\r\n    };  \r\n\r\n    \/\/entity item 1\r\n    function entity1(initialTime,incrementTime,endTime) {\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.incrementTime = incrementTime;\r\n      this.endTime = endTime;\r\n      this.nextState = \"increment\";\r\n      feq.newItem(initialTime,this);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.activate = function() {\r\n        bumpGlobalExecutionCount();\r\n        if (this.nextState == \"increment\") {\r\n          displayProgressText(\"entity \"+this.entityID+\" updated at time \"+globalSimClock);\r\n          \/\/set values for diplay-------------\r\n          \/\/displayStartTime = globalSimClock;\r\n          displayCurrentTime = globalSimClock;\r\n          \/\/displayEndTime = globalSimClock + this.incrementTime;\r\n          \/\/displayEntitiesScheduled = arrivals;\r\n          displayEntitiesGenerated++;\r\n          displayEntityID = this.entityID;\r\n          initBallFlag = true;\r\n          displayActivity = \"new entity\";\r\n          \/\/----------------------------------\r\n          if (globalSimClock + this.incrementTime >= this.endTime) {\r\n            this.nextState = \"destroy\";\r\n          }\r\n          advance(this.incrementTime);\r\n        } else if (this.nextState == \"destroy\") {\r\n          displayProgressText(\"entity \"+this.entityID+\" terminated at time \"+globalSimClock);\r\n          \/\/set values for diplay-------------\r\n          \/\/displayStartTime = globalSimClock;\r\n          displayCurrentTime = globalSimClock;\r\n          \/\/displayEndTime = globalSimClock + this.incrementTime;\r\n          \/\/displayEntitiesScheduled = arrivals;\r\n          \/\/displayEntitiesGenerated++;\r\n          displayEntityID = this.entityID;\r\n          initBallFlag = false;\r\n          displayActivity = \"terminated\";\r\n          \/\/----------------------------------\r\n        } else {\r\n          alert(\"entity \"+this.entityID+\" went into undefined state\");\r\n          displayProgressText(\"entity \"+this.entityID+\" in undefined state at time \"+globalSimClock);\r\n        }\r\n      };  \/\/this.activate      \r\n    };  \/\/entity\r\n\r\n    function errorUndefinedAdvanceState(id,state) {\r\n      \/\/alert(\"entity \"+id+\" went into undefined state\");\r\n      displayProgressText(\"entity \"+id+\" in undefined state: \"+state+\", at time \"+globalSimClock);\r\n    } \r\n    \r\n    \/\/entity item 2\r\n    function entity2(initialTime,initialPosition,speed,endPosition) {\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.currentPosition = initialPosition;\r\n      this.speed = speed;\r\n      this.endPosition = endPosition;\r\n      this.nextState = \"go_forward\";\r\n      this.waitCountdown = 5;\r\n      feq.newItem(initialTime,this);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.go_forward = function() {\r\n          displayProgressText(\"entity \"+this.entityID+\" updated at time \"+globalSimClock+\" position: \"+this.currentPosition);\r\n          this.currentPosition += this.speed;\r\n          if (this.currentPosition >= this.endPosition) {\r\n            if (this.currentPosition > this.endPosition) {\r\n              this.currentPosition = this.endPosition;\r\n            }\r\n            this.nextState = \"wait\";\r\n          }\r\n          advance(7.01);      \r\n      }\r\n      this.wait = function() {\r\n          this.waitCountdown--;\r\n          displayProgressText(\"entity \"+this.entityID+\" waiting at time \"+globalSimClock+\" wait count: \"+this.waitCountdown);\r\n          if (this.waitCountdown <= 0.0) {\r\n            this.nextState = \"destroy\";\r\n          }\r\n          advance(7.01);      \r\n      }\r\n      this.destroy = function() {\r\n          displayProgressText(\"entity \"+this.entityID+\" terminated at time \"+globalSimClock);\r\n      }\r\n      this.activate = function() {\r\n        bumpGlobalExecutionCount();\r\n        if (this.nextState == \"go_forward\") {\r\n          this.go_forward();\r\n        } else if (this.nextState == \"wait\") {\r\n          this.wait();\r\n        } else if (this.nextState == \"destroy\") {\r\n          this.destroy();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }\r\n      };  \/\/this.activate      \r\n    };  \/\/entity2\r\n\r\n    \/\/entity item 2a\r\n    function entity2a(initialTime,initialPosition,speed,endPosition) {\r\n      var go_forward_ = 0;\r\n      var wait_ = 1;         \/\/define named index values for clarity\r\n      var destroy_ = 2;\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.currentPosition = initialPosition;\r\n      this.speed = speed;\r\n      this.endPosition = endPosition;\r\n      this.nextState = go_forward_;\r\n      this.waitCountdown = 5;\r\n      feq.newItem(initialTime,this);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.go_forward = function(entity) {\r\n          displayProgressText(\"entity \"+this.entityID+\" updated at time \"+globalSimClock+\" position: \"+this.currentPosition);\r\n          this.currentPosition += this.speed;\r\n          if (this.currentPosition >= this.endPosition) {\r\n            if (this.currentPosition > this.endPosition) {\r\n              this.currentPosition = this.endPosition;\r\n            }\r\n            this.nextState = wait_;\r\n          }\r\n          advance(7.01);      \r\n      }\r\n      this.wait = function() {\r\n          this.waitCountdown--;\r\n          displayProgressText(\"entity \"+this.entityID+\" waiting at time \"+globalSimClock+\" wait count: \"+this.waitCountdown);\r\n          if (this.waitCountdown <= 0.0) {\r\n            this.nextState = destroy_;\r\n          }\r\n          advance(7.01);      \r\n      }\r\n      this.destroy = function() {\r\n          displayProgressText(\"entity \"+this.entityID+\" terminated at time \"+globalSimClock);\r\n      }\r\n      \/\/define the array of functions here\r\n      this.activateArray = [this.go_forward,this.wait,this.destroy];\r\n      this.activate = function() {\r\n        bumpGlobalExecutionCount();\r\n        if ((this.nextState >= 0) && (this.nextState <= 2)) {\r\n          \/\/call the desired function this way\r\n          this.activateArray[this.nextState]();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }\r\n      };  \/\/this.activate      \r\n    };  \/\/entity2a\r\n\r\n    \/\/entity item 3\r\n    function entity3(initialTime,executionCount) {\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.executionCount = executionCount;\r\n      \/\/this.nextState = \"block1\";\r\n      \/\/feq.newItem(initialTime,this);\r\n      this.ceqItem = new currentEventItem(this);\r\n      ceq.addCeqItem(this.ceqItem);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.blockingCondition = 0;\r\n      this.checkBlockingCondition = function() {\r\n        if (this.blockingCondition < 0) {\r\n          return false;\r\n        } else if (this.blockingCondition == 0) {\r\n          var fred = globalExecutionCount % this.executionCount;\r\n          if (Math.abs(fred) <= 0.00001) {\r\n            displayProgressText(\"entity \"+this.entityID+\" reports at time \"+globalSimClock+\" executions: \"+globalExecutionCount);\r\n            return true;\r\n          } else {\r\n            return false;\r\n          }\r\n        } \r\n      }\r\n\/*      this.activate = function() {\r\n        if (this.nextState == \"block1\") {\r\n          displayProgressText(\"entity \"+this.entityID+\" checking blocks at time \"+globalSimClock+\" blockState: \"+this.blockingCondition);\r\n          ceq.addItem(this.ceqItem);\r\n        } else {\r\n          alert(\"entity \"+this.entityID+\" went into undefined state\");\r\n          displayProgressText(\"entity \"+this.entityID+\" in undefined state at time \"+globalSimClock);\r\n        }\r\n      };  \/\/this.activate      *\/\r\n    };  \/\/entity3\r\n\r\n    var conditionFlag = function(flag) {\r\n      this.flag = flag;\r\n      this.getFlag = function () {\r\n        return this.flag;\r\n      }\r\n      this.setFlag = function(flag) {\r\n        this.flag = flag;\r\n      }\r\n    }\r\n    var flag1 = new conditionFlag(false);\r\n    var flag2 = new conditionFlag(false);\r\n    \r\n    \/\/entity item 4\r\n    function entity4(initialTime,incrementTime,endTime,flagPointer) {\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.incrementTime = incrementTime;\r\n      this.endTime = endTime;\r\n      this.flagPointer = flagPointer;\r\n      this.nextState = \"increment\";\r\n      feq.newItem(initialTime,this);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.activate = function() {\r\n        bumpGlobalExecutionCount();\r\n        if (this.nextState == \"increment\") {\r\n          displayProgressText(\"entity \"+this.entityID+\" sets flag at time \"+globalSimClock+\" flag value: \"+this.flagPointer.getFlag()+\" flag1: \"+flag1.getFlag()+\" flag2: \"+flag2.getFlag());\r\n          if (globalSimClock + this.incrementTime >= this.endTime) {\r\n            this.nextState = \"destroy\";\r\n          }\r\n          advance(this.incrementTime);\r\n        } else if (this.nextState == \"destroy\") {\r\n          this.flagPointer.setFlag(true);\r\n          displayProgressText(\"entity \"+this.entityID+\" terminated at time \"+globalSimClock+\" flag value: \"+this.flagPointer.getFlag()+\" flag1: \"+flag1.getFlag()+\" flag2: \"+flag2.getFlag());\r\n        } else {\r\n          alert(\"entity \"+this.entityID+\" went into undefined state\");\r\n          displayProgressText(\"entity \"+this.entityID+\" in undefined state at time \"+globalSimClock);\r\n        }\r\n      };  \/\/this.activate      \r\n    };  \/\/entity\r\n\r\n    \/\/entity item 5\r\n    function entity5(initialTime,flagPointer) {\r\n      this.entityID = getNewID();\r\n      this.initialTime = initialTime;\r\n      this.flagPointer = flagPointer;\r\n      \/\/this.nextState = \"block1\";\r\n      \/\/feq.newItem(initialTime,this);\r\n      this.ceqItem = new currentEventItem(this);\r\n      ceq.addCeqItem(this.ceqItem);\r\n      displayProgressText(\"entity \"+this.entityID+\" created at time \"+globalSimClock);\r\n      this.blockingCondition = 0;\r\n      this.checkBlockingCondition = function() {\r\n        if (this.blockingCondition < 0) {\r\n          return false;\r\n        } else if (this.blockingCondition == 0) {\r\n          if (this.flagPointer.getFlag()) {\r\n            displayProgressText(\"entity \"+this.entityID+\" unblocks at time \"+globalSimClock+\" flag value: \"+this.flagPointer.getFlag());\r\n            return true;\r\n          } else {\r\n            displayProgressText(\"entity \"+this.entityID+\" still blocked at time \"+globalSimClock+\" flag value: \"+this.flagPointer.getFlag()+\" flag1: \"+flag1.getFlag()+\" flag2: \"+flag2.getFlag());\r\n            return false;\r\n          }\r\n        } \r\n      }\r\n    };  \/\/entity5\r\n\r\n    \/\/time to end the entire simulation\r\n    var endSimTime = 360.0;  \/\/time increments in minutes, run for 6 hours\r\n    \r\n    \/\/collection of all entities in the system\r\n    var setOfEntities = new Array();\r\n    \r\n    function compareNumeric(a,b) {\r\n      return a-b;\r\n    }\r\n    \r\n    \/\/Entry component\r\n    function entryComponent(scheduleBlockMinutes,scheduleArray) {\r\n      \/\/initially assume it just generates entity1s that disappear after a single cycle\r\n      this.entityID = getNewID();\r\n\r\n      this.initialTime = 0.0;\r\n      this.incrementTime = scheduleBlockMinutes;\r\n      this.arrayIndex = 0;\r\n      this.endTime = endSimTime;\r\n      this.nextState = \"increment\";\r\n      feq.newItem(globalSimClock,this);  \/\/assume all components created at time zero\r\n      this.generateNewEntity = function(start,increment,end) {\r\n        var newEntity = new entity1(start,increment,end);\r\n        setOfEntities.push(newEntity);\r\n      }\r\n      this.increment = function() {  \/\/should be called at the beginning of every half-hour block\r\n        \/\/get arrival count per array index\r\n        var arrivals = scheduleArray[this.arrayIndex];\r\n        if (arrivals > 0) {\r\n\/*          \/\/distribute arrivals evenly across the time span\r\n          var increment = scheduleBlockMinutes \/ (arrivals + 1);\r\n          var arrivalBump = 0.0;\r\n          for (var i=0; i<arrivals; i++) {\r\n            arrivalBump += increment;\r\n            this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);\r\n          }  *\/\r\n          \/\/distribute arrivals randomly across the time span\r\n\/*          for (var i=0; i<arrivals; i++) {\r\n            var arrivalBump = Math.random() * scheduleBlockMinutes;\r\n            this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);\r\n          } *\/\r\n          \/\/distribute arrivals randomly across the time span but have them arrive in sorted order by ID\r\n          var arrivalArray = [];\r\n          for (var i=0; i<arrivals; i++) {\r\n            arrivalArray[i] = Math.random() * scheduleBlockMinutes;\r\n          } \r\n          \/\/sort the array\r\n          arrivalArray.sort(compareNumeric);\r\n          \/\/generate the arrivals          \r\n          for (var i=0; i<arrivals; i++) {\r\n            var arrivalBump = arrivalArray[i];\r\n            this.generateNewEntity(globalSimClock + arrivalBump, 2.0, globalSimClock + arrivalBump + 1.0);            \r\n          } \r\n        }\r\n        this.arrayIndex++;\r\n        displayProgressText(\"Entry component \"+this.entityID+\" generates \"+arrivals+\" new entities at time \"+globalSimClock);\r\n        \/\/set values for diplay-------------\r\n        displayStartTime = globalSimClock;\r\n        displayCurrentTime = globalSimClock;\r\n        displayEndTime = globalSimClock + this.incrementTime;\r\n        displayEntitiesScheduled = arrivals;\r\n        displayEntitiesGenerated = 0;\r\n        displayEntityID = this.entityID;\r\n        displayActivity = \"Entry update\";\r\n        \/\/----------------------------------\r\n        if (globalSimClock + this.incrementTime >= this.endTime) {\r\n          this.nextState = \"destroy\";\r\n        }\r\n        advance(this.incrementTime);\r\n      }\r\n      this.destroy = function() {\r\n        \/\/this.flagPointer.setFlag(true);\r\n        displayProgressText(\"Entry component \"+this.entityID+\" terminated at time \"+globalSimClock);\r\n      }\r\n      this.activate = function() {\r\n        if (this.nextState == \"increment\") {\r\n          this.increment();\r\n        } else if (this.nextState == \"destroy\") {\r\n          this.destroy();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }      \r\n      }\r\n    };  \/\/entryComponent\r\n    \r\n    \/\/schedule for six hours of arrivals in half-hour blocks\r\n    var arrivalSchedule = [0,0,1,3,4,4,5,6,3,2,1,0]; \r\n    \r\n    var entry1 = new entryComponent(30.0,arrivalSchedule);\r\n    \r\n\/*    var entityA = new entity1(11.0,10.0,100.0);\r\n    var entityB = new entity1(13.0,13.0,100.0);\r\n    var entityC = new entity2a(12.0,-40,5,-1);\r\n    var entityD = new entity3(0.0,5);\r\n    \r\n    var entityE = new entity4(0.0,45.0,45.0,flag1);\r\n    var entityF = new entity4(0.0,63.7,63.7,flag2);\r\n    \r\n    var entityG = new entity5(0.0,flag1);\r\n    var entityH = new entity5(0.0,flag2);\r\n*\/\r\n    \/\/***** main event loop *************************************************************\r\n    var keepRunning = true;\r\n\/*    while (keepRunning) {\r\n      ceq.processCeq();\r\n    \r\n      var itemList = feq.getFirstItem();\r\n      \r\n      if (itemList) {\r\n        feqCurrent = itemList[0];\r\n        feqCurrent.entity.activate();\r\n      } else {\r\n        keepRunning = false;\r\n      }\r\n    }  \/\/while (keepRunning)\r\n*\/        \r\n\/\/***************************************************************************************\r\n\/\/*** end Discrete-Event Simulation Framework\r\n\/\/***************************************************************************************    \r\n  \r\n        \r\n\/\/***************************************************************************************\r\n\/\/*** begin animation section\r\n\/\/***************************************************************************************    \r\n    \/\/handle to animation mechanism\r\n    var requestID;\r\n    \/\/status flag\r\n    var running = false;\r\n  \r\n    \/\/animation object  \r\n    function Animation(width,height,id) {\r\n      this.id = id;\r\n      this.xDemo = 0.0;\r\n      this.yDemo = 0.0;\r\n      this.yVelocity = 0.0;\r\n      this.canvas = document.getElementById(id);\r\n      if (this.canvas) {  \r\n        \/\/element already exists\r\n        if (this.canvas.nodeName == \"CANVAS\") {\r\n          \/\/element *is* a canvas\r\n          this.width = this.canvas.width;\r\n          this.height = this.canvas.height;\r\n        } else {\r\n          \/\/element is *not* a canvas\r\n          alert(\"Error: Element you've linked to is *not* a canvas.\");\r\n        }\r\n      } else {\r\n        \/\/element does not already exist and must be created\r\n        this.width = width;\r\n        this.height = height;\r\n        this.canvas = document.createElement(\"canvas\");\r\n        this.canvas.id = this.id;\r\n        this.canvas.width = this.width;\r\n        this.canvas.height = this.height;\r\n        document.body.appendChild(this.canvas);\r\n      }\r\n      this.canvas.style.border = \"2px solid lime\";\r\n      this.canvas.style.margin = \"5px auto\";\r\n      this.ctx = this.canvas.getContext(\"2d\");\r\n      this.ctx.lineWidth = 1.0;\r\n      this.ctx.lineCap = \"butt\"; \/\/\"square\";\r\n      this.clearCanvas = function(color) {\r\n        this.ctx.fillStyle = color;\r\n        this.ctx.fillRect(0,0,this.width,this.height);\r\n      }\r\n      this.drawCircle = function(x,y,r,c) {\r\n        this.ctx.strokeStyle = c;\r\n        this.ctx.beginPath();\r\n        this.ctx.arc(x,y,r,0,2*Math.PI,true);\r\n        this.ctx.stroke();\r\n        this.ctx.fillStyle = c;\r\n        this.ctx.fill();\r\n      };\r\n      this.drawEntry = function(x,y,w,h,c) {\r\n        this.xLocation = x;\r\n        this.yLocation = y;\r\n        this.entryWidth = w;\r\n        this.entryHeight = h;\r\n        this.color = c;\r\n        this.clearCanvas(\"#000000\");\r\n        this.ctx.strokeStyle = c;\r\n        this.ctx.beginPath();\r\n        this.ctx.moveTo(x+0.5,y+0.5);\r\n        this.ctx.lineTo(x+w+0.5,y+0.5);\r\n        this.ctx.lineTo(x+w+0.5,y+h+0.5);\r\n        this.ctx.lineTo(x+0.5,y+h+0.5);\r\n        this.ctx.lineTo(x+0.5,y+0.5);\r\n        this.ctx.stroke();\r\n        this.ctx.font = \"12px Arial\";\r\n        this.ctx.fillStyle = \"#00FFFF\";\r\n        this.ctx.textAlign = \"left\";\r\n        this.ctx.fillText(\"Entry\",x+85,y+12);\r\n        this.ctx.textAlign = \"right\";\r\n        this.ctx.fillText(\"Start Time:\",x+78,y+24);\r\n        this.ctx.fillText(\"Current Time:\",x+78,y+36);\r\n        this.ctx.fillText(\"End Time:\",x+78,y+48);\r\n        this.ctx.fillText(\"# Entities:\",x+78,y+60);\r\n        this.ctx.fillText(\"# Remaining:\",x+78,y+72);\r\n        this.ctx.fillText(\"Entity ID:\",x+78,y+84);\r\n        this.ctx.fillText(\"Activity:\",x+78,y+96);\r\n      };\r\n      this.drawValues = function(start,current,end,numGenerated,numRemaining,ID,activity) {\r\n        this.ctx.font = \"12px Arial\";\r\n        this.ctx.fillStyle = \"#FF0000\";\r\n        this.ctx.textAlign = \"left\";\r\n        this.ctx.fillText(start.toFixed(5),this.xLocation+83,this.yLocation+24);\r\n        this.ctx.fillText(current.toFixed(5),this.xLocation+83,this.yLocation+36);\r\n        this.ctx.fillText(end.toFixed(5),this.xLocation+83,this.yLocation+48);\r\n        this.ctx.fillText(numGenerated.toFixed(0),this.xLocation+83,this.yLocation+60);\r\n        this.ctx.fillText(numRemaining.toFixed(0),this.xLocation+83,this.yLocation+72);\r\n        this.ctx.fillText(ID,this.xLocation+83,this.yLocation+84);\r\n        this.ctx.fillText(activity,this.xLocation+83,this.yLocation+96);\r\n      };\r\n      this.clearCanvas(\"#000000\");\r\n      this.initBall = function(x,y) {\r\n        this.xDemo = x;\r\n        this.yDemo = y;\r\n        this.yVelocity = 0.0;\r\n      }\r\n      this.yVelocity = 0.0;\r\n      this.demo2 = function() {\r\n        this.clearCanvas(\"#000000\");\r\n        this.drawEntry(20,20,150,100,\"#FFFF00\");\r\n        this.drawValues(\r\n          displayStartTime,\r\n          displayCurrentTime,\r\n          displayEndTime,\r\n          displayEntitiesScheduled,\r\n          (displayEntitiesScheduled-displayEntitiesGenerated),\r\n          displayEntityID,        \r\n          displayActivity\r\n        );\r\n        \/\/this.drawCircle(350.0,50.0,30.0,\"#FF0000\");\r\n        this.drawCircle(this.xDemo,this.yDemo,10.0,\"#00FF00\");\r\n        this.xDemo += 2.0;\r\n        this.yDemo += this.yVelocity;\r\n        this.yVelocity += 0.3;\r\n        if (this.yDemo > (this.height + 30)) {\r\n        \/\/if (this.yDemo > (this.height - 50)) {\r\n          running = false;\r\n          cancelAnimationFrame(requestID);\r\n        }\r\n      };\r\n      this.endMessage = function() {\r\n        this.ctx.font = \"12px Arial\";\r\n        this.ctx.fillStyle = \"#FF00FF\";\r\n        this.ctx.textAlign = \"left\";\r\n        this.ctx.fillText(\"Simulation has ended\",20.0,150.0);\r\n      }\r\n    };\r\n    var bcAnimation = new Animation(400,400,\"bcanimation\");\r\n    \r\n    bcAnimation.drawEntry(20,20,150,120,\"#FFFF00\");\r\n\/\/    bcAnimation.drawValues(180.0,203.765,210.0,4,1,27);\r\n    bcAnimation.initBall(180,70);\r\n\/\/    bcAnimation.demo2();\r\n    \r\n    \/\/running = true;\r\n    function animateCircle() {\r\n      bcAnimation.demo2();\r\n      if (running) {\r\n        requestID = window.requestAnimationFrame(animateCircle);\r\n      }\r\n    }\r\n    \/\/animateCircle();\r\n    \/\/requestID = window.requestAnimationFrame(animateCircle);\r\n    \r\n    var element = document.getElementById(\"step_button\");\r\n    element.innerHTML = \"Step\";\r\n    function stepClick() {\r\n      \/\/do an iteration of the main event loop from the DES section\r\n      if (keepRunning) {\r\n        ceq.processCeq();\r\n      \r\n        var itemList = feq.getFirstItem();\r\n        \r\n        if (itemList) {\r\n          feqCurrent = itemList[0];\r\n          feqCurrent.entity.activate();\r\n          bcAnimation.clearCanvas(\"#000000\");\r\n          bcAnimation.drawEntry(20,20,150,100,\"#FFFF00\");\r\n          bcAnimation.drawValues(\r\n            displayStartTime,\r\n            displayCurrentTime,\r\n            displayEndTime,\r\n            displayEntitiesScheduled,\r\n            (displayEntitiesScheduled-displayEntitiesGenerated),\r\n            displayEntityID,\r\n            displayActivity\r\n          );  \r\n          if (initBallFlag) {\r\n            running = true;\r\n            bcAnimation.initBall(180,70);\r\n            requestID = window.requestAnimationFrame(animateCircle);\r\n            initBallFlag = false;\r\n          }\r\n        } else {\r\n          keepRunning = false;\r\n          bcAnimation.endMessage();\r\n        }\r\n      }\r\n    }\r\n    \r\n  <\/script>\r\n<\/body>\r\n<\/html>\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Today I added an animation to the simulation. There are a lot of things that could be said about this, and I&#8217;ll go into just a few of them today. At this point I wanted to get some kind of &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/09\/28\/a-simple-discrete-event-simulation-part-17\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60],"tags":[121,49],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1017"}],"collection":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1017"}],"version-history":[{"count":9,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1017\/revisions"}],"predecessor-version":[{"id":1445,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1017\/revisions\/1445"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1017"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1017"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}