{"id":1116,"date":"2016-11-07T23:17:15","date_gmt":"2016-11-08T04:17:15","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1116"},"modified":"2017-02-03T14:28:30","modified_gmt":"2017-02-03T19:28:30","slug":"a-simple-discrete-event-simulation-part-37","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/07\/a-simple-discrete-event-simulation-part-37\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 37"},"content":{"rendered":"<p>Today I finished putting together a basic Path component.  It mostly works like a queue but it has &#8220;physical&#8221; dimensions that take time for the entities to traverse.  It also draws the path, its endpoints, and any entities in their proper positions.<\/p>\n<p>The Path is component three in the demo.  It&#8217;ll take a <em>lot<\/em> of clicks to get through it.<\/p>\n<p>(Click on the &#8220;Step&#8221; button to advance through the model, refresh the page to run it again.)<\/p>\n<p><iframe loading=\"lazy\" width=\"400px\" height=\"750px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20161107.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>Here&#8217;s the code for the Path component.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function PathComponent() {\r\n      setOfComponents.push(this);\r\n      this.componentID = getNewID();\r\n      this.componentName = \"Path\";\r\n      this.entryTime = \"\";\r\n      this.entryEntityID = \"\";\r\n      this.countInQueue = 0;\r\n      this.exitTime = \"\";\r\n      this.exitEntityID = \"\";\r\n      this.exitResidenceTime = \"\";\r\n      this.x1 = 0.0;\r\n      this.y1 = 0.0;\r\n      this.x2 = 0.0;\r\n      this.y2 = 0.0;\r\n      this.pathLength = 0.0;\r\n      this.nextComponent = null;\r\n      this.entityQueue = [];\r\n      this.traverseQueue = [];\r\n      this.endTraverse = 0.0;\r\n      \r\n      this.getComponentID =function() {\r\n        return this.componentID;\r\n      };\r\n      this.getComponentName =function() {\r\n        return this.componentName;\r\n      };\r\n      this.getEntryTime =function() {\r\n        return this.entryTime;\r\n      };\r\n      this.getEntryEntityID =function() {\r\n        return this.entryEntityID;\r\n      };\r\n      this.getExitTime =function() {\r\n        return this.exitTime;\r\n      };\r\n      this.getExitEntityID =function() {\r\n        return this.exitEntityID;\r\n      };\r\n      this.getCountInQueue =function() {\r\n        return this.countInQueue;\r\n      };\r\n      this.getExitResidenceTime =function() {\r\n        return this.exitResidenceTime;\r\n      };\r\n      \r\n      this.setStartPoint = function(x1,y1) {\r\n        this.x1 = x1;\r\n        this.y1 = y1;\r\n      };\r\n      this.setEndPoint = function(x2,y2) {\r\n        this.x2 = x2;\r\n        this.y2 = y2;\r\n      };\r\n      this.setSpeedTime = function(speed, maxRefreshTime) {\r\n        this.speed = speed;\r\n        this.maxRefreshTime = maxRefreshTime;\r\n      };\r\n      this.calcPathLength = function() {\r\n        this.pathLength = Math.sqrt((this.x2-this.x1)*(this.x2-this.x1) +\r\n                                    (this.y2-this.y1)*(this.y2-this.y1));\r\n        this.endTraverse = this.pathLength;\r\n      };\r\n      this.isOpen = function() {\r\n        if (this.entityQueue.length > 0) {\r\n          if (this.traverseQueue[0] < (10 + 2)) {\r\n            return false;\r\n          }\r\n        }\r\n        return true;\r\n      };\r\n      \r\n      this.drawData = function() {\r\n        this.nodeColor = \"#FF00FF\";\r\n        this.lineColor = \"#FFFFFF\";\r\n        globalCTX.strokeStyle = this.lineColor;\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(this.x1+0.5,this.y1+0.5);\r\n        globalCTX.lineTo(this.x2+0.5,this.y2+0.5);\r\n        globalCTX.stroke();\r\n        drawNode(this.x1+0.5,this.y1+0.5,3,this.nodeColor);\r\n        drawNode(this.x2+0.5,this.y2+0.5,3,this.nodeColor);\r\n        \r\n        var numEntities = this.entityQueue.length;\r\n        if (numEntities > 0) {\r\n          for (var i=0; i<numEntities; i++) {\r\n            this.entityQueue[i].drawEntity();\r\n          }\r\n        }\r\n      };\r\n      this.assignNextComponent = function(next) {\r\n        this.nextComponent = next;\r\n      };\r\n      this.moveEntity = function(entity) {\r\n        \/\/moves entities down path, but keeps them from overlapping\r\n        \/\/for now assumes radius of 5 and clearance of 2\r\n        \/\/start from discharge end of list\r\n        var index = this.entityQueue.length - 1;\r\n        while ((this.entityQueue[index].entityID != entity.entityID) &#038;&#038; (index > 0)) {\r\n          index--;\r\n        }\r\n        var distanceIncrement = this.speed * this.maxRefreshTime;\r\n        var dist = distanceIncrement;\r\n        var timeIncrement = this.maxRefreshTime;\r\n        var nextState = \"move\";\r\n        if (index < this.entityQueue.length - 1) {\r\n          \/\/not the lead entity, check against next entity\r\n          if (this.traverseQueue[index]+distanceIncrement > (this.traverseQueue[index+1] - 10 - 2)) {\r\n            dist = this.traverseQueue[index+1] - 10 - 2 - this.traverseQueue[index];\r\n            if (dist <= 0.0) {\r\n              dist = 0.0;\r\n              timeIncrement = this.maxRefreshTime; \/\/0.5;\r\n            } else {\r\n              timeIncrement *= dist \/ distanceIncrement;\r\n            }\r\n          }\r\n        } else {\r\n          \/\/this is the lead entity, check against end of path\r\n          if (this.traverseQueue[index]+distanceIncrement > this.pathLength) {\r\n            dist = this.pathLength - this.traverseQueue[index] + 0.001;\r\n            timeIncrement *= dist \/ distanceIncrement;\r\n            nextState = \"forward\";\r\n          }\r\n        }  \/\/TODO: also check against endTraverse, which considers possible entity on subsequent path\r\n        this.traverseQueue[index] += dist; \/\/distanceIncrement;\r\n        var frac = this.traverseQueue[index] \/ this.pathLength;\r\n        var x = this.x1 + (this.x2 - this.x1) * frac;\r\n        var y = this.y1 + (this.y2 - this.y1) * frac;\r\n        entity.setLocation(x,y);\r\n        feq.newItem(globalSimClock+timeIncrement,this,nextState,entity);\r\n        displayProgressText(\"Path comp. \"+this.componentID+\" moves entity \"+entity.entityID+\" at time \"+globalSimClock.toFixed(6));\r\n      };\r\n      this.forwardEntity = function() {\r\n        if (this.nextComponent.isOpen()) {\r\n          this.traverseQueue.pop();\r\n          var entity = this.entityQueue.pop();\r\n          if (entity) {\r\n            this.countInQueue--;\r\n            this.exitTime = globalSimClock;\r\n            this.exitEntityID = entity.entityID;\r\n            displayProgressText(\"Path comp. \"+this.componentID+\" forwards entity: \"+this.exitEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n            this.nextComponent.receiveEntity(entity);\r\n          }\r\n        }\r\n      };\r\n      this.receiveEntity = function(entity) {\r\n        \/\/receive the entity\r\n        entity.setLocalEntryTime();  \/\/record time entity entered queue\r\n        entity.setLocation(this.x1,this.y1);\r\n        feq.newItem(globalSimClock+this.maxRefreshTime,this,\"move\",entity);\r\n        this.traverseQueue.unshift(0.0);\r\n        this.entityQueue.unshift(entity);\r\n        this.countInQueue++;\r\n        \/\/display what was done\r\n        this.entryTime = globalSimClock;\r\n        this.entryEntityID = entity.entityID;\r\n        displayProgressText(\"Path comp. \"+this.componentID+\" receives entity: \"+this.entryEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n        \/\/if (this.nextComponent.isOpen()) {\r\n        \/\/  this.forwardEntity();\r\n        \/\/}\r\n      };\r\n      this.activate = function(nextState,entity2) {\r\n        if (nextState == \"move\") {\r\n          this.moveEntity(entity2);\r\n        } else if (nextState == \"forward\") {\r\n          this.forwardEntity();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }      \r\n      };  \/\/this.activate\r\n    }  \/\/PathComponent\r\n<\/pre>\n<p>Here&#8217;s how the Path component is instantiated and initialized.  In the longer run we want a very generalizable element that can connect anything to anything, including to other Paths.  In practice we&#8217;ll define nodes independently and then link the paths to pairs of nodes so that completely arbitrary networks can be defined.  For now the endpoints are defined directly within the Path component.  The Path has an implied direction, and elements can only move along it one way.  That said, the path can go in any direction on the screen and the code will handle it naturally.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\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 arrival1 = new ArrivalsComponent(30.0,arrivalSchedule);\r\n    arrival1.defineDataGroup(10,10,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n\r\n    var entry1 = new EntryComponent(2.0);\r\n    entry1.defineDataGroup(175,10,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    \r\n    arrival1.assignNextComponent(entry1);\r\n    \r\n    var path1 = new PathComponent();\r\n    path1.setStartPoint(180,73);\r\n    path1.setEndPoint(320,196);\r\n    path1.setSpeedTime(7.5,1.0);\r\n    path1.calcPathLength();\r\n    \r\n    entry1.assignNextComponent(path1);\r\n    \r\n    var exit1 = new ExitComponent(2.0);\r\n    exit1.defineDataGroup(175,196,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    exit1.assignPreviousComponent(path1);\r\n    \r\n    path1.assignNextComponent(exit1);\r\n<\/pre>\n<p>If entities are moving along the path they are going at the assigned speed; there is no mechanism for accelerating and decelerating.  That can be added later if we want.  Entities are assumed to have a radius of five and require a clearance of two.  For the time being the units of measure are in screen pixels.  Later we&#8217;ll have to add in the ability to scale the elements, which will add another layer of complexity.  If an element cannot move the full distance it &#8220;wants&#8221; to, because it has to wait for an element in front of it to move out of the way, it pauses for a definable interval (I think one time unit in the demo) and then tries to move again.  This is akin to simulating the reaction time of people or vehicles edging forward in queues.  They don&#8217;t all move in lockstep, they take a certain amount of time to react to the space that opens up in front of them.<\/p>\n<p>I had to add another layer of complexity to the discrete event handling mechanism because we are treating the moving entities as being entirely passive.  If an entity within a component needs to be moved according to independent logic that uses the discrete event handling mechanism, then the item that goes into the future events queue has to carry information about the component and the specific entity within the component.  Looking at the relevant calls:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n        \/\/creating a new event item in the Path component's move method\r\n        \/\/\"this\" refers to the Path component\r\n        \/\/\"entity\" refers to the specific item being moved\r\n        feq.newItem(globalSimClock+timeIncrement,this,nextState,entity);\r\n...\r\n        \/\/calls in the main event processing looop\r\n        \/\/note the second parameter added to the activate method\r\n        var itemList = feq.getFirstItem();\r\n        \r\n        if (itemList) {\r\n          feqCurrent = itemList[0];\r\n          feqCurrent.entity.activate(feqCurrent.getNextState(),feqCurrent.entity2);\r\n<\/pre>\n<p>Here&#8217;s the updated definition of the <code>newItem<\/code> method in the <code>FutureEventsQueue<\/code> object.  The only change is that it passes the newly added fourth parameter, the one pointing to the internal entity, to the <code>new FutureEventItem<\/code> call.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      this.newItem = function(time,entity,nextState,entity2) {\r\n        \/\/create futureEventItem\r\n        var feqItem = new FutureEventItem(time,\"absolute\",entity,nextState,entity2);\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        } else {\r\n          \/\/find index of feq item to insert before\r\n          \/\/findIndex is a native JavaScript method of the Array object\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        }\r\n      };\r\n<\/pre>\n<p>Here's the updated code for the FutureEventItem object.  All it does is store the extra entity reference.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/future events queue item\r\n    function FutureEventItem(time,type,entity,nextState,entity2) {\r\n      this.activationTime = 0.0;\r\n      this.nextState = nextState;\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.toFixed(6));  \/\/alert on invalid time\r\n        }\r\n      }\r\n      this.entity = entity;\r\n      this.entity2 = entity2;\r\n      this.getActivationTime = function() {\r\n        return this.activationTime;\r\n      };\r\n      this.update = function(time,type,nextState) {\r\n        this.nextState = nextState;\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: \"+this.entity.entityID+\" Current Time: \"+globalSimClock+\" Specified Time: \"+time.toFixed(6));  \/\/alert on invalid time\r\n          }\r\n        }\r\n      };\r\n      this.getNextState = function() {\r\n        return this.nextState;\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<\/pre>\n<p>Here's the working result of all this, in the <code>activate<\/code> method of the Path component:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      this.activate = function(nextState,entity2) {\r\n        if (nextState == \"move\") {\r\n          this.moveEntity(entity2);\r\n        } else if (nextState == \"forward\") {\r\n          this.forwardEntity();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }      \r\n      };  \/\/this.activate\r\n<\/pre>\n<p>JavaScript is bizarre in many ways but one of the nice things about it is that if you choose not to include parameters at the end of the list for function calls, and the relevant variables or parameters are never accessed, then everything goes on working as before.  That means that all the other uses of this mechanism that <em>don't<\/em> use the extra entity parameter will continue to work without modification.  We have to keep track of this kind of thing carefully.  JavaScript won't let you shoot yourself in the foot in the same way that C or C++ will, but it'll give you a whole new set of creative ways to do it.<\/p>\n<p>There are some other things that bear further description but I'll get to those shortly.<\/p>\n<p>I want to observe that I do most of my editing in Notepad++ but I occasionally use WebStorm to help me find certain classes of errors I introduce.  I typically do this when the debugger in Firefox refuses to display the code, which means it had trouble parsing something, and I can't find the error quickly.  I may then go through the code to follow some of WebStorm's suggestions about style, through not all of them.  I've turned off flipping if-else statements and eliminating curly brackets in one-line loops and if statements, for example.<\/p>\n<p>I do not use WebStorm for interactive debugging because it doesn't have access to the DOM.  That's only accessible to the debugger built into each browser.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I finished putting together a basic Path component. It mostly works like a queue but it has &#8220;physical&#8221; dimensions that take time for the entities to traverse. It also draws the path, its endpoints, and any entities in their &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/07\/a-simple-discrete-event-simulation-part-37\/\">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\/1116"}],"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=1116"}],"version-history":[{"count":3,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1116\/revisions"}],"predecessor-version":[{"id":1428,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1116\/revisions\/1428"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}