{"id":1108,"date":"2016-11-01T23:29:54","date_gmt":"2016-11-02T04:29:54","guid":{"rendered":"http:\/\/rpchurchill.com\/?p=1108"},"modified":"2016-11-03T20:24:59","modified_gmt":"2016-11-04T01:24:59","slug":"a-simple-discrete-event-simulation-part-34","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/01\/a-simple-discrete-event-simulation-part-34\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 34"},"content":{"rendered":"<p>The exercise of writing out requirements and mulling them over yielded the desired insights about how to proceed.  Today I&#8217;ll describe the external display mechanism, which blindly redraws all of the desired values with every global redraw event and in a manner entirely external to the simulation element.<\/p>\n<p>Here&#8217;s the code for the individual display items.  The addition here is the <code>updateValue<\/code> method, which takes the new input value as a parameter.  Remembering that JavaScript lets you do some interesting things&#8211;while also being highly annoying and cumbersome&#8211;we see that the method is capable of handling inputs that are functions.  If the input parameter is not a function then the parameter is assumed to be a value which can be assigned directly.  If the input parameter is a function (that itself has no parameters, we can expand that capability later), then the method obtains the value returned by the function.  Naturally this only works if the function passed as a parameter returns a usable value.  This method works for simple functions but does not appear to work if you pass in a method call to an object.  That is apparently too many layers of indirection.  I have it as a note to explore this further.<\/p>\n<p>The other thing the update does is check to see if the new value is different than the last value.  If it is the method returns a value of true (as opposed to a default of false).  This capability isn&#8217;t important for today&#8217;s work but will be for tomorrow&#8217;s.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function DisplayValue(value,label,format,places) {\r\n      \/\/assumes alignment and color\/fillStyle are set before calling\r\n      this.value = value;\r\n      this.label = label + \":\";\r\n      this.format = format;\r\n      this.places = places;\r\n      globalCTX.font = \"12px Arial\";\r\n      this.width = globalCTX.measureText(label).width;\r\n      this.getWidth = function() {\r\n        return this.width;\r\n      };\r\n\/\/here's the new capability\r\n      this.updateValue = function(newValue) {\r\n        var tempValue = '';\r\n        if (typeof newValue === \"function\") {\r\n          tempValue = newValue.call(tempValue); \/\/TODO: expand to handle optional parameters\r\n        } else {\r\n          tempValue = newValue;\r\n        }\r\n        if (tempValue != this.value) {\r\n          this.value = tempValue;\r\n          return true;\r\n        } else {\r\n          return false;\r\n        }\r\n      };\r\n      this.drawValue = function(x,y) {\r\n        var s;\r\n        if (this.format == \"integer\") {\r\n          s = this.value;\r\n        } else if (this.format == \"numdec\") {\r\n          if (typeof this.value === \"number\") {\r\n            s = this.value.toFixed(this.places);\r\n          } else {\r\n            s = this.value;\r\n          }\r\n        } else if (this.format == \"numwide\") {\r\n          if (typeof this.value === \"number\") {\r\n            s = this.value.toPrecision(this.places);\r\n          } else {\r\n            s = this.value;\r\n          }\r\n        } else if (this.format == \"text\") {\r\n          s = this.value;\r\n        } else if (this.format == \"bool\") {\r\n          if (typeof this.value === \"boolean\") {\r\n            if (this.value) {\r\n              s = \"TRUE\";\r\n            } else {\r\n              s = \"FALSE\";\r\n            }\r\n          } else {\r\n            s = this.value;\r\n          }\r\n        }\r\n        globalCTX.fillText(s,x,y);\r\n      }\r\n    }  \/\/displayValue\r\n<\/pre>\n<p>Next up is a reworking of the <code>DisplayGroup<\/code> object.  Since the mechanism must be completely external to any simulation element I went ahead and moved the definition of many of the parameters back to the function\/constructor call itself (the <code>define<\/code> method is left in place for now).  I also made the <code>addValue<\/code> method capable of accepting functions as arguments, with the same limitations described above.  I also modified the object so that an empty string can be passed in for the label value, in which case the values will be drawn starting on the first line instead of the second. <\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function DisplayGroup(label,x,y,vw,lc,vc,bc) {\r\n      this.label = label;\r\n      this.xLocation = x;\r\n      this.yLocation = y;\r\n      this.valueWidth = vw;\r\n      this.labelColor = lc;\r\n      this.valueColor = vc;\r\n      this.borderColor = bc;\r\n      if (this.label.length > 0) {\r\n        this.labelHeight = 12;\r\n      } else {\r\n        this.labelHeight = 0;\r\n      }\r\n      this.maxLabelWidth = 0;\r\n      this.valueCount = 0;\r\n      this.valueList = []; \/\/new Array();\r\n      this.define = function(label,x,y,vw,lc,vc,bc) {\r\n        \/\/used to define outside of an object\r\n        this.label = label;\r\n        this.xLocation = x;\r\n        this.yLocation = y;\r\n        this.valueWidth = vw;\r\n        this.labelColor = lc;\r\n        this.valueColor = vc;\r\n        this.borderColor = bc;\r\n        if (this.label.length > 0) {\r\n          this.labelHeight = 12;\r\n        } else {\r\n          this.labelHeight = 0;\r\n        }\r\n      };\r\n      this.addValue = function(value,label,format,places) {\r\n        var tempValue = '';\r\n        if (typeof value === \"function\") {\r\n          tempValue = value.call(tempValue);\r\n        } else {\r\n          tempValue = value;\r\n        }\r\n        var v = new DisplayValue(tempValue,label,format,places);\r\n        this.valueList.push(v);\r\n        this.valueCount++;\r\n        globalCTX.font = \"12px Arial\";\r\n        var w = v.getWidth();\r\n        if (w > this.maxLabelWidth) {\r\n          this.maxLabelWidth = w;\r\n        }\r\n      };\r\n      this.drawBasic = function() {\r\n        this.height = (this.valueCount * 12) + this.labelHeight + 3;\r\n        this.width = this.maxLabelWidth + this.valueWidth + 8;\r\n        globalCTX.strokeStyle = this.borderColor;\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(this.xLocation+0.5,this.yLocation+0.5);\r\n        globalCTX.lineTo(this.xLocation+this.width+0.5,this.yLocation+0.5);\r\n        globalCTX.lineTo(this.xLocation+this.width+0.5,this.yLocation+this.height+0.5);\r\n        globalCTX.lineTo(this.xLocation+0.5,this.yLocation+this.height+0.5);\r\n        globalCTX.lineTo(this.xLocation+0.5,this.yLocation+0.5);\r\n        globalCTX.stroke();\r\n        globalCTX.font = \"12px Arial\";\r\n        globalCTX.fillStyle = this.labelColor;\r\n        if (this.label.length > 0) {\r\n          globalCTX.textAlign = \"center\";\r\n          globalCTX.fillText(this.label,this.xLocation+(this.width*0.5),this.yLocation+12);\r\n        }\r\n        globalCTX.textAlign = \"right\";\r\n        for (var i=0; i<this.valueCount; i++) {\r\n          var l = this.valueList[i].label;\r\n          var x = this.xLocation+this.maxLabelWidth+5;\r\n          var y = this.yLocation+(i*12)+this.labelHeight+12;\r\n          \/\/globalCTX.fillText(valueList[i].label,this.xLocation+this.maxLabelWidth+3,this.yLocation+(i*12)+12);\r\n          globalCTX.fillText(l,x,y);\r\n        }\r\n        globalCTX.fillStyle = this.valueColor;\r\n        globalCTX.textAlign = \"left\";\r\n        for (i=0; i<this.valueCount; i++) {\r\n          this.valueList[i].drawValue(this.xLocation+this.maxLabelWidth+8,this.yLocation+(i*12)+this.labelHeight+12);\r\n        }\r\n      };\r\n    }  \/\/DisplayGroup\r\n<\/pre>\n<p>Here is the streamlined code for the Queue component, from yesterday.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function QueueComponent(displayDelay) {\r\n      setOfComponents.push(this);\r\n      this.componentID = getNewID();\r\n      this.componentName = \"Queue\";\r\n      this.currentEntryEntityID = \"\";\r\n      this.countInQueue = 0;\r\n      this.exitResidenceTime = \"\";\r\n      this.nextComponent = null;\r\n      this.entityQueue = [];\r\n\r\n      this.drawData = function() {\r\n      };\r\n      this.assignNextComponent = function(next) {\r\n        this.nextComponent = next;\r\n      };\r\n      this.forwardEntity = function() {\r\n        if (this.nextComponent.isOpen()) {\r\n          var entity = this.entityQueue.pop();\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            this.currentExitEntityID = entity.entityID;\r\n            displayProgressText(\"Queue comp. \"+this.componentID+\" forwards entity: \"+this.currentExitEntityID+\" 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        this.entityQueue.unshift(entity);\r\n        this.countInQueue++;\r\n        \/\/display what was done\r\n        this.currentEntryEntityID = entity.entityID;\r\n        displayProgressText(\"Queue comp. \"+this.componentID+\" receives entity: \"+this.currentEntryEntityID+\" at time \"+globalSimClock.toFixed(6));\r\n        if (this.nextComponent.isOpen()) {\r\n          this.forwardEntity();\r\n        }\r\n      };      \r\n    }  \/\/QueueComponent\r\n<\/pre>\n<p>Here is how a queue component and all of the display mechanisms are instantiated.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/declare the Queue component itself\r\n    var queue1 = new QueueComponent();\r\n\r\n    \/\/declare the external display object\r\n    var queue1DataGroup = new DisplayGroup(\"Queue\",175,79,80,\"#00FFFF\",\"#FF0000\",\"#FFFF00\",0.0);\r\n\r\n    \/\/set up the values to be displayed within\r\n    var gtemp = queue1.getComponentID();\r\n    queue1DataGroup.addValue(gtemp,\"Comp. ID\",\"integer\");\r\n    gtemp = queue1.getCountInQueue();\r\n    queue1DataGroup.addValue(gtemp,\"# In Queue\",\"integer\");\r\n\r\n    \/\/create an external custom update function\r\n    \/\/this is where JavaScript gets annoying\r\n    function queue1DataGroupUpdate() {\r\n      var temp = queue1.getComponentID();\r\n      queue1DataGroup.valueList[0].updateValue(temp);\r\n      temp = queue1.getCountInQueue();\r\n      queue1DataGroup.valueList[1].updateValue(temp);\r\n    };\r\n<\/pre>\n<p>Finally, the requisite calls to update and draw the values are made for every screen refresh.  In theory these calls can be added to a global array like the components are, which would allow them to also be invoked in a loop.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      \/\/draw everything\r\n      clearCanvas(\"#000000\");\r\n      var numComponents = setOfComponents.length;\r\n      for (i=0; i<numComponents; i++) {\r\n        setOfComponents[i].drawData();\r\n      }\r\n      queue1DataGroupUpdate();\r\n      queue1DataGroup.drawBasic();\r\n<\/pre>\n<p>As described previously, much of this overhead is due to the fact that JavaScript enforces passing simple data types by value, which means we have to go out of our way to provide updates.  Beyond that we have to define everything about the external display mechanisms before using them.<\/p>\n<p>Tomorrow I'll expand these definitions to support display groups that are visible only for a limited time when triggered by a change in the displayed values.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The exercise of writing out requirements and mulling them over yielded the desired insights about how to proceed. Today I&#8217;ll describe the external display mechanism, which blindly redraws all of the desired values with every global redraw event and in &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2016\/11\/01\/a-simple-discrete-event-simulation-part-34\/\">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],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1108"}],"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=1108"}],"version-history":[{"count":2,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1108\/revisions"}],"predecessor-version":[{"id":1111,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1108\/revisions\/1111"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1108"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1108"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1108"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}