{"id":1189,"date":"2016-11-28T23:45:49","date_gmt":"2016-11-29T04:45:49","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1189"},"modified":"2017-02-03T13:16:56","modified_gmt":"2017-02-03T18:16:56","slug":"a-simple-discrete-event-simulation-part-48","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/28\/a-simple-discrete-event-simulation-part-48\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 48"},"content":{"rendered":"<p><iframe loading=\"lazy\" width=\"418px\" height=\"980px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20161128a.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>I got an awful lot done over the weekend and today toward making the various behaviors for each type of component as similar as possible.  This really helped me replicate the different controls and interlocks I had come up with for individual components across all of them in a consistent way.  The process of continually revisiting everything also makes everything more clear over time.<\/p>\n<p>I tried a new configuration to test different combinations of components and permissions.  Specifically, I had a main queue that was non-exclusive feed a pair of exclusive local queues that each feed an exclusive process.  The main queue is of infinite size while the local queues have a maximum capacity of three.  I increased the number of entities generated and shortened up some of the queue traverse and process times, which allows us to observe the behavior of the local queues when they fill up completely.  I see a couple of things I still need to fix.  One is that the Path components feeding each of the local Queue components should not be flagged as exclusive unless the number of entities on the path and in the local queue are equal to the maximum capacity.  I also need to review the code for the distribution logic (in this case from Queue 4 to local Queues 7 and 8).  The code that calculates where the next entity is going to go gets called more often than an entity is available to be forwarded, so the distribution method does not operate as smoothly as it should.<\/p>\n<p>Here&#8217;s the code that initializes the current configuration.  It&#8217;s a lot of code so it won&#8217;t be long before I start trying to streamline and automate these declarations.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/schedule for seven hours of arrivals in half-hour blocks\r\n    \/\/var arrivalSchedule = [0,0,1,3,4,4,5,6,3,2,1,0,0,0]; \r\n    var arrivalSchedule = [0,0,1,5,6,6,7,8,5,4,3,1,0,0]; \r\n    \/\/var entryDistribution = [[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0],[0.7,1.0]];\r\n    var entryDistribution = [[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0],[1.0]];\r\n    \r\n    var arrival1 = new ArrivalsComponent(30.0,arrivalSchedule,entryDistribution);\r\n    arrival1.defineDataGroup(90,2,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n\r\n    var routingTable = [1.0];\r\n    var entry1 = new EntryComponent(2.0,routingTable);\r\n    entry1.defineDataGroup(90,105,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    entry1.setRoutingMethod(3);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n    \r\n    arrival1.assignNextComponent(entry1);\r\n    \r\n    var path0 = new PathComponent();\r\n    path0.setStartPoint(168,168);\r\n    path0.setEndPoint(168,193);\r\n    path0.setSpeedTime(10.0,1.0);\r\n    path0.calcPathLength();\r\n\r\n    entry1.assignNextComponent(path0);\r\n    path0.assignPreviousComponent(entry1);\r\n\r\n    \/\/var queue0 = new QueueComponent(2.0,9.0);\r\n    var queue0 = new QueueComponent(2.0,6.0);\r\n    queue0.defineDataGroup(90,193,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    queue0.setRoutingMethod(2);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n\r\n    path0.assignNextComponent(queue0);\r\n    queue0.assignPreviousComponent(path0);\r\n    \r\n    var path1 = new PathComponent();\r\n    path1.setStartPoint(170,304);\r\n    path1.setEndPoint(95,329);\r\n    path1.setSpeedTime(20,1.0);\r\n    path1.calcPathLength();\r\n\r\n    queue0.assignNextComponent(path1);\r\n    path1.assignPreviousComponent(queue0);\r\n    \r\n    var path2 = new PathComponent();\r\n    path2.setStartPoint(170,304);\r\n    path2.setEndPoint(245,329);\r\n    path2.setSpeedTime(20,1.0);\r\n    path2.calcPathLength();\r\n\r\n    queue0.assignNextComponent(path2);\r\n    path2.assignPreviousComponent(queue0);\r\n    \r\n    var queue1 = new QueueComponent(2.0,3.0,3); \r\n    queue1.defineDataGroup(5,329,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    queue1.setRoutingMethod(1);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n    queue1.setExclusive(true);\r\n    \r\n    path1.assignNextComponent(queue1);\r\n    queue1.assignPreviousComponent(path1);\r\n\r\n    var queue2 = new QueueComponent(2.0,3.0,3); \r\n    queue2.defineDataGroup(175,329,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    queue2.setRoutingMethod(1);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n    queue2.setExclusive(true);\r\n    \r\n    path2.assignNextComponent(queue2);\r\n    queue2.assignPreviousComponent(path2);\r\n\r\n    var path3 = new PathComponent();\r\n    path3.setStartPoint(95,440);\r\n    path3.setEndPoint(95,465);\r\n    path3.setSpeedTime(20,1.0);\r\n    path3.calcPathLength();\r\n    \r\n    queue1.assignNextComponent(path3);\r\n    path3.assignPreviousComponent(queue1);\r\n    \r\n    var path4 = new PathComponent();\r\n    path4.setStartPoint(245,440);\r\n    path4.setEndPoint(245,465);\r\n    path4.setSpeedTime(20,1.0);\r\n    path4.calcPathLength();\r\n    \r\n    queue2.assignNextComponent(path4);\r\n    path4.assignPreviousComponent(queue2);\r\n    \r\n    var process1 = new ProcessComponent(2.0,10.0);\r\n    process1.defineDataGroup(5,465,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    process1.setExclusive(true);\r\n    process1.setRoutingMethod(1);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n\r\n    path3.assignNextComponent(process1);\r\n    process1.assignPreviousComponent(path3);\r\n    \r\n    var process2 = new ProcessComponent(2.0,10.0);\r\n    process2.defineDataGroup(175,465,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    process2.setExclusive(true);\r\n    process2.setRoutingMethod(1);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n\r\n    path4.assignNextComponent(process2);\r\n    process2.assignPreviousComponent(path4);\r\n    \r\n    var path5 = new PathComponent();\r\n    path5.setStartPoint(95,539);\r\n    path5.setEndPoint(170,564);\r\n    path5.setSpeedTime(30,1.0);\r\n    path5.calcPathLength();\r\n    \r\n    process1.assignNextComponent(path5);\r\n    path5.assignPreviousComponent(process1);\r\n    \r\n    var path6 = new PathComponent();\r\n    path6.setStartPoint(245,539);\r\n    path6.setEndPoint(170,564);\r\n    path6.setSpeedTime(30,1.0);\r\n    path6.calcPathLength();\r\n    \r\n    process2.assignNextComponent(path6);\r\n    path6.assignPreviousComponent(process2);\r\n\r\n    var exit1 = new ExitComponent(2.0);\r\n    exit1.defineDataGroup(90,564,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\");\r\n    \r\n    path5.assignNextComponent(exit1);\r\n    path6.assignNextComponent(exit1);\r\n    exit1.assignPreviousComponent(path5);\r\n    exit1.assignPreviousComponent(path6);\r\n<\/pre>\n<p>Here&#8217;s the code for the entire Queue component.  Of special note is the logic for the <code>forwardEntity<\/code> method which supports three different methods for calculating the next destination of the forwarded entity (1: single connection, 2: distribution logic, 3: model routing logic).  I&#8217;m going to have to adjust this a bit so I test for whether an entity can be forwarded before I run the distribution logic, or possibly all three types of logic.  Pull requests sent from downstream components can exercise the logic when no  entity is available to be pulled, and entities can finish traversing the queue but be preventing from moving downstream because the next components are blocked.  It may be a simple matter of calculating a potential destination and saving it as a temporary variable, and then acting on it and updating the persistent variable when an entity can actually be forwarded.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function QueueComponent(displayDelay, traversalTime, maxCapacity = Infinity) {\r\n      \/\/for now always non-exclusive, could be exclusive when at capacity--or just limited by isOpen function and exclusivity doesn't matter\r\n      setOfComponents.push(this);\r\n      this.componentID = getNewComponentID();\r\n      this.componentType = \"Queue\";\r\n      this.componentName = \"Queue\";\r\n      this.exclusive = false;\r\n      this.routingMethod = 2;  \/\/1: one connection, 2: distribution, 3 routing\r\n      this.previousComponentList = [];\r\n      this.previousComponentCount = 0;\r\n      this.nextComponentList = [];\r\n      this.nextComponentCount = 0;\r\n      this.traversalTime = traversalTime;\r\n      this.maxCapacity = maxCapacity;  \/\/negative or very large means infinite capacity and non-exclusive by default)\r\n      this.previousComponentIndex = 0;\r\n      this.nextComponentIndex = 0;\r\n      this.entityQueue = [];\r\n      this.openStatus = true;\r\n      this.entryTime = \"\";\r\n      this.entryEntityID = \"\";\r\n      this.exitTime = \"\";\r\n      this.exitEntityID = \"\";\r\n      this.exitResidenceTime = \"\";\r\n      this.countInQueue = 0;\r\n      this.countInTraversal = 0;\r\n      this.activity = \"\";\r\n      this.endEntryDisplayTime = 0;\r\n      this.endExitDisplayTime = 0;\r\n      this.endAllDisplayTime = 0;\r\n      this.displayDelay = displayDelay;\r\n      \/\/this.nextTraverseCompleteTime = 0.0;  \/\/TODO: eliminate this when related TODOs addressed\r\n\r\n      this.reset = function() {\r\n        this.previousComponentIndex = this.previousComponentCount - 1;\r\n        this.nextComponentIndex = this.nextComponentCount - 1;\r\n        this.entityQueue = [];\r\n        this.openStatus = true;\r\n        this.entryTime = \"\";\r\n        this.entryEntityID = \"\";\r\n        this.exitTime = \"\";\r\n        this.exitEntityID = \"\";\r\n        this.exitResidenceTime = \"\";\r\n        this.countInQueue = 0;\r\n        this.countInTraversal = 0;\r\n        this.activity = \"\";\r\n        this.endEntryDisplayTime = 0;\r\n        this.endExitDisplayTime = 0;\r\n        this.endAllDisplayTime = 0;\r\n        \/\/this.nextTraverseCompleteTime = 0.0;  \/\/TODO: eliminate this when related TODOs addressed\r\n      };\r\n      this.assignPreviousComponent = function(prev) {  \/\/TODO: implement code that makes this actually work\r\n        this.previousComponentList.push(prev);\r\n        this.previousComponentCount++;\r\n        this.previousComponentIndex = this.previousComponentCount - 1;\r\n      };\r\n      this.assignNextComponent = function(next) {\r\n        this.nextComponentList.push(next);\r\n        this.nextComponentCount++;\r\n        this.nextComponentIndex = this.nextComponentCount - 1;\r\n      };\r\n      \r\n      this.getComponentID = function() {\r\n        return this.componentID;\r\n      };\r\n      this.getComponentType = function() {  \/\/PathComponent\r\n        return this.componentType;\r\n      };\r\n      this.getComponentName = function() {\r\n        return this.componentName;\r\n      };\r\n      this.setComponentName = function(componentName) {\r\n        this.componentName = componentName;\r\n      };\r\n      this.getExclusive = function() {\r\n        return this.exclusive;\r\n      };\r\n      this.setExclusive = function(exclusive) {\r\n        this.exclusive = exclusive;\r\n      };\r\n      this.getTraversalTime = function() {\r\n        return this.traversalTime;\r\n      };\r\n      this.getMaxCapacity = function() {\r\n        return this.maxCapacity;\r\n      };\r\n      this.getOpenStatus = function() {\r\n        return this.openStatus;\r\n      };\r\n      this.setOpenStatus = function(openStatus) {\r\n        this.openStatus = openStatus;\r\n      };\r\n      this.getRoutingMethod = function() {\r\n        return this.routingMethod;\r\n      };\r\n      this.setRoutingMethod = function(routingMethod) {\r\n        this.routingMethod = routingMethod;\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.getExitResidenceTime = function() {\r\n        return this.exitResidenceTime;\r\n      };\r\n      this.getCountInQueue = function() {\r\n        return this.countInQueue;\r\n      };\r\n      this.getCountInTraversal = function() {\r\n        return this.countInTraversal;\r\n      };\r\n      this.getActivity = function() {\r\n        return this.activity;\r\n      };\r\n      this.getEndEntryDisplayTime =function() {\r\n        return this.endEntryDisplayTime;\r\n      };\r\n      this.getEndExitDisplayTime =function() {\r\n        return this.endExitDisplayTime;\r\n      };\r\n      this.getEndAllDisplayTime =function() {\r\n        return this.endAllDisplayTime;\r\n      };\r\n      \r\n      this.dataGroup = new DisplayGroup1();\r\n      this.defineDataGroup = function(x,y,vw,bc,vc,lc) {\r\n        this.dataGroup.define(this.componentID,this.componentType,x,y,vw,bc,vc,lc);\r\n      };\r\n      this.dataGroup.addValue(this.entryEntityID,\"Entry ID\",\"integer\");\r\n      this.dataGroup.addValue(this.countInQueue,\"# In Queue\",\"integer\");\r\n      this.dataGroup.addValue(this.countInTraversal,\"# Traversing\",\"integer\");\r\n      this.dataGroup.addValue(this.maxCapacity,\"Capacity\",\"integer\")\r\n      this.dataGroup.addValue(this.exitEntityID,\"Exit ID\",\"integer\");\r\n      this.dataGroup.addValue(this.exitResidenceTime,\"Resdnce Tm\",\"numdec\",5);\r\n      this.dataGroup.addValue(this.nextComponentIndex,\"Next comp.\",\"integer\");\r\n      this.dataGroup.addValue(this.activity,\"Activity\",\"text\");\r\n      \r\n      this.assignDisplayValues = function() {\r\n        this.dataGroup.valueList[0].value = this.entryEntityID;\r\n        this.dataGroup.valueList[1].value = this.countInQueue;\r\n        this.dataGroup.valueList[2].value = this.countInTraversal;\r\n        this.dataGroup.valueList[3].value = this.maxCapacity;\r\n        this.dataGroup.valueList[4].value = this.exitEntityID;\r\n        this.dataGroup.valueList[5].value = this.exitResidenceTime;\r\n        this.dataGroup.valueList[6].value = this.nextComponentIndex;\r\n        this.dataGroup.valueList[7].value = this.activity;\r\n        if (this.exclusive) {\r\n          if (this.openStatus) {\r\n            this.dataGroup.setBorderColor(\"#00FF00\");\r\n          } else {\r\n            this.dataGroup.setBorderColor(\"#FF0000\");\r\n          }\r\n        }\r\n      };\r\n\r\n      this.drawData = function() {\r\n        this.assignDisplayValues();\r\n        this.dataGroup.drawBasic();\r\n      };\r\n      this.isOpen = function() {  \/\/QueueComponent\r\n        return this.openStatus;  \/\/Queue can always receive more entities, see exclusivity discussion above\r\n      };\r\n      this.clearEntryDisplay = function() {\r\n        \/\/only clear display if a new one hasn't started a new timer\r\n        if (globalSimClock >= this.endEntryDisplayTime) {\r\n          this.entryTime = \"\";\r\n          this.entryEntityID = \"\";\r\n          \/\/this.exitResidenceTime = \"\";\r\n        }\r\n        if (globalSimClock >= this.endAllDisplayTime) {\r\n          this.activity= \"\";        \r\n        }\r\n        \/\/displayProgressText(\"Queue entry \"+this.componentID+\" clears at time \"+globalSimClock.toFixed(6));\r\n      };\r\n      this.clearExitDisplay = function() {\r\n        \/\/only clear display if a new one hasn't started a new timer\r\n        if (globalSimClock >= this.endExitDisplayTime) {\r\n          this.exitTime = \"\";\r\n          this.exitEntityID = \"\";\r\n          this.exitResidenceTime = \"\";\r\n        }\r\n        if (globalSimClock >= this.endAllDisplayTime) {\r\n          this.activity= \"\";        \r\n        }\r\n        \/\/displayProgressText(\"Queue exit \"+this.componentID+\" clears at time \"+globalSimClock.toFixed(6));\r\n      };\r\n      this.pullFromPrevious = function() {  \/\/QueueComponent\r\n        if (this.previousComponentList[0].getComponentType() != \"Path\") {  \/\/TODO: make this support indices other than 0.  How?\r\n          if (this.previousComponentList[0].getComponentType() != \"Entry\") {\r\n            this.previousComponentList[0].forwardEntity();  \/\/TODO: must only be used on Queue and Process components that support multiple previous links\r\n          }\r\n        } else {\r\n          \/\/this.previousComponentList[0].previousComponent.forwardEntity();  \/\/TODO: rework so can pull through arbitrary # of paths to next nonpath component\r\n          this.previousComponentList[0].pullFromPrevious();\r\n        }      \r\n      };\r\n      this.nextOpen = function() {\r\n        var startIndex = this.nextComponentIndex;\r\n        var tempIndex = startIndex;\r\n        do {\r\n          tempIndex++;\r\n          if (tempIndex >= this.nextComponentCount) {\r\n            tempIndex = 0;\r\n          }\r\n          if (this.nextComponentList[tempIndex].isOpen()) {\r\n            \/\/open link found, update and return nextComponentIndex\r\n            this.nextComponentIndex = tempIndex;\r\n            return tempIndex;\r\n          }\r\n        } while (tempIndex != startIndex);\r\n        return -1;  \/\/no open links found, leave nextComponentIndex unchanged\r\n      };\r\n      this.traverseComplete = function() {\r\n        this.countInTraversal--;   \/\/TODO: ensure handled properly if traversal time is zero\r\n        \/\/figure out which entity just finished traversing\r\n        var tempID = this.entityQueue[this.countInTraversal].entityID;  \/\/TODO: this works for FIFO, but not necessarily for other logics\r\n        displayProgressText(\"Queue comp. \"+this.componentID+\" entity: \"+tempID+\" trav. at time \"+globalSimClock.toFixed(6));\r\n        this.forwardEntity();\r\n      };\r\n      this.forwardEntity = function() {  \/\/QueueComponent\r\n        var dest = -1;\r\n        if (this.routingMethod == 1) {  \/\/single connection\r\n          if (this.nextComponentList[0].isOpen()) {\r\n            dest = 0;\r\n          }\r\n        } else if (this.routingMethod == 2) {  \/\/distribution\r\n          if (this.nextOpen() >= 0) {\r\n            dest = this.nextComponentIndex;\r\n          }\r\n        } else if (this.routingMethod == 3) {  \/\/model routing logic\r\n          if (this.savedDestination >= 0) {\r\n            dest = this.savedDestination;\r\n          } else {\r\n            dest = 0;\r\n            var test = Math.random();\r\n            while (test > routingTable[dest]) {\r\n              dest++;\r\n            }\r\n            if (dest <= this.nextComponentCount) {\r\n              if (!this.nextComponentList[dest].isOpen()) {\r\n                dest = -1;\r\n              }\r\n            } else {\r\n              alert(\"Entry comp. tried to assign destination with too high of an index\")            \r\n            }\r\n          }\r\n        } else {  \/\/0 uninitialized or anything else\r\n          alert(\"comp. \"+this.componentID+\" incorrect routing method: \"+this.routingMethod);\r\n        }\r\n        if (dest >= 0) {\r\n          if (this.countInQueue > this.countInTraversal) {\r\n            var entity = this.entityQueue.pop();  \/\/TODO: are we testing to ensure the next entity is really available\r\n            if (entity) {\r\n              \/\/calculate how long item was in queue\r\n              this.exitResidenceTime = globalSimClock - entity.getLocalEntryTime();\r\n              \/\/now use this to calculate stats for the interval\r\n                       \/\/TODO: calculate stats as needed\r\n              this.countInQueue--;\r\n              if (this.countInQueue >= this.maxCapacity) {\r\n                this.openStatus = false;\r\n              } else {\r\n                this.openStatus = true;\r\n              }\r\n              this.exitTime = globalSimClock;\r\n              this.exitEntityID = entity.entityID;\r\n              this.activity = \"forward entity\";          \r\n              this.endExitDisplayTime = globalSimClock+this.displayDelay;\r\n              this.endAllDisplayTime = this.endExitDisplayTime;\r\n              feq.newItem(this.endExitDisplayTime,this,\"clearExitDisplay\");\r\n              displayProgressText(\"Queue comp. \"+this.componentID+\" forwards entity: \"+this.exitEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n              if (this.exclusive) {\r\n                if (this.previousComponentList[0].getComponentType() == \"Path\") {  \/\/TODO: figure out how to do this backwards through multiple connections\r\n                  this.previousComponentList[0].setPreviousOpen();\r\n                }\r\n                this.pullFromPrevious(); \/\/TODO: call this with a modest (~1 sec) delay to account for reaction time? \/\/may or may not successfully get an entity but should always be called\r\n              }\r\n              this.nextComponentList[this.nextComponentIndex].receiveEntity(entity);\r\n            }\r\n          }\r\n        }\r\n      };\r\n      this.receiveEntity = function(entity) {  \/\/QueueComponent\r\n        \/\/receive the entity\r\n        entity.setLocalEntryTime();  \/\/record time entity entered queue\r\n        this.entityQueue.unshift(entity);\r\n        this.countInQueue++;\r\n        this.countInTraversal++;   \/\/TODO: ensure handled properly if traversal time is zero\r\n        if (this.countInQueue >= this.maxCapacity) {\r\n          this.openStatus = false;\r\n        } else {\r\n          this.openStatus = true;\r\n        }\r\n        \/\/display what was done\r\n        this.entryTime = globalSimClock;\r\n        this.entryEntityID = entity.entityID;\r\n        this.activity = \"receive entity\";\r\n        \/\/set timer to clear the display after a bit\r\n        this.endEntryDisplayTime = globalSimClock+this.displayDelay;\r\n        this.endAllDisplayTime = this.endEntryDisplayTime;\r\n        feq.newItem(this.endEntryDisplayTime,this,\"clearEntryDisplay\");\r\n        \/\/set timer for the new entity to track its traversal time  \/\/TODO: don't bother if traversal time is zero, also insure countInTraversal is decremented (or not incremented) as needed\r\n        \/\/this.nextTraverseCompleteTime = globalSimClock+this.traversalTime;\r\n        \/\/feq.newItem(this.nextTraverseCompleteTime,this,\"traverseComplete\")\r\n        var traverseCompleteTime = globalSimClock+this.traversalTime;\r\n        feq.newItem(traverseCompleteTime,this,\"traverseComplete\");  \/\/TODO: eliminate variable and assign value directly in function call\r\n        displayProgressText(\"Queue comp. \"+this.componentID+\" receives entity: \"+this.entryEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n        \/\/if (this.nextComponent.isOpen()) {  \/\/TODO: run this if traversal time is zero\r\n        \/\/  this.forwardEntity();\r\n        \/\/}\r\n      };\r\n      this.activate = function(nextState) {\r\n        if (nextState == \"clearEntryDisplay\") {\r\n          this.clearEntryDisplay();\r\n        } else if (nextState == \"clearExitDisplay\") {\r\n          this.clearExitDisplay();\r\n        } else if (nextState == \"traverseComplete\") {\r\n          this.traverseComplete();\r\n        } else {\r\n          errorUndefinedAdvanceState(this.entityID,this.nextState);\r\n        }      \r\n      };  \/\/this.activate\r\n    }  \/\/QueueComponent\r\n<\/pre>\n<p>The Process component works almost exactly the same way as the Queue component, except for the time being the maximum capacity is one.  <\/p>\n<p>Here&#8217;s the code for the Path component.  It works largely the same way but each path currently supports only one previous and one next connection.  Note the recursiive nature of the <code>pullFromPrevious<\/code> method, which passes the request through contiguous Path components until asking the first-enncountered non-Path (non-Entry) component to forward an entity.<\/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 = getNewComponentID();\r\n      this.componentType = \"Path\";\r\n      this.componentName = \"Path\";\r\n      this.exclusive = false;\r\n      this.previousComponent = null;\r\n      this.nextComponent = null;\r\n      this.entityQueue = [];\r\n      this.traverseQueue = [];\r\n      this.openStatus = true;\r\n      this.entryTime = \"\";\r\n      this.entryEntityID = \"\";\r\n      this.exitTime = \"\";\r\n      this.exitEntityID = \"\";\r\n      this.exitResidenceTime = \"\";  \/\/TODO: probably not needed\r\n      this.countInQueue = 0;\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.endTraverse = 0.0;\r\n      \r\n      this.reset = function() {\r\n        this.entityQueue = [];\r\n        this.traverseQueue = [];\r\n        this.openStatus = true;\r\n        this.entryTime = \"\";\r\n        this.entryEntityID = \"\";\r\n        this.exitTime = \"\";\r\n        this.exitEntityID = \"\";\r\n        this.exitResidenceTime = \"\";  \/\/TODO: probably not needed\r\n        this.countInQueue = 0;\r\n        \/\/this.endTraverse = 0.0;\r\n      };\r\n      this.assignPreviousComponent = function(previous) {\r\n        this.previousComponent = previous;\r\n      };\r\n      this.assignNextComponent = function(next) {\r\n        this.nextComponent = next;\r\n        this.exclusive = next.getExclusive();\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      \r\n      this.getComponentID =function() {\r\n        return this.componentID;\r\n      };\r\n      this.getComponentType =function() {  \/\/PathComponent\r\n        return this.componentType;\r\n      };\r\n      this.getComponentName =function() {\r\n        return this.componentName;\r\n      };\r\n      this.setComponentName =function(componentName) {\r\n        this.componentName = componentName;\r\n      };\r\n      this.getExclusive = function() {\r\n        return this.exclusive;\r\n      };\r\n      this.setExclusive = function(exclusive) {\r\n        this.exclusive = exclusive;\r\n      };\r\n      this.getOpenStatus = function() {\r\n        return this.openStatus;\r\n      };\r\n      this.setOpenStatus = function(openStatus) {\r\n        this.openStatus = openStatus;\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.getExitResidenceTime =function() {\r\n        return this.exitResidenceTime;\r\n      };\r\n      this.getCountInQueue =function() {\r\n        return this.countInQueue;\r\n      };\r\n      \r\n      this.drawData = function() {\r\n        \/\/this.nodeColor = \"#FF00FF\";\r\n        if (this.exclusive) {\r\n          if (this.openStatus) {\r\n            this.lineColor = \"#00FF00\";\r\n          } else {\r\n            this.lineColor = \"#FF0000\";\r\n          }\r\n        } else {\r\n          this.lineColor = \"#FFFFFF\";\r\n        }\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.drawNodes = function() {\r\n        this.nodeColor = \"#FF00FF\";\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      this.drawEntities = function() {\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.isOpen = function() {  \/\/PathComponent\r\n        if (!this.exclusive) {\r\n          if (this.entityQueue.length > 0) {\r\n            if (this.traverseQueue[0] < (10 + 2)) {\r\n              if (this.previousComponent.componentType == \"Path\") {\r\n                this.openStatus = false;\r\n                return false;\r\n              \/\/} else {\r\n              \/\/  this.openStatus = true;\r\n              \/\/  return true;\r\n              }\r\n            }\r\n          }\r\n        } else {\r\n          if (this.countInQueue <= 0){\r\n            this.openStatus = this.nextComponent.isOpen();\r\n            return this.openStatus;\r\n          } else {\r\n            this.openStatus = false;\r\n            return false;\r\n          }\r\n        }\r\n        this.openStatus = true;\r\n        return true;\r\n      };\r\n      this.pullFromPrevious = function() {  \/\/PathComponent\r\n        if (this.previousComponent.getComponentType != \"Path\") {  \/\/TODO: figure out what, if anything, happens when need to support multiple incoming\/previous links\r\n          if (this.previousComponent.getComponentType != \"Entry\") {\r\n            this.previousComponent.forwardEntity();\r\n          }\r\n        } else {\r\n          this.previousComponent.pullFromPrevious();\r\n        }\r\n      };\r\n      this.setNextClosed = function() {\r\n        this.openStatus = false;\r\n        if (this.nextComponent.getComponentType != \"Path\") {\r\n          this.nextComponent.setOpenStatus(false);\r\n        } else {\r\n          this.nextComponent.setNextClosed();\r\n        }\r\n      };\r\n      this.setPreviousOpen = function() {\r\n        this.openStatus = true;\r\n        if (this.previousComponent.getComponentType == \"Path\") {\r\n          this.previousComponent.setPreviousOpen();\r\n        }\r\n      };      \r\n      this.moveEntity = function(entity) {  \/\/PathComponent\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() {  \/\/PathComponent\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        \/\/if this Component is exclusive it can only be set back to open by the downstream component when it clears \r\n      };\r\n      this.receiveEntity = function(entity) {  \/\/PathComponent\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.exclusive) {\r\n          this.openStatus = false;\r\n          \/\/if (this.openStatus) {\r\n          \/\/  this.setNextClosed();\r\n          \/\/}\r\n        }\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>At this point I&#8217;ve mostly worked out the procedures for routing entities forward using different kinds of logic, but I haven&#8217;t examined support for complex logic for pulling entities forward from multiple upstream connections.  So far the pull logic has only been used for components with a single upstream connection (the code blindly references <code>this.previousConnectionList[0]<\/code>).  What if multiple components are all trying to forward entities to the same exclusive component downstream?  When the downstream component clears by forwarding one of its own entities, how does it know which upstream component to pull from?  I&#8217;ve mentioned previously that the required logic, applicable only to pulling to exclusive components (non-exclusive components can simply receive everything sent to them) should work something like the distribution logic going forward, but an extra queue may be required.  It&#8217;s also possible that the need to do this can be largely finessed by ensuring that some kind of non-exclusive queue is always included so the complex, rotating pull logic is never required.  Alternatively, there may be a simpler solution I simply haven&#8217;t seen because I haven&#8217;t experimented with it yet.  The pulling component, for example, could poll all of the connections to see which upstream has the oldest forwarding request outstanding, but there could be other considerations that demand a different priority order.<\/p>\n<p>I&#8217;m slowly trying different combinations and catching inconsistencies but I&#8217;m getting the feeling that things are getting a little entropic.  Continued inspection, testing, and honing should bring more loose ends into focus so they can be continually corrected and streamlined as opportunities present themselves.  At some point I&#8217;m going to have to run tests against a number of configurations to ensure every combination of components is operating properly.  That&#8217;s going on the To Do list.<\/p>\n<p>I reworked the drawing of Path components so the line segment is drawn at the same time as all the other components are drawn, and then the <code>drawModel<\/code> function circles back to draw all of the nodes and then all of the entities.  This makes the display look more consistent.  I also removed the ending times for the display of entry and exit entity IDs since we know that mechanism works and because it saves space.  Never mind that I added the maximum Capacity to the Queue component displays and the exit count to the Exit component.  Finally, I changed the lower text region to stop displaying messages when the entry and exit ID information clears for each component.  This means that some steps will execute without generating a message.  This was done to reduce clutter so I could concentrate on debugging.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I got an awful lot done over the weekend and today toward making the various behaviors for each type of component as similar as possible. This really helped me replicate the different controls and interlocks I had come up with &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/28\/a-simple-discrete-event-simulation-part-48\/\">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\/1189"}],"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=1189"}],"version-history":[{"count":4,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1189\/revisions"}],"predecessor-version":[{"id":1418,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1189\/revisions\/1418"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1189"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1189"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1189"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}