Today I updated the mechanism by which the components pull entities from previous components when there are multiple options. This involves pulling the entity that has been waiting the longest. Each entity is timestamped using a new member named forwardAttemptTime
, for which a getter and setter have also been created. In the current implementation the value is set to Infinity
when an entity is received and then to the current time when an entity completes its minimum traverse time for Queue components or its process time for Process components. I’m thinking this needs to be updated to the time each entity finishes its traverse or process and then reaches the head of the queue, instead. In this case the timestamp could be stored once with each component rather than once for each entity within each component. That’s how I originally implemented the mechanism. It didn’t work because I updated the timestamp every time a forward was attempted whether it was successful or not. It would have worked correctly if the timestamp is updated correctly. I’ll experiment with that and report back tomorrow. In the meantime the existing animation illustrates the intent.
Here’s the important bit of code. It polls all backwards connections to find the one whose head entity has been waiting the longest. This is why the default value for the timestamp is Infinity
and not zero. Once the oldest item is identified the call is sent back to forward the entity to the current component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
this.pullFromPrevious = function() { //ProcessComponent var oldest = this.previousComponentList[0].getForwardAttemptTime(); var oldestIndex = 0; for (var i=1; i<this.previousComponentCount; i++) { var age = this.previousComponentList[i].getForwardAttemptTime(); if (age < oldest) { oldestIndex = i; } } if (this.previousComponentList[oldestIndex].getComponentType() != "Path") { if (this.previousComponentList[oldestIndex].getComponentType() != "Entry") { //TODO: this should call forward entity in a way that ensures that previous component only sends entity to where it is requested and if one is available and if this is a legitimate destination this.previousComponentList[oldestIndex].forwardEntity(); } } else { displayProgressText("Process comp. "+this.componentID+" pulls from previous ("+oldestIndex+") at time "+globalSimClock.toFixed(6)); this.previousComponentList[oldestIndex].pullFromPrevious(); } }; |
Here’s the code that gets the relevant forwardAttemptTime
value from Queue and Process components. Per the discussion above this may be reworked.
1 2 3 4 5 6 7 |
this.getForwardAttemptTime = function() { if (this.countInQueue > 0) { return this.entityQueue[this.countInQueue-1].getForwardAttemptTime(); } else { return Infinity; } }; |
Here’s the code that defines the example system.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
//schedule for seven hours of arrivals in half-hour blocks var arrivalSchedule = [0,0,1,5,6,6,7,8,5,4,3,1,0,0]; 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]]; var arrival1 = new ArrivalsComponent(30.0,arrivalSchedule,entryDistribution); arrival1.defineDataGroup(90,2,80,"#00FFFF","#FF0000","#FFFF00"); var routingTable = [1.0]; var entry1 = new EntryComponent(2.0,routingTable); entry1.defineDataGroup(90,105,80,"#00FFFF","#FF0000","#FFFF00"); entry1.setRoutingMethod(2); //1 single connection, 2 distribution logic, 3 model logic arrival1.assignNextComponent(entry1); var path1 = new PathComponent(); path1.setStartPoint(168,168); path1.setEndPoint(95,193); path1.setSpeedTime(20.0,1.0); path1.calcPathLength(); var path2 = new PathComponent(); path2.setStartPoint(168,168); path2.setEndPoint(245,193); path2.setSpeedTime(20.0,1.0); path2.calcPathLength(); entry1.assignNextComponent(path1); path1.assignPreviousComponent(entry1); entry1.assignNextComponent(path2); path2.assignPreviousComponent(entry1); var queue1 = new QueueComponent(2.0,3.0); queue1.defineDataGroup(5,193,80,"#00FFFF","#FF0000","#FFFF00"); queue1.setRoutingMethod(3); //1 single connection, 2 distribution logic, 3 model logic queue1.setExclusive(false); var queue2 = new QueueComponent(2.0,3.0); queue2.defineDataGroup(175,193,80,"#00FFFF","#FF0000","#FFFF00"); queue2.setRoutingMethod(3); //1 single connection, 2 distribution logic, 3 model logic queue2.setExclusive(false); path1.assignNextComponent(queue1); queue1.assignPreviousComponent(path1); path2.assignNextComponent(queue2); queue2.assignPreviousComponent(path2); var path3 = new PathComponent(); path3.setStartPoint(95,315); path3.setEndPoint(168,340); path3.setSpeedTime(20.0,1.0); path3.calcPathLength(); var path4 = new PathComponent(); path4.setStartPoint(245,315); path4.setEndPoint(168,340); path4.setSpeedTime(20.0,1.0); path4.calcPathLength(); queue1.assignNextComponent(path3); path3.assignPreviousComponent(queue1); queue2.assignNextComponent(path4); path4.assignPreviousComponent(queue2); var process1 = new ProcessComponent(2.0,4.0); process1.defineDataGroup(90,340,80,"#00FFFF","#FF0000","#FFFF00"); process1.setExclusive(true); process1.setRoutingMethod(1); //1 single connection, 2 distribution logic, 3 model logic path3.assignNextComponent(process1); process1.assignPreviousComponent(path3); path4.assignNextComponent(process1); process1.assignPreviousComponent(path4); var path5 = new PathComponent(); path5.setStartPoint(168,415); path5.setEndPoint(168,440); path5.setSpeedTime(10.0,1.0); path5.calcPathLength(); process1.assignNextComponent(path5); path5.assignPreviousComponent(process1); var exit1 = new ExitComponent(2.0); exit1.defineDataGroup(90,440,80,"#00FFFF","#FF0000","#FFFF00"); path5.assignNextComponent(exit1); exit1.assignPreviousComponent(path5); |
Thinking about this also points out that the pullFromPrevious
method needs to call the forwardEntity
method from the sending component so it’s guaranteed to forward the requested entity to the right place. If the previous entity has only one forward connection, which is the case in our present example, then there’s no way to make an error. However, if the upstream component is using distributed or model routing logic the situation could become much more complicated. If an upstream component is using distribution logic then it’s safe for the receiving component to request the pull because all of the upstream component’s possible destinations are equally valid. If an upstream component is using model routing logic, however, and the waiting entity is not scheduled to be forwarded to the requesting component, then the pull would not be allowed. Indeed, in this case the upstream component should not even be picked in this case. I’ve added a comment to the code to remind myself of this.