├── ui ├── shell ├── images │ ├── server.png │ ├── customers.png │ ├── odometer.png │ ├── settings.gif │ ├── splitter.png │ └── orange-arrow.gif ├── css │ └── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ └── ui-bg_highlight-soft_75_ffe45c_1x100.png ├── test.html ├── panel.css ├── simpanel.js ├── panel.html └── queue-offline.js ├── .istanbul.yml ├── queuing ├── images │ ├── banner.png │ ├── server.png │ ├── customers.png │ ├── door_out.png │ ├── odometer.png │ ├── settings.gif │ ├── splitter.png │ ├── background.gif │ └── orange-arrow.gif ├── todo.txt ├── css │ ├── images │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ ├── ui-icons_ffffff_256x240.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ └── ui-bg_highlight-soft_75_ffe45c_1x100.png │ └── queuing.css ├── tests │ └── test.js ├── src │ ├── monitor_model.js │ ├── source_model.js │ ├── sink_model.js │ ├── splitter_model.js │ ├── server_model.js │ ├── queue-offline.js │ ├── image_view.js │ ├── models.js │ └── splitter_view.js └── release │ └── makerelease.sh ├── .babelrc ├── docs ├── contact.rst ├── images │ ├── entity-prototype.png │ └── statistics-time-series.png ├── simulator.rst ├── _static │ ├── examples.css │ ├── traffic_lights.html │ └── buffet_restaurant.html ├── _templates │ └── layout.html ├── tutorial.rst ├── download.rst ├── examples │ ├── buffet_restaurant.js │ ├── producers-consumers.rst │ ├── traffic_lights.js │ └── buffet-restaurant.rst ├── index.rst ├── random.rst ├── Makefile ├── conf.py ├── events.rst └── entities.rst ├── .editorconfig ├── examples ├── examples.html ├── examples.css ├── bower.json ├── buffet_restaurant.js ├── traffic_lights.html ├── mmc.js ├── buffet_restaurant.html └── traffic_lights.js ├── .travis.yml ├── src ├── test │ ├── queue-tests.js │ ├── sim-tests.js │ ├── utils.js │ ├── pqueue-tests.js │ ├── event-tests.js │ ├── stats-tests.js │ ├── timer-tests.js │ └── message-tests.js ├── lib │ ├── model.js │ ├── queues.js │ ├── request.js │ └── stats.js └── sim.js ├── bower.json ├── .gitignore ├── LICENSE ├── README ├── package.json ├── README.md └── .eslintrc /ui/shell: -------------------------------------------------------------------------------- 1 | ../../v8/samples/build/Debug/shell -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | root: src/ 3 | include-all-sources: true 4 | -------------------------------------------------------------------------------- /ui/images/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/server.png -------------------------------------------------------------------------------- /ui/images/customers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/customers.png -------------------------------------------------------------------------------- /ui/images/odometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/odometer.png -------------------------------------------------------------------------------- /ui/images/settings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/settings.gif -------------------------------------------------------------------------------- /ui/images/splitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/splitter.png -------------------------------------------------------------------------------- /queuing/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/banner.png -------------------------------------------------------------------------------- /queuing/images/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/server.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [ 4 | "transform-function-bind" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /queuing/images/customers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/customers.png -------------------------------------------------------------------------------- /queuing/images/door_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/door_out.png -------------------------------------------------------------------------------- /queuing/images/odometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/odometer.png -------------------------------------------------------------------------------- /queuing/images/settings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/settings.gif -------------------------------------------------------------------------------- /queuing/images/splitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/splitter.png -------------------------------------------------------------------------------- /ui/images/orange-arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/images/orange-arrow.gif -------------------------------------------------------------------------------- /queuing/images/background.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/background.gif -------------------------------------------------------------------------------- /docs/contact.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Contact Us 3 | ============= 4 | 5 | Contact us at *mvarshney (at) gmail (dot) com*. -------------------------------------------------------------------------------- /docs/images/entity-prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/docs/images/entity-prototype.png -------------------------------------------------------------------------------- /queuing/images/orange-arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/images/orange-arrow.gif -------------------------------------------------------------------------------- /docs/images/statistics-time-series.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/docs/images/statistics-time-series.png -------------------------------------------------------------------------------- /queuing/todo.txt: -------------------------------------------------------------------------------- 1 | 2 | - Simulation: show sim and real time 3 | - Stats: check standard deviation 4 | - Forms: input validation 5 | -------------------------------------------------------------------------------- /ui/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /queuing/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /queuing/css/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /queuing/css/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /queuing/css/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /queuing/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /ui/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/ui/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /queuing/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/btelles/simjs-updated/HEAD/queuing/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /docs/simulator.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | SIM.JS Reference Guide 3 | ======================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | entities 9 | request 10 | resources 11 | events 12 | statistics -------------------------------------------------------------------------------- /examples/examples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "4" 5 | - "5" 6 | install: 7 | - npm install 8 | script: 9 | - npm run check 10 | - npm run build 11 | - npm run validate 12 | after_success: 13 | - npm run coveralls 14 | - npm run github-release 15 | -------------------------------------------------------------------------------- /docs/_static/examples.css: -------------------------------------------------------------------------------- 1 | div.form { 2 | background-color: FFCC99; 3 | width: 500px; 4 | margin-top: 5px; 5 | margin-left: 10%; 6 | margin-right: auto; 7 | margin-bottom: 20px; 8 | border: 5px solid FF9900; 9 | } 10 | 11 | div.results { 12 | background-color: FFCC99; 13 | border: 5px solid FF9900; 14 | } -------------------------------------------------------------------------------- /examples/examples.css: -------------------------------------------------------------------------------- 1 | div.form { 2 | background-color: FFCC99; 3 | width: 500px; 4 | margin-top: 5px; 5 | margin-left: 10%; 6 | margin-right: auto; 7 | margin-bottom: 20px; 8 | border: 5px solid FF9900; 9 | } 10 | 11 | div.results { 12 | background-color: FFCC99; 13 | border: 5px solid FF9900; 14 | } -------------------------------------------------------------------------------- /src/test/queue-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import * as Sim from '../sim'; 3 | 4 | test('testFCFSQueueSimple', (t) => { 5 | const q = new Sim.Queue(); 6 | 7 | q.push(10, 10); 8 | q.shift(20); 9 | q.finalize(10); 10 | const report = q.report(); 11 | 12 | t.is(report[0], 1); 13 | t.is(report[1], 10); 14 | }); 15 | -------------------------------------------------------------------------------- /examples/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "homepage": "https://github.com/btelles/simjs-source", 4 | "authors": [ 5 | "Bernardo Telles " 6 | ], 7 | "description": "", 8 | "main": "", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/test/sim-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import * as Sim from '../sim'; 3 | import 'babel-core/register'; 4 | 5 | test('testStartArguments', (t) => { 6 | const sim = new Sim.Sim(); 7 | 8 | class MyEntity extends Sim.Entity { 9 | start(a, b) { 10 | t.is(a, 10); 11 | t.is(b.a, 20); 12 | } 13 | } 14 | 15 | sim.addEntity(MyEntity, null, 10, { a: 20 }); 16 | sim.simulate(100); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /src/lib/model.js: -------------------------------------------------------------------------------- 1 | class Model { 2 | constructor(name) { 3 | this.id = this.constructor._nextId(); 4 | this.name = name || `${this.constructor.name} ${this.id}`; 5 | } 6 | 7 | static get totalInstances() { 8 | return !this._totalInstances ? 0 : this._totalInstances; 9 | } 10 | 11 | static _nextId() { 12 | this._totalInstances = this.totalInstances + 1; 13 | return this._totalInstances; 14 | } 15 | } 16 | 17 | export { Model }; 18 | export default Model; 19 | -------------------------------------------------------------------------------- /ui/test.html: -------------------------------------------------------------------------------- 1 | New Button 2 | - check if there is existing scenario 3 | - pop up dialog confirming to "Discard", "save", "cancel" 4 | 5 | Load 6 | + GUI to input string 7 | 8 | Save 9 | + GUI to show the text string 10 | 11 | Adding object 12 | + tooltip to say names 13 | - make sure that objects are not dropped in the palette 14 | 15 | Delete object 16 | - remove objects from QueueApp.models, QueueApp.views 17 | 18 | 19 | Queue 20 | - number of servers? 21 | - queue discipline? 22 | - bounded queue? 23 | 24 | Monitor 25 | - check which statistics must be shown 26 | 27 | Sim 28 | - check standard deviation -------------------------------------------------------------------------------- /queuing/tests/test.js: -------------------------------------------------------------------------------- 1 | //load('../release/queuing-offline-min.js'); 2 | load('../../src/sim.js'); 3 | load('../../src/request.js'); 4 | load('../../src/queues.js'); 5 | load('../../src/random.js'); 6 | load('../../src/stats.js'); 7 | load('../src/queue-offline.js'); 8 | 9 | var model = '{"until":14400,"seed":1234,"version":"1.0","objects":[{"x":440,"y":88,"type":"sink","name":"sink_1","model":null},{"x":108,"y":70,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":225,"y":66,"type":"queue","name":"queue_1","out":"sink_1","model":{"nservers":1,"mu":1,"infinite":true,"maxqlen":0}}]}'; 10 | 11 | QueueSimulator(model); 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simjs", 3 | "description": "SIM.JS Updated is a general-purpose Discrete Event Simulation library", 4 | "main": "sim.js", 5 | "authors": [ 6 | "btelles@gmail.com" 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "discrete", 11 | "event", 12 | "simulation", 13 | "queue", 14 | "store", 15 | "buffer", 16 | "container" 17 | ], 18 | "homepage": "https://github.com/btelles/simjs-updated", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "examples", 24 | "queuing", 25 | "ui", 26 | "package.json", 27 | "src" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {%- block extrahead %} 4 | {{ super() }} 5 | 17 | {% endblock %} 18 | 19 | {% block footer %} 20 | {{ super() }} 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /src/test/utils.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | function assertAlmost(a, b, eps) { 4 | eps = (typeof eps === 'undefined') ? 1e-7 : eps; 5 | if (isNaN(a) || isNaN(b) || (Math.abs(a - b) > eps)) { 6 | 7 | console.log(`Error: Values ${a} and ${b} are not almost equal`); // eslint-disable-line no-console 8 | console.trace(); // eslint-disable-line no-console 9 | 10 | throw new Error('Stopped on failure'); 11 | } 12 | } 13 | 14 | function assertFail() { 15 | 16 | console.log('Error: Failed'); // eslint-disable-line no-console 17 | console.trace(); // eslint-disable-line no-console 18 | throw new Error('Stopped on failure'); 19 | } 20 | 21 | test('testAssertAlmost', () => { 22 | assertAlmost(5, 7, 2); 23 | }); 24 | 25 | export { assertFail, assertAlmost }; 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | test/.temp 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | lib-cov 17 | .nyc_output 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | # OS generated files 39 | .DS_Store 40 | .DS_Store? 41 | .Spotlight-V100 42 | .Trashes 43 | ehthumbs.db 44 | Thumbs.db 45 | 46 | .idea 47 | -------------------------------------------------------------------------------- /ui/panel.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 62.5%; 3 | } 4 | 5 | #toolbar { 6 | padding: 10px 4px; 7 | } 8 | 9 | #canvas { 10 | margin: 10px; 11 | } 12 | 13 | #progressbar { 14 | width: 400px; 15 | float: left; 16 | } 17 | 18 | input.text { margin-bottom:12px; width:95%; padding: .4em; } 19 | fieldset { padding:0; border:0; margin-top:5px; } 20 | 21 | pre { 22 | font-size: 1.4em; 23 | } 24 | 25 | #results { 26 | background: #F7B64A; 27 | width: 600px; 28 | margin: 10px; 29 | } 30 | .stats { 31 | display: inline-block; 32 | border: 1px solid #FAF6AA; 33 | padding: 0.4em; 34 | margin: 0.4em; 35 | background: #F7D68A; 36 | } 37 | .stats_table { 38 | border: 1px; 39 | font-size: 1.2em; 40 | border-collapse:collapse; 41 | background-color: #FAF6AA; 42 | } 43 | 44 | #server_stats, #monitor_stats { 45 | display: none; 46 | } 47 | 48 | #settings li { 49 | border: 1px solid red; 50 | padding: 0.2em; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/sim.js: -------------------------------------------------------------------------------- 1 | import { Sim, Entity, Event, Buffer, Facility, Store, argCheck } from './lib/sim.js'; 2 | import { DataSeries, TimeSeries, Population } from './lib/stats.js'; 3 | import { Request } from './lib/request.js'; 4 | import { PQueue, Queue } from './lib/queues.js'; 5 | import { Random } from './lib/random.js'; 6 | import { Model } from './lib/model.js'; 7 | 8 | export { Sim, Entity, Event, Buffer, Facility, Store }; 9 | export { DataSeries, TimeSeries, Population }; 10 | export { Request }; 11 | export { PQueue, Queue, argCheck }; 12 | export { Random }; 13 | export { Model }; 14 | 15 | if (typeof window !== 'undefined') { 16 | window.Sim = { 17 | argCheck: argCheck, 18 | Buffer: Buffer, 19 | DataSeries: DataSeries, 20 | Entity: Entity, 21 | Event: Event, 22 | Facility: Facility, 23 | Model: Model, 24 | PQueue: PQueue, 25 | Population: Population, 26 | Queue: Queue, 27 | Random: Random, 28 | Request: Request, 29 | Sim: Sim, 30 | Store: Store, 31 | TimeSeries: TimeSeries 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Maneesh Varshney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /docs/tutorial.rst: -------------------------------------------------------------------------------- 1 | .. _tutorial-main: 2 | 3 | =================== 4 | SIM.JS Tutorial 5 | =================== 6 | 7 | :ref:`example-traffic-lights` models the flow of cars at an intersection with traffic lights. The objective of the simulation is to study the average wait duration for cars at the intersection and the average number of cars waiting at the intersection. 8 | 9 | * The complete source code is available `here `_. 10 | * You can play with the simulation model `here `_. 11 | 12 | :ref:`example-buffet-restaurant` models buffet-style restaurant. Customer arrive at restaurant, pick up food from buffet area (we will will assume only one food item: salad), reach cashier for payment and leave the restaurant. The objective of the simulation is to study the average time spent by customers at the restaurant, and the average number of customers inside the restaurant. 13 | 14 | * The complete source code is available `here `_. 15 | * You can play with the simulation model `here `_. 16 | 17 | 18 | Tutorials 19 | =========== 20 | 21 | .. toctree:: 22 | :maxdepth: 1 23 | 24 | examples/traffic-lights 25 | examples/buffet-restaurant -------------------------------------------------------------------------------- /src/test/pqueue-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import * as Sim from '../sim'; 3 | 4 | test('testPQueue', (t) => { 5 | function assertArrays(a, b) { 6 | t.is(a.length, b.length); 7 | 8 | for (let i = 0; i < a.length; i++) { 9 | 10 | t.is(a[i], b[i]); 11 | } 12 | } 13 | 14 | const dataset = [[], 15 | 16 | [0], 17 | [1], 18 | [1, 2], 19 | [2, 1], 20 | [1, 2, 3], 21 | [3, 2, 1], 22 | [3, 1, 2], 23 | [1, 2, 3, 4], 24 | [4, 3, 1, 2], 25 | [1, 1, 1, 1], 26 | [1, 1, 3, 1, 1], 27 | [9, 8, 7, 6, 5, 4, 3, 2, 1], 28 | [9, 8, 7, 6, 5, 4, 3, 2, 1, 10]]; 29 | 30 | for (let i = 0; i < dataset.length; i++) { 31 | 32 | const arr = dataset[i]; 33 | 34 | // insert 35 | const pq = new Sim.PQueue(); 36 | 37 | for (let j = 0; j < arr.length; j++) { 38 | 39 | pq.insert(new Sim.Request(0, 0, arr[j])); 40 | } 41 | 42 | const out = []; 43 | 44 | while (true) { // eslint-disable-line no-constant-condition 45 | const a = pq.remove(); 46 | 47 | if (a === null) break; 48 | out.push(a.deliverAt); 49 | } 50 | 51 | assertArrays(arr.sort((a, b) => { return a - b; }), out); 52 | 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /queuing/src/monitor_model.js: -------------------------------------------------------------------------------- 1 | class MonitorModel { 2 | constructor(view) { 3 | this.view = view; 4 | this.dest = null; 5 | this.statTable = $('#monitor_stats').clone().attr('id', view.name); 6 | this.statTable.find('h2').text(view.name); 7 | 8 | $("#results").append(this.statTable); 9 | this.stat = [ 10 | this.statTable.find('#arrival'), 11 | this.statTable.find('#inter'), 12 | this.statTable.find('#interd')]; 13 | } 14 | 15 | jsonify() { 16 | return null; 17 | } 18 | 19 | start() { 20 | this.entity = QueueApp.sim.addEntity(MonitorEntity); 21 | } 22 | 23 | connect() { 24 | if (this.dest) this.entity.dest = this.dest.entity; 25 | } 26 | 27 | showStats() { 28 | const m = this.entity.monitor; 29 | 30 | this.stat[0].text(m.count()); 31 | this.stat[1].text(m.average().toFixed(3)); 32 | this.stat[2].text(m.deviation().toFixed(3)); 33 | } 34 | 35 | showSettings(x, y) { 36 | const d = $('#monitor_form'); 37 | QueueApp.form_view = this.view; 38 | d.dialog('option', {title: this.view.name, position: [x, y]}) 39 | .dialog('open'); 40 | } 41 | 42 | saveSettings(dialog) { 43 | } 44 | 45 | unlink() { 46 | this.statTable.remove(); 47 | } 48 | } 49 | 50 | /*-------------------------*/ 51 | class MonitorEntity extends Sim.Entity { 52 | start() { 53 | this.monitor = new Sim.TimeSeries(); 54 | } 55 | 56 | arrive() { 57 | this.monitor.record(1, this.time()); 58 | if (this.dest) this.dest.arrive(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/download.rst: -------------------------------------------------------------------------------- 1 | .. _download: 2 | 3 | ============================= 4 | Download SIM.JS, random.js 5 | ============================= 6 | 7 | The current version of SIM.JS is |version|. 8 | 9 | The **sim.js** file contains the simulator kernel and includes the **random.js** library. You can also download the **random.js** library separately from the links below. 10 | 11 | Both libraries are available as debug and release versions. The release version has stripped out code to check and validate arguments, and is minified (with Google's `Closure Compiler `_). 12 | 13 | Download links (right-click and save): 14 | 15 | * **sim.js** [:download:`Debug version <../release/sim-0.25-debug.js>`] [:download:`Release version <../release/sim-0.25.js>`] (includes random.js) 16 | * **random.js** [:download:`Debug version <../release/random-0.25-debug.js>`] [:download:`Release version <../release/random-0.25.js>`] 17 | 18 | 19 | Source Code Access 20 | ==================== 21 | 22 | The source code is hosted at ``code.google.com``: `http://code.google.com/p/simjs-source/ `_. 23 | 24 | Use this command to anonymously check out the latest project source code: 25 | 26 | .. topic:: SVN Checkout 27 | 28 | # Non-members may check out a read-only working copy anonymously over HTTP. 29 | 30 | *svn checkout http://simjs-source.googlecode.com/svn/trunk/ simjs-source-read-only* 31 | 32 | 33 | License 34 | =========== 35 | 36 | The SIM.JS library is licensed as Lesser GPL (LGPL). 37 | -------------------------------------------------------------------------------- /queuing/src/source_model.js: -------------------------------------------------------------------------------- 1 | class SourceModel { 2 | constructor(view) { 3 | this.view = view; 4 | this.lambda = 0.25; 5 | this.dest = null; 6 | this.view.image.attr({title: `Interarrival rate = ${this.lambda}`}); 7 | } 8 | 9 | jsonify() { 10 | return { 11 | lambda: this.lambda 12 | }; 13 | } 14 | 15 | start() { 16 | this.entity = QueueApp.sim.addEntity(SourceEntity, null, this.lambda); 17 | } 18 | 19 | connect() { 20 | this.entity.dest = this.dest ? this.dest.entity : null; 21 | } 22 | 23 | showSettings() { 24 | const d = $('#source_form'); 25 | QueueApp.form_view = this.view; 26 | d.find('#source_form_rate').val(this.lambda); 27 | 28 | d.show().position({ 29 | of: $(this.view.image.node), 30 | at: 'center center', 31 | my: 'left top' 32 | }); 33 | } 34 | 35 | saveSettings(dialog) { 36 | const d = $('#source_form'); 37 | this.lambda = d.find('#source_form_rate').val(); 38 | this.view.image.attr({title: `Interarrival rate = ${this.lambda}`}); 39 | } 40 | 41 | unlink() { 42 | this.view = null; 43 | } 44 | 45 | showStats() { 46 | this.view.showCounters(NaN, this.entity.generated); 47 | } 48 | } 49 | 50 | /*-------------------------*/ 51 | class SourceEntity extends Sim.Entity { 52 | start(lambda) { 53 | this.lambda = lambda; 54 | this.setTimer(0).done(this.traffic); 55 | this.generated = 0; 56 | } 57 | 58 | traffic() { 59 | if (!this.dest) return; 60 | this.dest.arrive(this.time()); 61 | 62 | this.generated ++; 63 | 64 | const duration = QueueApp.random.exponential(this.lambda); 65 | 66 | this.setTimer(duration).done(this.traffic); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/examples/buffet_restaurant.js: -------------------------------------------------------------------------------- 1 | function buffetRestaurantSimulation( 2 | BuffetCapacity, 3 | PreparationTime, 4 | MeanArrival, 5 | CashierTime, 6 | Seed, 7 | Simtime) 8 | { 9 | var sim = new Sim.Sim(); 10 | var stats = new Sim.Population(); 11 | var cashier = new Sim.Facility('Cashier'); 12 | var buffet = new Sim.Buffer('Buffet', BuffetCapacity); 13 | var random = new Random(Seed); 14 | 15 | class Customer extends Sim.Entity { 16 | start() { 17 | this.order(); 18 | 19 | var nextCustomerAt = random.exponential (1.0 / MeanArrival); 20 | this.setTimer(nextCustomerAt).done(this.start); 21 | }, 22 | 23 | order() { 24 | sim.log("Customer ENTER at " + this.time()); 25 | stats.enter(this.time()); 26 | 27 | this.getBuffer(buffet, 1).done(() => { 28 | sim.log("Customer at CASHIER " + this.time() + " (entered at " + this.callbackData + ")"); 29 | var serviceTime = random.exponential(1.0 / CashierTime); 30 | this.useFacility(cashier, serviceTime).done(function () { 31 | sim.log("Customer LEAVE at " + this.time() + " (entered at " + this.callbackData + ")"); 32 | stats.leave(this.callbackData, this.time()); 33 | }).setData(this.callbackData); 34 | }).setData(this.time()); 35 | } 36 | }; 37 | 38 | class Chef extends Sim.Entity { 39 | start() { 40 | this.putBuffer(buffet, BuffetCapacity - buffet.current()); 41 | this.setTimer(PreparationTime).done(this.start); 42 | } 43 | }; 44 | 45 | sim.addEntity(Customer); 46 | sim.addEntity(Chef); 47 | 48 | // Uncomment these line to display logging information 49 | // sim.setLogger(function (msg) { 50 | // document.write(msg); 51 | // }); 52 | 53 | sim.simulate(Simtime); 54 | 55 | return [stats.durationSeries.average(), 56 | stats.durationSeries.deviation(), 57 | stats.sizeSeries.average(), 58 | stats.sizeSeries.deviation()]; 59 | 60 | } -------------------------------------------------------------------------------- /queuing/src/sink_model.js: -------------------------------------------------------------------------------- 1 | class SinkModel { 2 | constructor(view) { 3 | this.view = view; 4 | this.entity = null; 5 | 6 | this.statTable = $('#sink_stats').clone().attr('id', view.name); 7 | this.statTable.find('h2').text(view.name); 8 | 9 | $("#results").append(this.statTable); 10 | this.stat = [ 11 | this.statTable.find('#depart'), 12 | this.statTable.find('#pop'), 13 | this.statTable.find('#popd'), 14 | this.statTable.find('#stay'), 15 | this.statTable.find('#stayd') 16 | ]; 17 | } 18 | 19 | jsonify() { 20 | return null; 21 | } 22 | 23 | start() { 24 | this.entity = QueueApp.sim.addEntity(SinkEntity); 25 | 26 | } 27 | 28 | connect() { 29 | 30 | } 31 | 32 | showSettings(x, y) { 33 | const d = $('#sink_form'); 34 | QueueApp.form_view = this.view; 35 | d.show().position({ 36 | of: $(this.view.image.node), 37 | at: 'center center', 38 | my: 'left top' 39 | }); 40 | } 41 | 42 | saveSettings(dialog) { 43 | 44 | } 45 | 46 | showStats() { 47 | const p = this.entity.population; 48 | this.stat[0].text(p.durationSeries.count()); 49 | this.stat[1].text(p.sizeSeries.average().toFixed(3)); 50 | this.stat[2].text(p.sizeSeries.deviation().toFixed(3)); 51 | this.stat[3].text(p.durationSeries.average().toFixed(3)); 52 | this.stat[4].text(p.durationSeries.deviation().toFixed(3)); 53 | 54 | this.view.showCounters(p.durationSeries.count(), NaN); 55 | } 56 | 57 | unlink() { 58 | this.statTable.remove(); 59 | this.view = null; 60 | this.stat = null; 61 | } 62 | } 63 | 64 | /***************************************************/ 65 | 66 | class SinkEntity extends Sim.Entity { 67 | start() { 68 | this.population = new Sim.Population(); 69 | } 70 | 71 | arrive(stamp) { 72 | if (!stamp) stamp = 0; 73 | this.population.enter(stamp); 74 | this.population.leave(stamp, this.time()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README 2 | 3 | *SIM.JS Updated is a general-purpose Discrete Event Simulation library written entirely in JavaScript.* 4 | 5 | The original implementation was written in 2011, and can be found at http://www.simjs.com 6 | 7 | SIM.JS is a library for modeling discrete time event systems: 8 | 9 | * The library provides constructs to create Entities which are the active 10 | actors in the system and encapsulate the state and logic of the system 11 | operations. 12 | 13 | * The entities contend for *resources*, which can be Facilities (services 14 | that are requested by entities; facilities have a maximum limit on number 15 | of concurrent users) and Buffers (resources that can store finite amount 16 | of tokens; entities store or retrieve tokens from the buffers). 17 | 18 | * The entities communicate by waiting on Events or by sending Messages. 19 | 20 | * Statistics recording and analysis capability is provided by Data Series 21 | (collection of discrete, time-independent observations), Time Series 22 | (collection of discrete, time-dependent observations) and Population 23 | (the behavior of population growth and decline). 24 | 25 | * SIM.JS also provides a random number generation library to generate seeded 26 | random variates from various distributions, including uniform, exponential, 27 | normal, gamma, pareto and others. 28 | 29 | *SIM.JS is written in _idiomatic_ EcmaScript 2015 JavaScript*. The library is 30 | written in event-based design paradigm: the changes in system states are notified 31 | via callback functions. The design takes advantage of the powerful feature sets 32 | of JavaScript: prototype based inheritance, first-class functions, closures, 33 | anonymous functions, runtime object modifications and so on. Of course, a 34 | knowledge of these principles is not required (a lot of this is behind the scenes), 35 | but we do certainly hope that using SIM.JS will be a pleasurable experience for 36 | the amateur as well as the experienced JavaScript programmer. -------------------------------------------------------------------------------- /queuing/src/splitter_model.js: -------------------------------------------------------------------------------- 1 | class SplitterModel { 2 | constructor(view) { 3 | this.view = view; 4 | this.prob = 0.5; 5 | this.dest = [null, null]; 6 | const tooltip = ['Splitting', this.prob*100, '% / ', 100 - (this.prob * 100), '%'].join(' '); 7 | view.image.attr({title: tooltip}); 8 | } 9 | 10 | jsonify() { 11 | return {prob: this.prob}; 12 | } 13 | 14 | start() { 15 | this.entity = QueueApp.sim.addEntity(SplitterEntity, null, this.prob); 16 | } 17 | 18 | connect() { 19 | this.entity.dest1 = this.dest[0] ? this.dest[0].entity : null; 20 | this.entity.dest2 = this.dest[1] ? this.dest[1].entity : null; 21 | } 22 | 23 | showSettings(x, y) { 24 | const d = $('#splitter_form'); 25 | QueueApp.form_view = this.view; 26 | d.find('#splitter_form_perc').val(this.prob); 27 | 28 | d.show().position({ 29 | of: $(this.view.image.node), 30 | at: 'center center', 31 | my: 'left top' 32 | }); 33 | } 34 | 35 | saveSettings(dialog) { 36 | const d = $('#splitter_form'); 37 | this.prob = d.find('#splitter_form_perc').val(); 38 | const msg = ['Splitting', this.prob*100, '% / ', 100 - (this.prob * 100), '%'].join(' '); 39 | this.view.image.attr({title: msg}); 40 | } 41 | 42 | unlink() { 43 | this.view = null; 44 | } 45 | 46 | showStats() { 47 | /* 48 | var msg = ['In:', this.entity.arrived, 49 | 'Out [1]:', this.entity.to1, 50 | 'Out [2]:', this.entity.to2].join(' '); 51 | this.view.counters.attr({text: msg}); 52 | */ 53 | } 54 | } 55 | 56 | /*-------------------------*/ 57 | class SplitterEntity extends Sim.Entity { 58 | start(prob) { 59 | this.prob = prob; 60 | this.arrived = 0; 61 | this.to1 = 0; 62 | this.to2 = 0; 63 | } 64 | 65 | arrive(stamp) { 66 | this.arrived ++; 67 | const r = QueueApp.random.uniform(0.0, 1.0); 68 | if (r < this.prob) { 69 | this.to1 ++; 70 | if (this.dest1) this.dest1.arrive(stamp); 71 | } else { 72 | this.to2 ++; 73 | if (this.dest2) this.dest2.arrive(stamp); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/buffet_restaurant.js: -------------------------------------------------------------------------------- 1 | function buffetRestaurantSimulation( 2 | BuffetCapacity, 3 | PreparationTime, 4 | MeanArrival, 5 | CashierTime, 6 | Seed, 7 | Simtime) 8 | { 9 | var sim = new Sim.Sim(); 10 | var stats = new Sim.Population(); 11 | var cashier = new Sim.Facility('Cashier'); 12 | var buffet = new Sim.Buffer('Buffet', BuffetCapacity); 13 | var random = new Sim.Random(Seed); 14 | 15 | class Customer extends Sim.Entity { 16 | start() { 17 | this.order(); 18 | 19 | var nextCustomerAt = random.exponential (1.0 / MeanArrival); 20 | this.setTimer(nextCustomerAt).done(this.start); 21 | } 22 | 23 | order() { 24 | sim.log("Customer ENTER at " + this.time()); 25 | stats.enter(this.time()); 26 | 27 | this.getBuffer(buffet, 1).done(() => { 28 | sim.log("Customer at CASHIER " + this.time() + " (entered at " + this.callbackData + ")"); 29 | var serviceTime = random.exponential(1.0 / CashierTime); 30 | this.useFacility(cashier, serviceTime).done(() => { 31 | sim.log("Customer LEAVE at " + this.time() + " (entered at " + this.callbackData + ")"); 32 | stats.leave(this.callbackData, this.time()); 33 | }).setData(this.callbackData); 34 | }).setData(this.time()); 35 | } 36 | }; 37 | 38 | class Chef extends Sim.Entity { 39 | start() { 40 | this.putBuffer(buffet, BuffetCapacity - buffet.current()); 41 | this.setTimer(PreparationTime).done(this.start); 42 | } 43 | }; 44 | 45 | sim.addEntity(Customer); 46 | sim.addEntity(Chef); 47 | 48 | // Uncomment these line to display logging information 49 | // sim.setLogger(function (msg) { 50 | // document.write(msg); 51 | // }); 52 | 53 | sim.simulate(Simtime); 54 | 55 | return [stats.durationSeries.average(), 56 | stats.durationSeries.deviation(), 57 | stats.sizeSeries.average(), 58 | stats.sizeSeries.deviation()]; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simjs", 3 | "version": "2.0.3", 4 | "description": "SIM.JS Updated is a general-purpose Discrete Event Simulation library", 5 | "main": "sim.js", 6 | "files": [ 7 | "sim.js" 8 | ], 9 | "keywords": [ 10 | "discrete", 11 | "event", 12 | "simulation", 13 | "queue", 14 | "store", 15 | "buffer", 16 | "container" 17 | ], 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/btelles/simjs-updated" 22 | }, 23 | "scripts": { 24 | "audit": "nsp check package", 25 | "build": "browserify -d -e src/sim.js -t babelify --outfile sim.js", 26 | "check": "npm run audit && npm outdated --depth 0", 27 | "coverage": "nyc --reporter=lcov --reporter=text --reporter=html npm test", 28 | "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage", 29 | "github-release": "conventional-github-releaser -p angular", 30 | "lint": "eslint src", 31 | "test": "ava --require babel-core/register -S src/test/*-tests.js", 32 | "validate": "npm run lint && npm test", 33 | "prepare": "npm run build && npm run validate && npm run check", 34 | "pp": "npm run prepare", 35 | "major": "pmm major", 36 | "minor": "pmm minor", 37 | "patch": "pmm patch" 38 | }, 39 | "author": { 40 | "name": "Bernardo Telles", 41 | "email": "btelles@google.com", 42 | "url": "https://github.com/btelles" 43 | }, 44 | "pre-commit": [ 45 | "validate" 46 | ], 47 | "dependencies": {}, 48 | "devDependencies": { 49 | "ava": "^0.14.0", 50 | "babel-cli": "^6.5.1", 51 | "babel-core": "^6.5.2", 52 | "babel-eslint": "^6.0.0", 53 | "babel-plugin-transform-es2015-modules-commonjs": "^6.7.7", 54 | "babel-plugin-transform-function-bind": "^6.5.2", 55 | "babel-preset-es2015": "^6.5.0", 56 | "babelify": "^7.3.0", 57 | "browserify": "^13.0.0", 58 | "conventional-github-releaser": "^1.1.0", 59 | "coveralls": "^2.11.9", 60 | "cz-conventional-changelog": "^1.1.5", 61 | "eslint": "^2.5.1", 62 | "nsp": "^2.2.0", 63 | "nyc": "^6.1.1", 64 | "pmm": "^1.3.0", 65 | "pre-commit": "^1.1.2" 66 | }, 67 | "config": { 68 | "commitizen": { 69 | "path": "./node_modules/cz-conventional-changelog" 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/_static/traffic_lights.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 38 | 39 | 40 | 41 | 44 | 45 |
46 | 47 | 49 | 51 | 53 | 55 |
Time that the traffic light is green (min): 48 |
Mean interval between vehicle arrivals (min): 50 |
Random number generator seed: 52 |
Simulation time (min): 54 |
56 | 57 |
58 | 59 |
60 | Clear
61 |
62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/traffic_lights.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 38 | 39 | 40 | 41 | 44 | 45 |
46 | 47 | 49 | 51 | 53 | 55 |
Time that the traffic light is green (min): 48 |
Mean interval between vehicle arrivals (min): 50 |
Random number generator seed: 52 |
Simulation time (min): 54 |
56 | 57 |
58 | 59 |
60 | Clear
61 |
62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/mmc.js: -------------------------------------------------------------------------------- 1 | 2 | if (this.load) { 3 | load('../sim.js'); 4 | document = { 5 | write: function (str) { 6 | print(str); 7 | } 8 | }; 9 | } else { 10 | document.write("
");
11 | }
12 | 
13 | 
14 | function RunMMc(lambda, mu, nservers, simtime, seed) {
15 |   var sim = new Sim.Sim();
16 |   var server = new Sim.Facility('server', Sim.Facility.FCFS, nservers);
17 |   var rand = new Sim.Random((typeof seed !== 'undefined') ? seed : 12345);
18 |   class Generator extends Sim.Entity {
19 |     start() {
20 |       this.useFacility(server, rand.exponential(mu));
21 |       this.setTimer(rand.exponential(lambda)).done(this.start);
22 |     }
23 |   };
24 | 
25 |   sim.addEntity(Generator);
26 |   sim.simulate((simtime !== undefined) ? simtime : 20000);
27 |   server.finalize(sim.time());
28 | 
29 |   /** Expected results */
30 |   var rho = lambda / mu;
31 |   var Expected = {
32 |     // Expected number of users in system
33 |     N: rho / (1.0 - rho),
34 |     // Variance of number of users in system
35 |     S: rho / ( (1 - rho) * (1 - rho)),
36 | 
37 |     // Expected number of requests in the server
38 |     Ns: rho,
39 |     // Expected number of requests in the queue
40 |     Nq: rho * rho / (1 - rho),
41 | 
42 |     // Total expected waiting time (queue + service)
43 |     T: 1.0 / (mu - lambda),
44 |     // Expected waiting time in queue
45 |     W: rho / (mu - lambda)
46 |   };
47 | 
48 |   /** Obtained results */
49 |   var Obtained = {
50 |     N: server.systemStats().sizeSeries.average(),
51 |     S: server.systemStats().sizeSeries.variance(),
52 |     Ns: server.usage() / sim.time(),
53 |     Nq: server.queueStats().sizeSeries.average(),
54 |     T: server.systemStats().durationSeries.average(),
55 |     W: server.queueStats().durationSeries.average()
56 |   };
57 | 
58 |   document.write("M/M/c Simulation (lambda=" + lambda + ", mu=" + mu + ", c=" + nservers + ")\n");
59 |   var fields = ['N', 'S', 'Ns', 'Nq', 'T', 'W'];
60 |   for (var field in fields) {
61 |     var name = fields[field];
62 |     document.write(name
63 |         + ": Expected = " + Expected[name].toFixed(2)
64 |         + "   Obtained = " + Obtained[name].toFixed(2)
65 |         + "\n");
66 |   }
67 | }
68 | 
69 | 
70 | var cases = [[0.2, 1.0],
71 |              [0.3, 1.0],
72 |              [0.4, 1.0],
73 |              [0.5, 1.0],
74 |              [0.6, 1.0],
75 |              [0.7, 1.0],
76 |              [0.8, 1.0],
77 |              [0.9, 1.0]];
78 | for (var c in cases) {
79 |   cc = cases[c];
80 |   var start = new Date().getTime();
81 |   var a = RunMMc(cc[0], cc[1], 1, 100000, 543);
82 |   var end = new Date().getTime();
83 |   document.write("completed in " + (end - start) + " ms\n\n");
84 |   break;
85 | }
86 | //RunMMc(0.5, 1.0, 1, 100, 3);
87 | 
88 | 
89 | 
90 | 
91 | 
92 | 
93 | 
94 | 


--------------------------------------------------------------------------------
/docs/_static/buffet_restaurant.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 	
 3 | 	
 4 | 	
 5 | 	
 6 | 	
 7 | 	
 8 | 	
50 | 		
51 | 	
52 | 	
53 | 		
56 | 		
57 | 		
58 | 59 | 61 | 63 | 65 | 67 | 69 | 71 |
Buffer Capacity: 60 |
Salad preparation time (min): 62 |
Mean interval between customer arrivals (min): 64 |
Mean cashier transaction time (min): 66 |
Random number generation seed: 68 |
Simulation time (min): 70 |
72 | 73 |
74 | 75 |
76 | Clear
77 |
78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/examples/producers-consumers.rst: -------------------------------------------------------------------------------- 1 | **The Producer-Consumer Problem**: There are *nProducers* number of producer entities that produce tokens at rate of *productionRate* and stores them in a common buffer of *bufferSize* capacity. The producers must successfully store their produced items in buffer before they can begin on production of the next item. There are also *nConsumers* number of consumer entities that retrieve tokens from the same buffer and process them at rate of *consumerRate*. 2 | 3 | We would like to study what is the average wait times for the producers and the consumers, given different values of the various parameters (such as *bufferSize*, *productionRate* etc). 4 | 5 | We create the common buffer as: 6 | 7 | .. code-block:: js 8 | 9 | var buffer = new Sim.Buffer("buffer", bufferSize); 10 | 11 | We model the producers as entities that generate one token every *t* seconds, where *t* is exponential random number will mean *productionRate*. 12 | 13 | .. code-block:: js 14 | 15 | Random rand = new Random(SEED); 16 | 17 | class Producer extends Sim.Entity { 18 | start() { 19 | var timeToProduce = rand.exponential(1.0 / productionRate); 20 | 21 | // Set timer to self (models the time spend in production) 22 | this.setTimer(timeToProduce).done(() => { 23 | // Timer expires => item is ready to be stored in buffer. 24 | // When the item is successfully stored in buffer, we repeat 25 | // the process by recursively calling the same function. 26 | this.putBuffer(buffer, 1).done(this.start); 27 | }); 28 | } 29 | } 30 | 31 | We model the consumers as entities that retrieve tokens from the buffers, and process them for *t* seconds, where *t* is exponential random number will mean *consumptionRate*. 32 | 33 | .. code-block:: js 34 | 35 | class Consumer extends Sim.Entity { 36 | start() { 37 | // Retrieve one token from buffer 38 | this.getBuffer(buffer, 1).done(() => { 39 | // After an item has been retrieved, wait for some time 40 | // to model the consumption time. 41 | // After the waiting period is over, we repeat by 42 | // recursively calling this same function. 43 | var timeToConsume = rand.exponential(1.0 / consumptionRate); 44 | 45 | this.setTimer(timeToConsume).done(this.start); 46 | }); 47 | } 48 | } 49 | 50 | 51 | Finally we create the simulation and entity objects, and start the simulation. 52 | 53 | .. code-block:: js 54 | 55 | // Create simulator 56 | var sim = new Sim.Sim(); 57 | 58 | // Create producer entities 59 | for (var i = 0; i < nProducers; i++) sim.addEntity(Producer); 60 | 61 | // Create consumer entities 62 | for (var i = 0; i < nConsumers; i++) sim.addEntity(Consumer); 63 | 64 | // Start simulation 65 | sim.simulate(SIMTIME); 66 | 67 | // statistics 68 | buffer.report(); 69 | -------------------------------------------------------------------------------- /docs/examples/traffic_lights.js: -------------------------------------------------------------------------------- 1 | function trafficLightSimulation(GREEN_TIME, MEAN_ARRIVAL, SEED, SIMTIME) { 2 | var sim = new Sim.Sim(); 3 | var random = new Sim.Random(SEED); 4 | var trafficLights = [new Sim.Event("North-South Light"), 5 | new Sim.Event("East-West Light")]; 6 | var stats = new Sim.Population("Waiting at Intersection"); 7 | 8 | class LightController extends Sim.Entity { 9 | start() { 10 | sim.log(trafficLights[this.currentLight].name + " OFF" 11 | + ", " + trafficLights[1 - this.currentLight].name + " ON"); 12 | sim.log("------------------------------------------"); 13 | // turn off the current light 14 | trafficLights[this.currentLight].clear(); 15 | 16 | // turn on the other light. 17 | // Note the true parameter: the event must "sustain" 18 | trafficLights[1 - this.currentLight].fire(true); 19 | 20 | // update the currentLight variable 21 | this.currentLight = 1 - this.currentLight; 22 | 23 | // Repeat every GREEN_TIME interval 24 | this.setTimer(GREEN_TIME).done(this.start); 25 | } 26 | } 27 | 28 | LightController.currentLight = 0; // the light that is turned on currently 29 | 30 | 31 | class Traffic extends Sim.Entity { 32 | start() { 33 | this.generateTraffic("North", trafficLights[0]); // traffic for North -> South 34 | this.generateTraffic("South", trafficLights[0]); // traffic for South -> North 35 | this.generateTraffic("East", trafficLights[1]); // traffic for East -> West 36 | this.generateTraffic("West", trafficLights[1]); // traffic for West -> East 37 | }, 38 | generateTraffic(direction, light) { 39 | // STATS: record that vehicle as entered the intersection 40 | stats.enter(this.time()); 41 | sim.log("Arrive for " + direction); 42 | 43 | // wait on the light 44 | // The done() function will be called when the event fires 45 | // (i.e. the light turns green). 46 | this.waitEvent(light).done(() => { 47 | var arrivedAt = this.callbackData; 48 | // STATS: record that vehicle has left the intersection 49 | stats.leave(arrivedAt, this.time()); 50 | sim.log("Leave for " + direction + " (arrived at " + arrivedAt.toFixed(6) + ")"); 51 | }).setData(this.time()); 52 | 53 | // Repeat for the next car. Call this function again. 54 | var nextArrivalAt = random.exponential(1.0 / MEAN_ARRIVAL); 55 | this.setTimer(nextArrivalAt).done(this.generateTraffic, this, [direction, light]); 56 | } 57 | } 58 | 59 | sim.addEntity(LightController); 60 | sim.addEntity(Traffic); 61 | 62 | // Uncomment to display logging information 63 | // sim.setLogger(function (str) { 64 | // document.write(str); 65 | // }); 66 | 67 | // simulate for SIMTIME time 68 | sim.simulate(SIMTIME); 69 | 70 | return [stats.durationSeries.average(), 71 | stats.durationSeries.deviation(), 72 | stats.sizeSeries.average(), 73 | stats.sizeSeries.deviation()]; 74 | 75 | } 76 | -------------------------------------------------------------------------------- /examples/buffet_restaurant.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 50 | 51 | 52 | 53 | 56 | 57 |
58 | 59 | 61 | 63 | 65 | 67 | 69 | 71 |
Buffer Capacity: 60 |
Salad preparation time (min): 62 |
Mean interval between customer arrivals (min): 64 |
Mean cashier transaction time (min): 66 |
Random number generation seed: 68 |
Simulation time (min): 70 |
72 | 73 |
74 | 75 |
76 | Clear
77 |
78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. JSDES documentation master file, created by 2 | sphinx-quickstart on Tue Jun 21 14:41:02 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. Welcome to JSDES's documentation! 7 | ================================= 8 | 9 | SIM.JS -- Discrete Event Simulation in JavaScript 10 | ===================================================== 11 | 12 | **SIM.JS is a general-purpose Discrete Event Simulation library written entirely in JavaScript.** 13 | 14 | .. sidebar:: Table of Contents 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | 19 | basics 20 | tutorial 21 | simulator 22 | random 23 | download 24 | contact 25 | 26 | 27 | SIM.JS is a library for modeling discrete time event systems: 28 | 29 | * The library provides constructs to create :ref:`Entities ` which are the active actors in the system and encapsulate the state and logic of components in a system. 30 | * The entities contend for *resources*, which can be :ref:`Facilities ` (services that are requested by entities; supports FIFO, LIFO with preemption and Processor Sharing service disciplines), :ref:`Buffers ` (resources that store finite amount of tokens) and :ref:`Stores ` (resources that store JavaScript objects). 31 | * The entities communicate by waiting on :ref:`events-events` or by sending :ref:`events-messages`. 32 | * Statistics recording and analysis is provided by :ref:`statistics-data-series` (collection of discrete, time-independent observations), :ref:`statistics-time-series` (collection of discrete, time-dependent observations) and :ref:`statistics-population` (the behavior of population growth and decline). 33 | * SIM.JS also provides a random number generation library to generate seeded random variates from various distributions, including uniform, exponential, normal, gamma, pareto and others. 34 | 35 | **SIM.JS is written in *idiomatic* ES2015 JavaScript**. The library is written in event-based design paradigm: the changes in system states are notified via callback functions (:ref:`why not process-based? `). The design takes advantage of the powerful feature sets of JavaScript: prototype based inheritance, first-class functions, closures, anonymous functions, runtime object modifications and so on. Of course, a knowledge of these principles is not required (a lot of this behind the scenes), but we do certainly hope that using SIM.JS will be pleasurable experience for the amateur as well as the experienced practitioners of JavaScript. 36 | 37 | SIM.JS is free, open source and licensed under LGPL. 38 | 39 | .. topic:: Why Javascript? 40 | 41 | * The authors of this library are passionate about Discrete Event Simulations, and hope to see its widespread use in education as well as everyday applications. The authors also believe that in this era of Web 2.0, web-based applications are the best way to reach people. Which is why they concluded that there is a need for a Web enabled simulation engine. 42 | * These days when one can `boot linux `_ with JavaScript, play `Doom `_, run a myriad of productivity applications on the Web, we ask -- why not simulations? (And we say its about time!) 43 | 44 | -------------------------------------------------------------------------------- /queuing/release/makerelease.sh: -------------------------------------------------------------------------------- 1 | ## 2 | 3 | rm -rf queuing 4 | rm -f queuing.js 5 | rm -f queuing-offline.js 6 | 7 | cat ../../src/sim.js | grep -v ARG_CHECK >> queuing.js 8 | cat ../../src/request.js| grep -v ARG_CHECK >> queuing.js 9 | cat ../../src/queues.js | grep -v ARG_CHECK >> queuing.js 10 | cat ../../src/stats.js | grep -v ARG_CHECK >> queuing.js 11 | cat ../../src/random.js | grep -v ARG_CHECK >> queuing.js 12 | 13 | 14 | cat ../src/image_view.js >> queuing.js 15 | cat ../src/queue_app.js >> queuing.js 16 | cat ../src/server_model.js >> queuing.js 17 | cat ../src/source_model.js >> queuing.js 18 | cat ../src/sink_model.js >> queuing.js 19 | cat ../src/splitter_model.js >> queuing.js 20 | cat ../src/splitter_view.js >> queuing.js 21 | cat ../src/models.js >> queuing.js 22 | 23 | java -jar compiler.jar --js queuing.js --js_output_file queuing-min.js 24 | 25 | 26 | #cat ../../src/sim.js | grep -v ARG_CHECK >> queuing-offline.js 27 | #cat ../../src/request.js| grep -v ARG_CHECK >> queuing-offline.js 28 | #cat ../../src/queues.js | grep -v ARG_CHECK >> queuing-offline.js 29 | #cat ../../src/stats.js | grep -v ARG_CHECK >> queuing-offline.js 30 | #cat ../../src/random.js | grep -v ARG_CHECK >> queuing-offline.js 31 | # 32 | #cat ../src/queue-offline.js >> queuing-offline.js 33 | #java -jar compiler.jar --js queuing-offline.js --js_output_file queuing-offline-min.js 34 | 35 | # create directories 36 | mkdir queuing 37 | mkdir queuing/css 38 | mkdir queuing/css/images 39 | mkdir queuing/lib 40 | mkdir queuing/images 41 | 42 | # copy html 43 | cp ../index.html queuing 44 | 45 | # copy javascripts 46 | cp queuing-min.js queuing 47 | cp ../lib/raphael-min.js queuing/lib 48 | 49 | # copy css 50 | java -jar yuicompressor-2.4.6.jar --type css ../css/jquery-ui-1.8.16.custom.css > queuing/css/jquery-ui-1.8.16.custom.css 51 | java -jar yuicompressor-2.4.6.jar --type css ../css/queuing.css > queuing/css/queuing.css 52 | 53 | # copy images 54 | cp ../images/background.gif queuing/images 55 | cp ../images/banner.png queuing/images 56 | cp ../images/customers.png queuing/images 57 | cp ../images/door_out.png queuing/images 58 | cp ../images/odometer.png queuing/images 59 | cp ../images/orange-arrow.gif queuing/images 60 | cp ../images/server.png queuing/images 61 | cp ../images/settings.gif queuing/images 62 | cp ../images/splitter.png queuing/images 63 | 64 | cp ../css/images/ui-bg_diagonals-thick_18_b81900_40x40.png queuing/css/images 65 | cp ../css/images/ui-bg_diagonals-thick_20_666666_40x40.png queuing/css/images 66 | cp ../css/images/ui-bg_flat_10_000000_40x100.png queuing/css/images 67 | cp ../css/images/ui-bg_glass_100_f6f6f6_1x400.png queuing/css/images 68 | cp ../css/images/ui-bg_glass_100_fdf5ce_1x400.png queuing/css/images 69 | cp ../css/images/ui-bg_glass_65_ffffff_1x400.png queuing/css/images 70 | cp ../css/images/ui-bg_gloss-wave_35_f6a828_500x100.png queuing/css/images 71 | cp ../css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png queuing/css/images 72 | cp ../css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png queuing/css/images 73 | cp ../css/images/ui-icons_222222_256x240.png queuing/css/images 74 | cp ../css/images/ui-icons_228ef1_256x240.png queuing/css/images 75 | cp ../css/images/ui-icons_ef8c08_256x240.png queuing/css/images 76 | cp ../css/images/ui-icons_ffd27a_256x240.png queuing/css/images 77 | cp ../css/images/ui-icons_ffffff_256x240.png queuing/css/images 78 | 79 | -------------------------------------------------------------------------------- /examples/traffic_lights.js: -------------------------------------------------------------------------------- 1 | function trafficLightSimulation(GREEN_TIME, MEAN_ARRIVAL, SEED, SIMTIME) { 2 | var sim = new Sim.Sim(); 3 | var random = new Sim.Random(SEED); 4 | var trafficLights = [new Sim.Event("North-South Light"), 5 | new Sim.Event("East-West Light")]; 6 | var stats = new Sim.Population("Waiting at Intersection"); 7 | 8 | class LightController extends Sim.Entity { 9 | constructor(...args) { 10 | super(...args) 11 | // the light that is turned on currently 12 | this.currentLight = 0; 13 | } 14 | start() { 15 | sim.log(trafficLights[this.currentLight].name + " OFF" 16 | + ", " + trafficLights[1 - this.currentLight].name + " ON"); 17 | sim.log("------------------------------------------"); 18 | // turn off the current light 19 | trafficLights[this.currentLight].clear(); 20 | 21 | // turn on the other light. 22 | // Note the true parameter: the event must "sustain" 23 | trafficLights[1 - this.currentLight].fire(true); 24 | 25 | // update the currentLight variable 26 | this.currentLight = 1 - this.currentLight; 27 | 28 | // Repeat every GREEN_TIME interval 29 | this.setTimer(GREEN_TIME).done(this.start); 30 | } 31 | }; 32 | 33 | class Traffic extends Sim.Entity { 34 | start() { 35 | this.generateTraffic("North", trafficLights[0]); // traffic for North -> South 36 | this.generateTraffic("South", trafficLights[0]); // traffic for South -> North 37 | this.generateTraffic("East", trafficLights[1]); // traffic for East -> West 38 | this.generateTraffic("West", trafficLights[1]); // traffic for West -> East 39 | } 40 | generateTraffic(direction, light) { 41 | // STATS: record that vehicle as entered the intersection 42 | stats.enter(this.time()); 43 | sim.log("Arrive for " + direction); 44 | 45 | // wait on the light. 46 | // The done() function will be called when the event fires 47 | // (i.e. the light turns green). 48 | this.waitEvent(light).done(function () { 49 | var arrivedAt = this.callbackData; 50 | // STATS: record that vehicle has left the intersection 51 | stats.leave(arrivedAt, this.time()); 52 | sim.log("Leave for " + direction + " (arrived at " + arrivedAt.toFixed(6) + ")"); 53 | }).setData(this.time()); 54 | 55 | // Repeat for the next car. Call this function again. 56 | var nextArrivalAt = random.exponential(1.0 / MEAN_ARRIVAL); 57 | this.setTimer(nextArrivalAt).done(this.generateTraffic, this, [direction, light]); 58 | } 59 | }; 60 | 61 | sim.addEntity(LightController); 62 | sim.addEntity(Traffic); 63 | 64 | // Uncomment to display logging information 65 | // sim.setLogger(function (str) { 66 | // document.write(str); 67 | // }); 68 | 69 | // simulate for SIMTIME time 70 | sim.simulate(SIMTIME); 71 | 72 | return [stats.durationSeries.average(), 73 | stats.durationSeries.deviation(), 74 | stats.sizeSeries.average(), 75 | stats.sizeSeries.deviation()]; 76 | 77 | } 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

simjs-updated

2 | 3 |

4 | 5 | NPM Version 6 | 7 | 8 | 9 | License 10 | 11 | 12 | 13 | Github Issues 14 | 15 | 16 | 17 | 18 | Travis Status 19 | 20 | 21 | 22 | 23 | 24 | Coveralls 25 | 26 | 27 | 28 | 29 | 30 | Commitizen Friendly 31 | 32 | 33 |

34 | 35 |

36 | SimJS updated with ES2015 and updated toolchain 37 |

38 | 39 | 40 | 41 | *SIM.JS Updated is a general-purpose Discrete Event Simulation library written entirely in JavaScript.* 42 | 43 | The original implementation was written in 2011, and can be found at http://www.simjs.com 44 | 45 | Note, the documentation on that site is slightly out of date. We're using standard ES2015 46 | classes and idioms now, which should improve code readability. The documentation in the git 47 | repository has been updated though, and you should use that as your reference. 48 | 49 | SIM.JS is a library for modeling discrete time event systems: 50 | 51 | * The library provides constructs to create Entities which are the active 52 | actors in the system and encapsulate the state and logic of the system 53 | operations. 54 | 55 | * The entities contend for *resources*, which can be Facilities (services 56 | that are requested by entities; facilities have a maximum limit on number 57 | of concurrent users) and Buffers (resources that can store finite amount 58 | of tokens; entities store or retrieve tokens from the buffers). 59 | 60 | * The entities communicate by waiting on Events or by sending Messages. 61 | 62 | * Statistics recording and analysis capability is provided by Data Series 63 | (collection of discrete, time-independent observations), Time Series 64 | (collection of discrete, time-dependent observations) and Population 65 | (the behavior of population growth and decline). 66 | 67 | * SIM.JS also provides a random number generation library to generate seeded 68 | random variates from various distributions, including uniform, exponential, 69 | normal, gamma, pareto and others. 70 | 71 | *SIM.JS is written in _idiomatic_ EcmaScript 2015 JavaScript*. The library is 72 | written in event-based design paradigm: the changes in system states are notified 73 | via callback functions. The design takes advantage of the powerful feature sets 74 | of JavaScript: prototype based inheritance, first-class functions, closures, 75 | anonymous functions, runtime object modifications and so on. Of course, a 76 | knowledge of these principles is not required (a lot of this is behind the scenes), 77 | but we do certainly hope that using SIM.JS will be a pleasurable experience for 78 | the amateur as well as the experienced JavaScript programmer. 79 | -------------------------------------------------------------------------------- /queuing/css/queuing.css: -------------------------------------------------------------------------------- 1 | 2 | /***************************/ 3 | 4 | body { 5 | font-size: 62.5%; 6 | background-image: url('../images/background.gif'); 7 | } 8 | 9 | h1 { 10 | color: #1c94c4; 11 | margin-bottom: 0px; 12 | } 13 | 14 | p#subtitle { 15 | 16 | font-size: 1.2em; 17 | text-transform: uppercase; 18 | color: #1c94c4; 19 | } 20 | 21 | p.info { 22 | margin-left: 30px; 23 | font-size: 1.2em; 24 | } 25 | 26 | #like_buttons { 27 | position:absolute; 28 | top:30px; 29 | left:900px; 30 | padding: 4px; 31 | border-bottom: solid 4px #F7B64A; 32 | } 33 | 34 | #banner { 35 | /*background: #F7B64A;*/ 36 | background-image: url('../images/banner.png'); 37 | padding: 1px; 38 | padding-left: 30px; 39 | border-radius: 10px; 40 | width: 830px; 41 | } 42 | 43 | #content { 44 | border: 8px solid #bbb; 45 | border-radius: 10px; 46 | background: #ddd; 47 | width: 820px; 48 | padding: 10px; 49 | } 50 | 51 | #main_layout { 52 | width: 1080px; 53 | } 54 | 55 | #info{ 56 | border: 4px solid #ffd478; 57 | background: #FAF6AA; 58 | width: 200px; 59 | margin-bottom: 50px; 60 | position: absolute; 61 | top: 80px; 62 | left: 880px; 63 | padding: 5px; 64 | font-size: 1.1em; 65 | } 66 | 67 | #googlead { 68 | width: 200px; 69 | margin-bottom: 50px; 70 | position: absolute; 71 | top: 400px; 72 | left: 880px; 73 | /* border: 1px solid; */ 74 | } 75 | 76 | h1 { 77 | font-color: red; 78 | } 79 | 80 | #accordion { 81 | float: left; 82 | width: 200px; 83 | height: 400px; 84 | margin-right: 10px; 85 | } 86 | 87 | #toolbar { 88 | padding: 10px 4px; 89 | } 90 | 91 | #progressbar { 92 | width: 400px; 93 | float: left; 94 | } 95 | 96 | label { 97 | font-size: 1.2em; 98 | } 99 | 100 | input[type=text] { 101 | float: right; 102 | font-size: 1.2em; 103 | height: 1.4em; 104 | width: 70px; 105 | } 106 | 107 | #results { 108 | background: #ddd; 109 | width: 810px; 110 | } 111 | .stats { 112 | display: inline-block; 113 | border: 1px solid #FAF6AA; 114 | padding: 0.4em; 115 | margin: 0.4em; 116 | background: #F7D68A; 117 | } 118 | .stats_table { 119 | border: 1px; 120 | font-size: 1.2em; 121 | border-collapse:collapse; 122 | background-color: #FAF6AA; 123 | } 124 | 125 | #server_stats, #sink_stats { 126 | display: none; 127 | } 128 | 129 | 130 | .settings_form { 131 | padding: 10px; 132 | background-color: white; 133 | width: 220px; 134 | -moz-box-shadow: 5px 5px 5px #ccc; 135 | -webkit-box-shadow: 5px 5px 5px #ccc; 136 | box-shadow: 5px 5px 5px #ccc; 137 | /* position: relative; */ 138 | } 139 | 140 | .settings_form_close { 141 | position: absolute; 142 | top: -20px; right: -200px; 143 | width: 20px; height: 20px 144 | } 145 | 146 | 147 | .inline { 148 | display: inline; 149 | } 150 | 151 | .shift-down { 152 | margin-bottom: -5px; 153 | } 154 | 155 | strong { 156 | color: #F7B64A; 157 | } 158 | 159 | #sample_models { 160 | padding: 5px; 161 | } 162 | 163 | .about_text { 164 | display: none; 165 | } 166 | 167 | .navigation { 168 | list-style-type: none; 169 | padding-left: 0; 170 | margin-left: 0; 171 | } 172 | 173 | .navigation a { 174 | text-decoration: none; 175 | display: block; 176 | padding: 2px; 177 | border-bottom: dotted 1px black; 178 | } 179 | 180 | .navigation a:hover { 181 | background-color: #F7B64A; 182 | color: #fff; 183 | font-weight: bold; 184 | border-left: solid 5px black; 185 | } 186 | 187 | .navigation a:link a:visited { 188 | color: #F7B64A; 189 | } 190 | -------------------------------------------------------------------------------- /queuing/src/server_model.js: -------------------------------------------------------------------------------- 1 | class ServerModel { 2 | constructor(view) { 3 | this.view = view; 4 | this.nservers = 1; 5 | this.mu = 1; 6 | this.maxqlen = -1; 7 | 8 | this.entity = null; 9 | this.dest = null; 10 | this.statTable = $('#server_stats').clone().attr('id', view.name); 11 | this.statTable.find('h2').text(view.name); 12 | 13 | $("#results").append(this.statTable); 14 | this.stat = [ 15 | this.statTable.find('#arrival'), 16 | this.statTable.find('#drop'), 17 | this.statTable.find('#sutil'), 18 | this.statTable.find('#qtime'), 19 | this.statTable.find('#stime'), 20 | this.statTable.find('#qsize'), 21 | this.statTable.find('#ssize'), 22 | this.statTable.find('#qtimed'), 23 | this.statTable.find('#stimed'), 24 | this.statTable.find('#qsized'), 25 | this.statTable.find('#ssized') 26 | ]; 27 | 28 | this.view.image.attr({title: `Service rate = ${this.mu}`}); 29 | } 30 | 31 | jsonify() { 32 | return { 33 | nservers: this.nservers, 34 | mu: this.mu, 35 | maxqlen: this.maxqlen 36 | }; 37 | } 38 | 39 | start() { 40 | this.entity = QueueApp.sim.addEntity(ServerEntity, null, this.nservers, this.mu, this.maxqlen); 41 | 42 | } 43 | 44 | connect() { 45 | this.entity.dest = this.dest ? this.dest.entity : null; 46 | } 47 | 48 | showSettings(x, y) { 49 | const d = $('#server_form'); 50 | QueueApp.form_view = this.view; 51 | d.find('#server_form_rate').val(this.mu); 52 | d.find('#queue_length').val(this.maxqlen); 53 | d.show().position({ 54 | of: $(this.view.image.node), 55 | at: 'center center', 56 | my: 'left top' 57 | }); 58 | } 59 | 60 | saveSettings(dialog) { 61 | const d = $('#server_form'); 62 | this.mu = d.find('#server_form_rate').val(); 63 | this.maxqlen = d.find('#queue_length').val(); 64 | this.view.image.attr({title: `Service rate = ${this.mu}`}); 65 | } 66 | 67 | showStats() { 68 | const service = this.entity.facility; 69 | const qd = service.queueStats().durationSeries; 70 | const qs = service.queueStats().sizeSeries; 71 | const sd = service.systemStats().durationSeries; 72 | const ss = service.systemStats().sizeSeries; 73 | const usage = service.usage() / QueueApp.sim.time() * 100; 74 | this.stat[0].text(this.entity.arrived); 75 | this.stat[1].text(this.entity.dropped); 76 | this.stat[2].text(`${usage.toFixed(1)}%`); 77 | this.stat[3].text(qd.average().toFixed(3)); 78 | this.stat[4].text(sd.average().toFixed(3)); 79 | this.stat[5].text(qs.average().toFixed(3)); 80 | this.stat[6].text(ss.average().toFixed(3)); 81 | this.stat[7].text(qd.deviation().toFixed(3)); 82 | this.stat[8].text(sd.deviation().toFixed(3)); 83 | this.stat[9].text(qs.deviation().toFixed(3)); 84 | this.stat[10].text(ss.deviation().toFixed(3)); 85 | 86 | this.view.showCounters(qd.count(), sd.count()); 87 | } 88 | 89 | unlink() { 90 | this.statTable.remove(); 91 | this.view = null; 92 | this.stat = null; 93 | } 94 | } 95 | 96 | /***************************************************/ 97 | 98 | class ServerEntity extends Sim.Entity { 99 | start(nservers, mu, maxqlen) { 100 | this.mu = mu; 101 | this.facility = new Sim.Facility('queue', Sim.Facility.FCFS, nservers, maxqlen); 102 | this.dropped = 0; 103 | this.arrived = 0; 104 | } 105 | 106 | arrive(stamp) { 107 | this.arrived ++; 108 | const duration = QueueApp.random.exponential(this.mu); 109 | this.useFacility(this.facility, duration).done(this.completed, this, stamp); 110 | } 111 | 112 | completed(stamp) { 113 | if (this.callbackMessage === -1) { 114 | this.dropped ++; 115 | } else { 116 | if (this.dest) { 117 | this.dest.arrive(stamp); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/event-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import * as Sim from '../sim'; 3 | 4 | let finalized = 0; 5 | 6 | test('testEventFlash', (t) => { 7 | const sim = new Sim.Sim(); 8 | 9 | const event = new Sim.Event('event'); 10 | 11 | class MyEntity extends Sim.Entity { 12 | constructor(...args) { 13 | super(...args); 14 | this.count = 0; 15 | } 16 | start() { 17 | t.is(event.isFired, false); 18 | 19 | this.waitEvent(event).done(() => { 20 | t.is(event.isFired, false); 21 | t.is(this.callbackSource, event); 22 | t.is(this.time(), 10); 23 | this.count ++; 24 | }); 25 | 26 | this.setTimer(10).done(() => { 27 | t.is(event.isFired, false); 28 | event.fire(); 29 | }); 30 | 31 | this.setTimer(20).done(() => { 32 | t.is(event.isFired, false); 33 | this.waitEvent(event).done(() => { 34 | t.is(this.callbackSource, event); 35 | t.is(this.time(), 21); 36 | this.count ++; 37 | }); 38 | }); 39 | this.setTimer(21).done(() => { 40 | t.is(event.isFired, false); 41 | event.fire(); 42 | t.is(event.isFired, false); 43 | }); 44 | } 45 | finalize() { 46 | finalized++; 47 | t.is(this.time(), 21); 48 | t.is(this.count, 2); 49 | } 50 | } 51 | 52 | sim.addEntity(MyEntity); 53 | sim.simulate(100); 54 | }); 55 | 56 | test('testEventSustain', (t) => { 57 | const sim = new Sim.Sim(); 58 | 59 | const event = new Sim.Event('event'); 60 | 61 | class MyEntity extends Sim.Entity { 62 | constructor(...args) { 63 | super(...args); 64 | this.count = 0; 65 | } 66 | start() { 67 | t.is(event.isFired, false); 68 | 69 | this.waitEvent(event).done(() => { 70 | t.is(event.isFired, true); 71 | t.is(this.callbackSource, event); 72 | t.is(this.time(), 10); 73 | this.count ++; 74 | }); 75 | this.setTimer(10).done(() => { 76 | t.is(event.isFired, false); 77 | event.fire(true); 78 | t.is(event.isFired, true); 79 | }); 80 | 81 | this.setTimer(20).done(() => { 82 | t.is(event.isFired, true); 83 | this.waitEvent(event).done(() => { 84 | t.is(this.callbackSource, event); 85 | t.is(this.time(), 20); 86 | this.count ++; 87 | }); 88 | }); 89 | 90 | this.setTimer(30).done(() => { 91 | t.is(event.isFired, true); 92 | event.clear(); 93 | t.is(event.isFired, false); 94 | }); 95 | this.setTimer(40).done(() => { 96 | t.is(event.isFired, false); 97 | this.waitEvent(event).done(() => { 98 | t.is(this.callbackSource, event); 99 | t.is(this.time(), 41); 100 | this.count ++; 101 | }); 102 | }); 103 | 104 | this.setTimer(41).done(() => { event.fire(true); }); 105 | 106 | } 107 | finalize() { 108 | finalized++; 109 | t.is(this.time(), 41); 110 | t.is(this.count, 3); 111 | } 112 | } 113 | 114 | sim.addEntity(MyEntity); 115 | sim.simulate(100); 116 | }); 117 | 118 | test('testEventWaitQueue', (t) => { 119 | const barrier = new Sim.Event('Barrier'); 120 | 121 | const funnel = new Sim.Event('Funnel'); 122 | 123 | let wcount = 0; 124 | 125 | let qcount = 0; 126 | 127 | class MyEntity extends Sim.Entity { 128 | start(master) { 129 | this.waitEvent(barrier).done(() => { 130 | wcount++; 131 | }); 132 | 133 | this.queueEvent(funnel).done(() => { 134 | qcount++; 135 | }); 136 | 137 | if (master) { 138 | this.setTimer(10) 139 | .done(barrier.fire, barrier) 140 | .done(funnel.fire, funnel); 141 | } 142 | } 143 | finalize() { 144 | finalized++; 145 | } 146 | } 147 | 148 | const sim = new Sim.Sim(); 149 | 150 | const e = []; 151 | 152 | for (let i = 0; i < 100; i++) { 153 | 154 | e.push(sim.addEntity(MyEntity, null, i === 0)); 155 | } 156 | sim.simulate(100); 157 | t.is(wcount, 100); 158 | t.is(qcount, 1); 159 | }); 160 | -------------------------------------------------------------------------------- /docs/random.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Random Number Generation 3 | ======================== 4 | 5 | .. default-domain:: js 6 | 7 | Why not ``Math.random()``? 8 | --------------------------- 9 | The JavaScript's native ``Math.random()`` function is not suited for Discrete Event Simulations, since: 10 | 11 | * The ``Math.random()`` function cannot be *seeded*. There is no way to guarantee that the same random stream will be produced the next time the script is executed. 12 | * There is *only one stream* of random numbers. Statistics purists frown upon when two independent random distributions are drawn from same seed. 13 | * The javascript library provides only the *uniform* probability distribution function. In DES, as also in many other scientific applications, there is a need for other kinds of distributions, such as *exponential*, *gaussian*, *pareto* etc. 14 | * The native ``Math.random()`` does not use (at the time of writing) the arguably better *Mersenne Twister* algorithm for random number generator (see Mersenne Twister `website `_ and Wikipedia `article `_). 15 | 16 | The ``Sim.Random`` Library 17 | ----------------------- 18 | 19 | The ``Sim.Random`` library uses the Mersenne Twister algorithm for generating random number stream, and is based on the JavaScript implementation by Makoto Matsumoto and Takuji Nishimura (`code `_). 20 | 21 | The original code is wrapped around as a javascript class and there can be multiple objects each representing different random number streams. For example, 22 | 23 | .. code-block:: js 24 | 25 | /* Demonstrate that random number streams can be seeded, 26 | * and multiple streams can be created in a single script. */ 27 | var stream1 = new Sim.Random(1234); 28 | var stream2 = new Sim.Random(6789); 29 | 30 | stream1.random(); // returns 0.966453535715118 always 31 | stream2.random(); // returns 0.13574991398490965 always 32 | 33 | In addition, the ``Sim.Random`` library supports following probability distribution functions: 34 | 35 | .. hlist:: 36 | :columns: 4 37 | 38 | * :func:`~Sim.Random.exponential` 39 | * :func:`~Sim.Random.gamma` 40 | * :func:`~Sim.Random.normal` 41 | * :func:`~Sim.Random.pareto` 42 | * :func:`~Sim.Random.triangular` 43 | * :func:`~Sim.Random.uniform` 44 | * :func:`~Sim.Random.weibull` 45 | 46 | API Reference 47 | -------------- 48 | 49 | .. js:class:: Sim.Random ([seed]) 50 | 51 | :param integer seed: An optional seed value. If this argument is not provided, then the seed value is set to ``new Date().getTime()``. 52 | 53 | 54 | .. function:: Sim.Random.random() 55 | 56 | Returns a uniformly generated random floating point number in the range ``[0, 1.0)``. 57 | 58 | .. js:function:: Sim.Random.exponential(lambda) 59 | 60 | Exponential distribution. ``lambda`` is the rate (inverse of mean) for the distribution. ``lambda`` is a required parameters, and must be non-negative and non-zero. 61 | 62 | .. js:function:: Sim.Random.gamma(alpha, beta) 63 | 64 | Gamma distribution. ``alpha`` is sometimes also known as *shape* of the distribution, while ``beta`` as the *scale*. Both arguments are required. 65 | 66 | This function is adapted from Python 2.6 implementation of ``random.py``. 67 | 68 | .. js:function:: Sim.Random.normal(mu, sigma) 69 | 70 | Normal (or Gaussian) distribution. ``mu`` is the mean of the Gaussian probability density function, and ``sigma`` is the standard deviation. Both parameters are required. 71 | 72 | 73 | .. js:function:: Sim.Random.pareto(alpha) 74 | 75 | Pareto distribution. The ``alpha`` parameter is required. 76 | 77 | .. js:function:: Sim.Random.triangular(lower, upper, mode) 78 | 79 | Triangular distribution. The random number are generated between the range (``lower``, ``upper``) with ``mode`` as the mode value. All three parameters are required. 80 | 81 | .. js:function:: Sim.Random.uniform(lower, upper) 82 | 83 | Uniform distribution. Returns a uniformly generated random number in the range [``lower``, ``upper``). Both *lower* and *upper* arguments are required. 84 | 85 | .. js:function:: Sim.Random.weibull(alpha, beta) 86 | 87 | Weibull distribution. Both ``alpha`` and ``beta`` parameters are required. -------------------------------------------------------------------------------- /src/lib/queues.js: -------------------------------------------------------------------------------- 1 | import { argCheck } from './sim.js'; 2 | import { Population } from './stats.js'; 3 | import { Model } from './model.js'; 4 | 5 | class Queue extends Model { 6 | constructor(name) { 7 | super(name); 8 | this.data = []; 9 | this.timestamp = []; 10 | this.stats = new Population(); 11 | } 12 | 13 | top() { 14 | return this.data[0]; 15 | } 16 | 17 | back() { 18 | return (this.data.length) ? this.data[this.data.length - 1] : null; 19 | } 20 | 21 | push(value, timestamp) { 22 | argCheck(arguments, 2, 2); 23 | this.data.push(value); 24 | this.timestamp.push(timestamp); 25 | 26 | this.stats.enter(timestamp); 27 | } 28 | 29 | unshift(value, timestamp) { 30 | argCheck(arguments, 2, 2); 31 | this.data.unshift(value); 32 | this.timestamp.unshift(timestamp); 33 | 34 | this.stats.enter(timestamp); 35 | } 36 | 37 | shift(timestamp) { 38 | argCheck(arguments, 1, 1); 39 | 40 | const value = this.data.shift(); 41 | 42 | const enqueuedAt = this.timestamp.shift(); 43 | 44 | this.stats.leave(enqueuedAt, timestamp); 45 | return value; 46 | } 47 | 48 | pop(timestamp) { 49 | argCheck(arguments, 1, 1); 50 | 51 | const value = this.data.pop(); 52 | 53 | const enqueuedAt = this.timestamp.pop(); 54 | 55 | this.stats.leave(enqueuedAt, timestamp); 56 | return value; 57 | } 58 | 59 | passby(timestamp) { 60 | argCheck(arguments, 1, 1); 61 | 62 | this.stats.enter(timestamp); 63 | this.stats.leave(timestamp, timestamp); 64 | } 65 | 66 | finalize(timestamp) { 67 | argCheck(arguments, 1, 1); 68 | 69 | this.stats.finalize(timestamp); 70 | } 71 | 72 | reset() { 73 | this.stats.reset(); 74 | } 75 | 76 | clear() { 77 | this.reset(); 78 | this.data = []; 79 | this.timestamp = []; 80 | } 81 | 82 | report() { 83 | return [this.stats.sizeSeries.average(), 84 | this.stats.durationSeries.average()]; 85 | } 86 | 87 | empty() { 88 | return this.data.length === 0; 89 | } 90 | 91 | size() { 92 | return this.data.length; 93 | } 94 | } 95 | 96 | class PQueue extends Model { 97 | constructor(name) { 98 | super(name); 99 | this.data = []; 100 | this.order = 0; 101 | } 102 | 103 | greater(ro1, ro2) { 104 | if (ro1.deliverAt > ro2.deliverAt) return true; 105 | if (ro1.deliverAt === ro2.deliverAt) return ro1.order > ro2.order; 106 | return false; 107 | } 108 | 109 | insert(ro) { 110 | argCheck(arguments, 1, 1); 111 | ro.order = this.order ++; 112 | 113 | let index = this.data.length; 114 | 115 | this.data.push(ro); 116 | 117 | // insert into data at the end 118 | const a = this.data; 119 | 120 | const node = a[index]; 121 | 122 | // heap up 123 | while (index > 0) { 124 | const parentIndex = Math.floor((index - 1) / 2); 125 | 126 | if (this.greater(a[parentIndex], ro)) { 127 | a[index] = a[parentIndex]; 128 | index = parentIndex; 129 | } else { 130 | break; 131 | } 132 | } 133 | a[index] = node; 134 | } 135 | 136 | remove() { 137 | const a = this.data; 138 | 139 | let len = a.length; 140 | 141 | if (len <= 0) { 142 | return null; 143 | } 144 | if (len === 1) { 145 | return this.data.pop(); 146 | } 147 | const top = a[0]; 148 | 149 | // move the last node up 150 | a[0] = a.pop(); 151 | len--; 152 | 153 | // heap down 154 | let index = 0; 155 | 156 | const node = a[index]; 157 | 158 | while (index < Math.floor(len / 2)) { 159 | const leftChildIndex = 2 * index + 1; 160 | 161 | const rightChildIndex = 2 * index + 2; 162 | 163 | const smallerChildIndex = rightChildIndex < len 164 | && !this.greater(a[rightChildIndex], a[leftChildIndex]) 165 | ? rightChildIndex : leftChildIndex; 166 | 167 | if (this.greater(a[smallerChildIndex], node)) { 168 | break; 169 | } 170 | 171 | a[index] = a[smallerChildIndex]; 172 | index = smallerChildIndex; 173 | } 174 | a[index] = node; 175 | return top; 176 | } 177 | } 178 | 179 | export { Queue, PQueue }; 180 | -------------------------------------------------------------------------------- /ui/simpanel.js: -------------------------------------------------------------------------------- 1 | var SimPanel = { 2 | PLAY: 1, 3 | PAUSE: 2, 4 | RESUME: 3, 5 | STOP: 4, 6 | TICK: 5, 7 | COMPLETE: 6, 8 | 9 | IntervalLow: 40, 10 | Interval: 50, 11 | IntervalHigh: 60, 12 | EventsPerInterval: 100, 13 | IntervalPause: 0, 14 | 15 | /** Public APIs 16 | */ 17 | load: function (divname, cb) { 18 | divname = '#' + divname; 19 | 20 | // Add gui elements 21 | $(divname).html(' \ 22 | \ 23 | \ 24 | \ 25 | 0 \ 26 | 0 \ 27 | '); 28 | 29 | this.playButton = $(divname + ">#simpanel_play"); 30 | this.stopButton = $(divname + ">#simpanel_stop"); 31 | this.simUntilInput = $(divname + ">#simpanel_sim_until"); 32 | this.simTimeDisplay = $(divname + ">#simpanel_sim_time"); 33 | this.eventsSecDisplay = $(divname + ">#simpanel_events_per_sec"); 34 | 35 | // Add handlers 36 | this.playButton.click(this.play); 37 | this.stopButton.click(this.stop); 38 | 39 | // Disable some fields 40 | this.stopButton.hide(); 41 | this.simUntilInput.show(); 42 | this.simTimeDisplay.hide(); 43 | this.eventsSecDisplay.hide(); 44 | 45 | this.playing = false; 46 | this.paused = false; 47 | 48 | this.callback = cb; 49 | return this; 50 | }, 51 | 52 | /** Programmatic APIs. 53 | * These will be called by Panel UI. 54 | */ 55 | 56 | play: function (event) { 57 | var sp = SimPanel; 58 | 59 | if (sp.playing) { 60 | if (sp.paused) { 61 | return sp.resume(); 62 | } 63 | return sp.pause(); 64 | } 65 | 66 | if (sp.callback) { 67 | if (!sp.callback(sp.PLAY)) return; 68 | } 69 | 70 | if (!sp.sim) { 71 | alert("Simulation object is not configured"); 72 | return; 73 | } 74 | 75 | sp.playButton.val("Pause Simulation"); 76 | sp.stopButton.show(); 77 | sp.simUntilInput.hide(); 78 | sp.simTimeDisplay.show(); 79 | sp.eventsSecDisplay.show(); 80 | 81 | sp.playing = true; 82 | 83 | sp.until = sp.simUntilInput.val(); 84 | sp.startedAt = new Date().getTime(); 85 | sp.run(); 86 | }, 87 | 88 | pause: function () { 89 | var sp = SimPanel; 90 | 91 | SimPanel.playButton.val("Resume Simulation"); 92 | SimPanel.paused = true; 93 | }, 94 | 95 | resume: function () { 96 | var sp = SimPanel; 97 | if (sp.callback) sp.callback(sp.RESUME); 98 | sp.playButton.val("Pause Simulation"); 99 | sp.paused = false; 100 | sp.run(); 101 | }, 102 | 103 | stop: function () { 104 | var sp = SimPanel; 105 | 106 | sp.playing = false; 107 | sp.paused = false; 108 | 109 | sp.playButton.val("Start Simulation"); 110 | sp.stopButton.hide(); 111 | sp.simUntilInput.show(); 112 | sp.simTimeDisplay.hide(); 113 | sp.eventsSecDisplay.hide(); 114 | }, 115 | 116 | // 0: stopped, 1: playing, 2: paused 117 | getRunStatus: function () { 118 | 119 | }, 120 | 121 | setMode: function (mode) { 122 | 123 | }, 124 | 125 | getMode: function () { 126 | 127 | }, 128 | 129 | /*** private API */ 130 | run: function () { 131 | var sp = SimPanel; 132 | var start = new Date().getTime(); 133 | var completed = sp.sim.simulate(sp.until, sp.EventsPerInterval); 134 | var end = new Date().getTime(); 135 | 136 | sp.simTimeDisplay.text(sp.sim.time()); 137 | var eventsPerSec = sp.EventsPerInterval / (end - start) * 1000; 138 | sp.eventsSecDisplay.text(end - sp.startedAt); 139 | // sp.eventsSecDisplay.append([end-start, end-sp.startedAt, sp.EventsPerInterval, "]"].join(", ")); 140 | 141 | var diff = end - start; 142 | if (diff < sp.IntervalLow || diff > sp.IntervalHigh) { 143 | sp.EventsPerInterval = Math.floor(sp.EventsPerInterval / diff * sp.Interval); 144 | } 145 | 146 | if (completed) { 147 | if (sp.callback) sp.callback(sp.COMPLETE); 148 | sp.stop(); 149 | return; 150 | } 151 | 152 | if (!sp.playing) { 153 | if (sp.callback) sp.callback(sp.STOP); 154 | return; 155 | } 156 | 157 | if (sp.paused) { 158 | if (sp.callback) sp.callback(sp.PAUSE); 159 | return; 160 | } 161 | 162 | setTimeout(sp.run, sp.IntervalPause); 163 | } 164 | }; 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/test/stats-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { assertAlmost } from './utils'; 3 | import * as Sim from '../sim'; 4 | import 'babel-core/register'; 5 | 6 | test('testDataSeries', () => { 7 | // ai = i, wi = 0 8 | const m = new Sim.DataSeries(); 9 | 10 | for (let i = 1; i <= 100; i++) { 11 | 12 | m.record(i); 13 | } 14 | assertAlmost(m.average(), 50.5); 15 | assertAlmost(m.deviation(), 28.8660700477); 16 | assertAlmost(m.variance(), 833.25); 17 | assertAlmost(m.min(), 1.0); 18 | assertAlmost(m.max(), 100.0); 19 | assertAlmost(m.range(), 99.0); 20 | assertAlmost(m.sum(), 5050.0); 21 | assertAlmost(m.sumWeighted(), 5050.0); 22 | 23 | // ai = i, wi = 1 / i 24 | m.reset(); 25 | for (let i = 1; i <= 100; i++) { 26 | 27 | m.record(i, 1.0 / i); 28 | } 29 | assertAlmost(m.average(), 19.277563597396004); 30 | assertAlmost(m.variance(), 601.89250341685056); 31 | assertAlmost(m.sumWeighted(), 100.0); 32 | assertAlmost(m.min(), 1.0); 33 | assertAlmost(m.max(), 100.0); 34 | assertAlmost(m.range(), 99.0); 35 | assertAlmost(m.sum(), 5050.0); 36 | 37 | m.reset(); 38 | for (let i = 1; i <= 100; i++) { 39 | 40 | m.record(i, 2.0 * i); 41 | } 42 | assertAlmost(m.average(), 67.0); 43 | assertAlmost(m.variance(), 561.0); 44 | assertAlmost(m.min(), 1.0); 45 | assertAlmost(m.max(), 100.0); 46 | assertAlmost(m.range(), 99.0); 47 | assertAlmost(m.sum(), 5050.0); 48 | }); 49 | 50 | test('testTimeSeries', () => { 51 | let m = new Sim.TimeSeries(); 52 | 53 | // ai = i, ti = i 54 | for (let i = 1; i <= 100; i++) { 55 | 56 | m.record(i, i); 57 | } 58 | 59 | m.finalize(101); 60 | assertAlmost(m.average(), 50.5); 61 | assertAlmost(m.deviation(), 28.8660700477); 62 | assertAlmost(m.variance(), 833.25); 63 | assertAlmost(m.min(), 1.0); 64 | assertAlmost(m.max(), 100.0); 65 | assertAlmost(m.range(), 99.0); 66 | assertAlmost(m.sum(), 5050.0); 67 | 68 | // ai = 1, ti = i 69 | m.reset(); 70 | for (let i = 1; i <= 100; i++) { 71 | 72 | m.record(1, i); 73 | } 74 | m.finalize(100); 75 | 76 | assertAlmost(m.average(), 1.0); 77 | assertAlmost(m.variance(), 0); 78 | assertAlmost(m.min(), 1.0); 79 | assertAlmost(m.max(), 1.0); 80 | assertAlmost(m.range(), 0.0); 81 | assertAlmost(m.sum(), 100.0); 82 | 83 | // custom 84 | m.reset(); 85 | m.record(0, 0); 86 | m.record(100, 10); 87 | m.record(0, 100); 88 | assertAlmost(m.average(), 90.0); 89 | 90 | // with zero intervals 91 | m = new Sim.TimeSeries(); 92 | 93 | m.reset(); 94 | m.record(1, 0); 95 | m.record(100, 1); 96 | m.record(1, 1); 97 | m.record(0, 2); 98 | assertAlmost(m.average(), 1.0); 99 | }); 100 | 101 | test('testPopulation', () => { 102 | const m = new Sim.Population(); 103 | 104 | // test basic 105 | for (let i = 0; i < 100; i++) { 106 | 107 | m.enter(i); 108 | m.leave(i, i + 1); 109 | } 110 | assertAlmost(m.sizeSeries.average(), 1.0); 111 | assertAlmost(m.durationSeries.average(), 1.0); 112 | assertAlmost(m.sizeSeries.min(), 0); 113 | assertAlmost(m.sizeSeries.max(), 1); 114 | 115 | // test reset 116 | m.reset(); 117 | for (let i = 0; i < 100; i++) { 118 | 119 | m.enter(i); 120 | m.leave(i, i + 1); 121 | } 122 | assertAlmost(m.sizeSeries.average(), 1.0); 123 | assertAlmost(m.durationSeries.average(), 1.0); 124 | 125 | // ai arrive at i and leave at 100 126 | m.reset(); 127 | for (let i = 0; i < 100; i++) { 128 | 129 | m.enter(i); 130 | m.leave(i, 100); 131 | } 132 | assertAlmost(m.sizeSeries.average(), 50.5); 133 | assertAlmost(m.durationSeries.average(), 50.5); 134 | 135 | // two enters and then two leaves 136 | m.reset(); 137 | m.enter(0); 138 | m.enter(0); 139 | m.leave(0, 1); 140 | m.leave(0, 1); 141 | m.finalize(1); 142 | assertAlmost(m.sizeSeries.average(), 2); 143 | assertAlmost(m.durationSeries.average(), 1.0); 144 | 145 | // nested enter leave 146 | m.reset(); 147 | m.enter(0); 148 | m.enter(1); 149 | m.leave(1, 2); 150 | m.leave(0, 3); 151 | m.finalize(3); 152 | assertAlmost(m.sizeSeries.average(), 4.0 / 3); 153 | assertAlmost(m.durationSeries.average(), 2.0); 154 | 155 | // overlapped enter leave 156 | m.reset(); 157 | m.enter(0); 158 | m.enter(1); 159 | m.leave(0, 2); 160 | m.leave(1, 3); 161 | m.finalize(3); 162 | assertAlmost(m.sizeSeries.average(), 4.0 / 3); 163 | assertAlmost(m.durationSeries.average(), 2.0); 164 | }); 165 | 166 | test('testHistogram', (t) => { 167 | const m = new Sim.DataSeries(); 168 | 169 | m.setHistogram(0.5, 99.5, 99); 170 | for (let i = 0; i <= 100; i++) { 171 | 172 | m.record(i); 173 | } 174 | 175 | const h = m.getHistogram(); 176 | 177 | for (let i = 0; i <= 100; i++) { 178 | 179 | t.is(h[i], 1); 180 | } 181 | }); 182 | 183 | -------------------------------------------------------------------------------- /src/lib/request.js: -------------------------------------------------------------------------------- 1 | import { argCheck, Store, Buffer, Event } from './sim.js'; 2 | 3 | class Request { 4 | constructor(entity, currentTime, deliverAt) { 5 | this.entity = entity; 6 | this.scheduledAt = currentTime; 7 | this.deliverAt = deliverAt; 8 | this.callbacks = []; 9 | this.cancelled = false; 10 | this.group = null; 11 | } 12 | 13 | cancel() { 14 | // Ask the main request to handle cancellation 15 | if (this.group && this.group[0] !== this) { 16 | return this.group[0].cancel(); 17 | } 18 | 19 | // --> this is main request 20 | if (this.noRenege) return this; 21 | 22 | // if already cancelled, do nothing 23 | if (this.cancelled) return; 24 | 25 | // set flag 26 | this.cancelled = true; 27 | 28 | if (this.deliverAt === 0) { 29 | this.deliverAt = this.entity.time(); 30 | } 31 | 32 | if (this.source) { 33 | if ((this.source instanceof Buffer) 34 | || (this.source instanceof Store)) { 35 | this.source.progressPutQueue(); 36 | this.source.progressGetQueue(); 37 | } 38 | } 39 | 40 | if (!this.group) { 41 | return; 42 | } 43 | for (let i = 1; i < this.group.length; i++) { 44 | 45 | this.group[i].cancelled = true; 46 | if (this.group[i].deliverAt === 0) { 47 | this.group[i].deliverAt = this.entity.time(); 48 | } 49 | } 50 | } 51 | 52 | done(callback, context, argument) { 53 | argCheck(arguments, 0, 3, Function, Object); 54 | 55 | this.callbacks.push([callback, context, argument]); 56 | return this; 57 | } 58 | 59 | waitUntil(delay, callback, context, argument) { 60 | argCheck(arguments, 1, 4, null, Function, Object); 61 | if (this.noRenege) return this; 62 | 63 | const ro = this._addRequest( 64 | this.scheduledAt + delay, callback, context, argument); 65 | 66 | this.entity.sim.queue.insert(ro); 67 | return this; 68 | } 69 | 70 | unlessEvent(event, callback, context, argument) { 71 | argCheck(arguments, 1, 4, null, Function, Object); 72 | if (this.noRenege) return this; 73 | 74 | if (event instanceof Event) { 75 | const ro = this._addRequest(0, callback, context, argument); 76 | 77 | ro.msg = event; 78 | event.addWaitList(ro); 79 | 80 | } else if (event instanceof Array) { 81 | for (let i = 0; i < event.length; i++) { 82 | 83 | const ro = this._addRequest(0, callback, context, argument); 84 | 85 | ro.msg = event[i]; 86 | event[i].addWaitList(ro); 87 | } 88 | } 89 | 90 | return this; 91 | } 92 | 93 | setData(data) { 94 | this.data = data; 95 | return this; 96 | } 97 | 98 | deliver() { 99 | if (this.cancelled) return; 100 | this.cancel(); 101 | if (!this.callbacks) return; 102 | 103 | if (this.group && this.group.length > 0) { 104 | this._doCallback(this.group[0].source, 105 | this.msg, 106 | this.group[0].data); 107 | } else { 108 | this._doCallback(this.source, 109 | this.msg, 110 | this.data); 111 | } 112 | 113 | } 114 | 115 | cancelRenegeClauses() { 116 | // this.cancel = this.Null; 117 | // this.waitUntil = this.Null; 118 | // this.unlessEvent = this.Null; 119 | this.noRenege = true; 120 | 121 | if (!this.group || this.group[0] !== this) { 122 | return; 123 | } 124 | 125 | for (let i = 1; i < this.group.length; i++) { 126 | 127 | this.group[i].cancelled = true; 128 | if (this.group[i].deliverAt === 0) { 129 | this.group[i].deliverAt = this.entity.time(); 130 | } 131 | } 132 | } 133 | 134 | Null() { 135 | return this; 136 | } 137 | 138 | _addRequest(deliverAt, callback, context, argument) { 139 | const ro = new Request( 140 | this.entity, 141 | this.scheduledAt, 142 | deliverAt); 143 | 144 | ro.callbacks.push([callback, context, argument]); 145 | 146 | if (this.group === null) { 147 | this.group = [this]; 148 | } 149 | 150 | this.group.push(ro); 151 | ro.group = this.group; 152 | return ro; 153 | } 154 | 155 | _doCallback(source, msg, data) { 156 | for (let i = 0; i < this.callbacks.length; i++) { 157 | 158 | const callback = this.callbacks[i][0]; 159 | 160 | if (!callback) continue; 161 | 162 | let context = this.callbacks[i][1]; 163 | 164 | if (!context) context = this.entity; 165 | 166 | const argument = this.callbacks[i][2]; 167 | 168 | context.callbackSource = source; 169 | context.callbackMessage = msg; 170 | context.callbackData = data; 171 | 172 | if (!argument) { 173 | callback.call(context); 174 | } else if (argument instanceof Array) { 175 | callback.apply(context, argument); 176 | } else { 177 | callback.call(context, argument); 178 | } 179 | 180 | context.callbackSource = null; 181 | context.callbackMessage = null; 182 | context.callbackData = null; 183 | } 184 | } 185 | } 186 | 187 | export { Request }; 188 | -------------------------------------------------------------------------------- /src/lib/stats.js: -------------------------------------------------------------------------------- 1 | import { argCheck } from './sim.js'; 2 | 3 | class DataSeries { 4 | constructor(name) { 5 | this.name = name; 6 | this.reset(); 7 | } 8 | 9 | reset() { 10 | this.Count = 0; 11 | this.W = 0.0; 12 | this.A = 0.0; 13 | this.Q = 0.0; 14 | this.Max = -Infinity; 15 | this.Min = Infinity; 16 | this.Sum = 0; 17 | 18 | if (this.histogram) { 19 | for (let i = 0; i < this.histogram.length; i++) { 20 | 21 | this.histogram[i] = 0; 22 | } 23 | } 24 | } 25 | 26 | setHistogram(lower, upper, nbuckets) { 27 | argCheck(arguments, 3, 3); 28 | 29 | this.hLower = lower; 30 | this.hUpper = upper; 31 | this.hBucketSize = (upper - lower) / nbuckets; 32 | this.histogram = new Array(nbuckets + 2); 33 | for (let i = 0; i < this.histogram.length; i++) { 34 | 35 | this.histogram[i] = 0; 36 | } 37 | } 38 | 39 | getHistogram() { 40 | return this.histogram; 41 | } 42 | 43 | record(value, weight) { 44 | argCheck(arguments, 1, 2); 45 | 46 | const w = (typeof weight === 'undefined') ? 1 : weight; 47 | 48 | // document.write("Data series recording " + value + " (weight = " + w + ")\n"); 49 | 50 | if (value > this.Max) this.Max = value; 51 | if (value < this.Min) this.Min = value; 52 | this.Sum += value; 53 | this.Count ++; 54 | if (this.histogram) { 55 | if (value < this.hLower) { 56 | this.histogram[0] += w; 57 | } else if (value > this.hUpper) { 58 | this.histogram[this.histogram.length - 1] += w; 59 | } else { 60 | const index = Math.floor((value - this.hLower) / this.hBucketSize) + 1; 61 | 62 | this.histogram[index] += w; 63 | } 64 | } 65 | 66 | // Wi = Wi-1 + wi 67 | this.W = this.W + w; 68 | 69 | if (this.W === 0) { 70 | return; 71 | } 72 | 73 | // Ai = Ai-1 + wi/Wi * (xi - Ai-1) 74 | const lastA = this.A; 75 | 76 | this.A = lastA + (w / this.W) * (value - lastA); 77 | 78 | // Qi = Qi-1 + wi(xi - Ai-1)(xi - Ai) 79 | this.Q = this.Q + w * (value - lastA) * (value - this.A); 80 | // print("\tW=" + this.W + " A=" + this.A + " Q=" + this.Q + "\n"); 81 | } 82 | 83 | count() { 84 | return this.Count; 85 | } 86 | 87 | min() { 88 | return this.Min; 89 | } 90 | 91 | max() { 92 | return this.Max; 93 | } 94 | 95 | range() { 96 | return this.Max - this.Min; 97 | } 98 | 99 | sum() { 100 | return this.Sum; 101 | } 102 | 103 | sumWeighted() { 104 | return this.A * this.W; 105 | } 106 | 107 | average() { 108 | return this.A; 109 | } 110 | 111 | variance() { 112 | return this.Q / this.W; 113 | } 114 | 115 | deviation() { 116 | return Math.sqrt(this.variance()); 117 | } 118 | } 119 | 120 | class TimeSeries { 121 | constructor(name) { 122 | this.dataSeries = new DataSeries(name); 123 | } 124 | 125 | reset() { 126 | this.dataSeries.reset(); 127 | this.lastValue = NaN; 128 | this.lastTimestamp = NaN; 129 | } 130 | 131 | setHistogram(lower, upper, nbuckets) { 132 | argCheck(arguments, 3, 3); 133 | this.dataSeries.setHistogram(lower, upper, nbuckets); 134 | } 135 | 136 | getHistogram() { 137 | return this.dataSeries.getHistogram(); 138 | } 139 | 140 | record(value, timestamp) { 141 | argCheck(arguments, 2, 2); 142 | 143 | if (!isNaN(this.lastTimestamp)) { 144 | this.dataSeries.record(this.lastValue, timestamp - this.lastTimestamp); 145 | } 146 | 147 | this.lastValue = value; 148 | this.lastTimestamp = timestamp; 149 | } 150 | 151 | finalize(timestamp) { 152 | argCheck(arguments, 1, 1); 153 | 154 | this.record(NaN, timestamp); 155 | } 156 | 157 | count() { 158 | return this.dataSeries.count(); 159 | } 160 | 161 | min() { 162 | return this.dataSeries.min(); 163 | } 164 | 165 | max() { 166 | return this.dataSeries.max(); 167 | } 168 | 169 | range() { 170 | return this.dataSeries.range(); 171 | } 172 | 173 | sum() { 174 | return this.dataSeries.sum(); 175 | } 176 | 177 | average() { 178 | return this.dataSeries.average(); 179 | } 180 | 181 | deviation() { 182 | return this.dataSeries.deviation(); 183 | } 184 | 185 | variance() { 186 | return this.dataSeries.variance(); 187 | } 188 | } 189 | 190 | class Population { 191 | constructor(name) { 192 | this.name = name; 193 | this.population = 0; 194 | this.sizeSeries = new TimeSeries(); 195 | this.durationSeries = new DataSeries(); 196 | } 197 | 198 | reset() { 199 | this.sizeSeries.reset(); 200 | this.durationSeries.reset(); 201 | this.population = 0; 202 | } 203 | 204 | enter(timestamp) { 205 | argCheck(arguments, 1, 1); 206 | 207 | this.population ++; 208 | this.sizeSeries.record(this.population, timestamp); 209 | } 210 | 211 | leave(arrivalAt, leftAt) { 212 | argCheck(arguments, 2, 2); 213 | 214 | this.population --; 215 | this.sizeSeries.record(this.population, leftAt); 216 | this.durationSeries.record(leftAt - arrivalAt); 217 | } 218 | 219 | current() { 220 | return this.population; 221 | } 222 | 223 | finalize(timestamp) { 224 | this.sizeSeries.finalize(timestamp); 225 | } 226 | } 227 | 228 | export { DataSeries, TimeSeries, Population }; 229 | -------------------------------------------------------------------------------- /src/test/timer-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import * as Sim from '../sim'; 4 | 5 | import 'babel-core/register'; 6 | let finalized = 0; 7 | 8 | test('testTimerPlain', (t) => { 9 | const sim = new Sim.Sim(); 10 | 11 | class MyEntity extends Sim.Entity { 12 | start() { 13 | this.setTimer(10).done(this.onTimeout); 14 | } 15 | onTimeout() { 16 | this.count = 1; 17 | t.is(this.time(), 10); 18 | } 19 | finalize() { 20 | t.is(this.count, 1); 21 | t.is(this.time(), 10); 22 | finalized++; 23 | } 24 | } 25 | 26 | sim.addEntity(MyEntity); 27 | sim.simulate(100); 28 | }); 29 | 30 | test('testTimerCustomDone', (t) => { 31 | const sim = new Sim.Sim(); 32 | 33 | class MyEntity extends Sim.Entity { 34 | start() { 35 | this.setTimer(10).done(this.onTimeout); 36 | } 37 | onTimeout() { 38 | this.count = 1; 39 | t.is(this.time(), 10); 40 | } 41 | finalize() { 42 | t.is(this.count, 1); 43 | t.is(this.time(), 10); 44 | finalized++; 45 | } 46 | } 47 | 48 | sim.addEntity(MyEntity); 49 | sim.simulate(100); 50 | }); 51 | 52 | test('testTimerCustomDoneInline', (t) => { 53 | const sim = new Sim.Sim(); 54 | 55 | class MyEntity extends Sim.Entity { 56 | start() { 57 | this.setTimer(10).done(() => { 58 | t.is(this.time(), 10); 59 | this.count = 1; 60 | }); 61 | } 62 | finalize() { 63 | t.is(this.count, 1); 64 | t.is(this.time(), 10); 65 | finalized++; 66 | } 67 | } 68 | 69 | sim.addEntity(MyEntity); 70 | sim.simulate(100); 71 | }); 72 | 73 | test('testTimerRecursive', (t) => { 74 | const sim = new Sim.Sim(); 75 | 76 | class MyEntity extends Sim.Entity { 77 | constructor(...args) { 78 | super(...args); 79 | this.count = 0; 80 | } 81 | 82 | start() { 83 | t.is(this.time(), 10 * this.count); 84 | this.count ++; 85 | this.setTimer(10).done(this.start); 86 | } 87 | finalize() { 88 | t.is(this.count, 11); 89 | t.is(this.time(), 100); 90 | finalized++; 91 | } 92 | } 93 | 94 | sim.addEntity(MyEntity); 95 | sim.simulate(100); 96 | }); 97 | 98 | test('testTimerNoEvent', (t) => { 99 | const sim = new Sim.Sim(); 100 | 101 | class MyEntity extends Sim.Entity { 102 | start() {} // eslint-disable-line no-empty-function 103 | finalize() { 104 | finalized++; 105 | t.is(this.time(), 0); 106 | } 107 | } 108 | 109 | sim.addEntity(MyEntity); 110 | sim.simulate(100); 111 | }); 112 | 113 | test('testTimerZero', (t) => { 114 | const sim = new Sim.Sim(); 115 | 116 | class MyEntity extends Sim.Entity { 117 | start() { 118 | this.setTimer(0).done(() => { 119 | t.is(this.time(), 0); 120 | }); 121 | } 122 | finalize() { 123 | finalized++; 124 | t.is(this.time(), 0); 125 | } 126 | } 127 | 128 | sim.addEntity(MyEntity); 129 | sim.simulate(100); 130 | }); 131 | 132 | 133 | test('testTimerTimeout1', (t) => { 134 | const sim = new Sim.Sim(); 135 | 136 | class MyEntity extends Sim.Entity { 137 | start() { 138 | this.setTimer(10) 139 | .done(() => { 140 | t.fail(); 141 | }) 142 | .waitUntil(5, () => { 143 | t.is(this.time(), 5); 144 | this.count = 1; 145 | }); 146 | } 147 | finalize() { 148 | finalized++; 149 | t.is(this.time(), 10); 150 | t.is(this.count, 1); 151 | } 152 | } 153 | 154 | sim.addEntity(MyEntity); 155 | sim.simulate(100); 156 | }); 157 | 158 | test('testTimerTimeout2', (t) => { 159 | const sim = new Sim.Sim(); 160 | 161 | class MyEntity extends Sim.Entity { 162 | start() { 163 | this.setTimer(10) 164 | .done(() => { 165 | t.is(this.time(), 10); 166 | this.count = 1; 167 | }) 168 | .waitUntil(20, () => { 169 | t.fail(); 170 | }); 171 | } 172 | finalize() { 173 | finalized++; 174 | t.is(this.count, 1); 175 | t.is(this.time(), 20); 176 | } 177 | } 178 | 179 | sim.addEntity(MyEntity); 180 | sim.simulate(100); 181 | }); 182 | 183 | test('testTimerMultipleTimeouts', (t) => { 184 | const sim = new Sim.Sim(); 185 | 186 | class MyEntity extends Sim.Entity { 187 | start() { 188 | this.setTimer(50) 189 | .done(() => { 190 | t.fail(); 191 | }) 192 | .waitUntil(20, () => { 193 | t.fail(); 194 | }) 195 | .waitUntil(10, () => { 196 | t.is(this.time(), 10); 197 | this.count = 1; 198 | }); 199 | 200 | } 201 | finalize() { 202 | finalized++; 203 | t.is(this.count, 1); 204 | t.is(this.time(), 50); 205 | } 206 | } 207 | 208 | sim.addEntity(MyEntity); 209 | sim.simulate(100); 210 | }); 211 | 212 | test('testTimerWaitEvent', (t) => { 213 | const sim = new Sim.Sim(); 214 | 215 | const event = new Sim.Event(); 216 | 217 | class MyEntity extends Sim.Entity { 218 | start() { 219 | this.setTimer(50) 220 | .done(() => { 221 | t.fail(); 222 | }) 223 | .unlessEvent(event, () => { 224 | t.is(this.time(), 10); 225 | this.count = 1; 226 | }); 227 | 228 | this.setTimer(10).done(() => { 229 | event.fire(); 230 | }); 231 | 232 | } 233 | finalize() { 234 | finalized++; 235 | t.is(this.count, 1); 236 | t.is(this.time(), 50); 237 | } 238 | } 239 | 240 | sim.addEntity(MyEntity); 241 | sim.simulate(100); 242 | }); 243 | 244 | -------------------------------------------------------------------------------- /src/test/message-tests.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import * as Sim from '../sim'; 3 | 4 | let finalized = 0; 5 | 6 | 7 | test('testMessageSendOne', (t) => { 8 | const sim = new Sim.Sim(); 9 | 10 | let count = 0; 11 | 12 | class MyEntity extends Sim.Entity { 13 | start() {} // eslint-disable-line no-empty-function 14 | 15 | init() { 16 | if (this.master) { 17 | this.send('message', 10, this.other); 18 | } 19 | } 20 | onMessage(source, message) { 21 | t.is(source, this.other); 22 | t.is(message, 'message'); 23 | t.is(this.time(), 10); 24 | t.is(typeof this.master, 'undefined'); 25 | count++; 26 | } 27 | 28 | finalize() { 29 | finalized++; 30 | } 31 | } 32 | 33 | const o1 = sim.addEntity(MyEntity, true, null); 34 | 35 | const o2 = sim.addEntity(MyEntity, false, o1); 36 | 37 | o1.master = true; 38 | o1.other = o2; 39 | o2.other = o1; 40 | o1.init(); 41 | o2.init(); 42 | sim.simulate(100); 43 | t.is(count, 1); 44 | }); 45 | 46 | test('testMessageSendAll', (t) => { 47 | const sim = new Sim.Sim(); 48 | 49 | let count = 0; 50 | 51 | class MyEntity extends Sim.Entity { 52 | start() {} // eslint-disable-line no-empty-function 53 | 54 | init() { 55 | if (this.master) { 56 | this.send('message', 10); 57 | } 58 | } 59 | onMessage(source, message) { 60 | t.is(source, this.other); 61 | t.is(message, 'message'); 62 | t.is(this.time(), 10); 63 | t.is(typeof this.master, 'undefined'); 64 | count++; 65 | } 66 | 67 | finalize() { 68 | finalized++; 69 | } 70 | } 71 | 72 | const o1 = sim.addEntity(MyEntity); 73 | 74 | const o2 = sim.addEntity(MyEntity); 75 | 76 | const o3 = sim.addEntity(MyEntity); 77 | 78 | o1.master = true; 79 | o2.other = o1; 80 | o3.other = o1; 81 | o1.init(); o2.init(); 82 | 83 | sim.simulate(100); 84 | t.is(count, 2); 85 | }); 86 | 87 | test('testMessageSendArray', (t) => { 88 | const sim = new Sim.Sim(); 89 | 90 | let count = 0; 91 | 92 | class MyEntity extends Sim.Entity { 93 | start() {} // eslint-disable-line no-empty-function 94 | 95 | init() { 96 | if (this.master) { 97 | this.send('message', 10, this.array); 98 | } 99 | } 100 | 101 | onMessage(source, message) { 102 | t.is(source, this.other); 103 | t.is(message, 'message'); 104 | t.is(this.time(), 10); 105 | t.is(typeof this.master, 'undefined'); 106 | count++; 107 | } 108 | 109 | finalize() { 110 | finalized++; 111 | } 112 | } 113 | 114 | const o1 = sim.addEntity(MyEntity); 115 | 116 | const o2 = sim.addEntity(MyEntity); 117 | 118 | const o3 = sim.addEntity(MyEntity); 119 | 120 | o1.master = true; 121 | o1.array = [o2, o3, o1]; 122 | o2.other = o1; 123 | o3.other = o1; 124 | o1.init(); o2.init(); o3.init(); 125 | 126 | sim.simulate(100); 127 | t.is(count, 2); 128 | }); 129 | 130 | test('testMessageNoCallback', (t) => { 131 | const sim = new Sim.Sim(); 132 | 133 | class MyEntity extends Sim.Entity { 134 | start() {} // eslint-disable-line no-empty-function 135 | 136 | init() { 137 | if (this.master) { 138 | this.send('message', 10, this.other); 139 | } 140 | } 141 | 142 | finalize() { 143 | finalized++; 144 | } 145 | } 146 | 147 | const o1 = sim.addEntity(MyEntity); 148 | 149 | const o2 = sim.addEntity(MyEntity); 150 | 151 | o1.master = true; 152 | o1.other = o2; 153 | o2.other = o1; 154 | o1.init(); o2.init(); 155 | sim.simulate(100); 156 | t.is(sim.time(), 10); 157 | }); 158 | 159 | test('testMessageDelayedSendOne', (t) => { 160 | const sim = new Sim.Sim(); 161 | 162 | let count = 0; 163 | 164 | class MyEntity extends Sim.Entity { 165 | start() {} // eslint-disable-line no-empty-function 166 | 167 | init() { 168 | if (this.master) { 169 | this.setTimer(10).done(this.send, this, ['message', 10, this.other]); 170 | } 171 | } 172 | onMessage(source, message) { 173 | t.is(source, this.other); 174 | t.is(message, 'message'); 175 | t.is(this.time(), 20); 176 | t.is(typeof this.master, 'undefined'); 177 | count++; 178 | } 179 | 180 | finalize() { 181 | finalized++; 182 | } 183 | } 184 | 185 | const o1 = sim.addEntity(MyEntity); 186 | 187 | const o2 = sim.addEntity(MyEntity); 188 | 189 | o1.master = true; 190 | o1.other = o2; 191 | o2.other = o1; 192 | o1.init(); o2.init(); 193 | sim.simulate(100); 194 | t.is(count, 1); 195 | }); 196 | 197 | test('testMessageZeroDelay', (t) => { 198 | const sim = new Sim.Sim(); 199 | 200 | let count = 0; 201 | 202 | class MyEntity extends Sim.Entity { 203 | start() {} // eslint-disable-line no-empty-function 204 | 205 | init() { 206 | if (this.master) { 207 | this.setTimer(10).done(this.send, this, ['message', 0, this.other]); 208 | } 209 | } 210 | onMessage(source, message) { 211 | t.is(source, this.other); 212 | t.is(message, 'message'); 213 | t.is(this.time(), 10); 214 | t.is(typeof this.master, 'undefined'); 215 | count++; 216 | } 217 | 218 | finalize() { 219 | finalized++; 220 | } 221 | } 222 | 223 | const o1 = sim.addEntity(MyEntity); 224 | 225 | const o2 = sim.addEntity(MyEntity); 226 | 227 | o1.master = true; 228 | o1.other = o2; 229 | o2.other = o1; 230 | o1.init(); o2.init(); 231 | sim.simulate(100); 232 | t.is(count, 1); 233 | }); 234 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | all: html 4 | 5 | SIMJS_VERSION=0.25 6 | 7 | # You can set these variables from the command line. 8 | SPHINXOPTS = 9 | SPHINXBUILD = sphinx-build 10 | PAPER = 11 | BUILDDIR = _build 12 | 13 | # Internal variables. 14 | PAPEROPT_a4 = -D latex_paper_size=a4 15 | PAPEROPT_letter = -D latex_paper_size=letter 16 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 17 | 18 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 19 | 20 | help: 21 | @echo "Please use \`make ' where is one of" 22 | @echo " html to make standalone HTML files" 23 | @echo " dirhtml to make HTML files named index.html in directories" 24 | @echo " singlehtml to make a single large HTML file" 25 | @echo " pickle to make pickle files" 26 | @echo " json to make JSON files" 27 | @echo " htmlhelp to make HTML files and a HTML help project" 28 | @echo " qthelp to make HTML files and a qthelp project" 29 | @echo " devhelp to make HTML files and a Devhelp project" 30 | @echo " epub to make an epub" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " text to make text files" 34 | @echo " man to make manual pages" 35 | @echo " changes to make an overview of all changed/added/deprecated items" 36 | @echo " linkcheck to check all external links for integrity" 37 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 38 | 39 | clean: 40 | -rm -rf $(BUILDDIR)/* 41 | 42 | html: 43 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 44 | @cd _build/html; mv index.html index-orig.html; awk '{sub(/default.css/, "cloud.css"); print}' index-orig.html > index.html; rm -f index-orig.html 45 | @cp ../release/sim-${SIMJS_VERSION}.js _build/html/examples 46 | @cp ../examples/buffet_restaurant.html _build/html/examples 47 | @cp ../examples/buffet_restaurant.js _build/html/examples 48 | @cp ../examples/traffic_lights.html _build/html/examples 49 | @cp ../examples/traffic_lights.js _build/html/examples 50 | @cp ../examples/examples.css _build/html/examples 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 53 | 54 | dirhtml: 55 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 58 | 59 | singlehtml: 60 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 61 | @echo 62 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 63 | 64 | pickle: 65 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 66 | @echo 67 | @echo "Build finished; now you can process the pickle files." 68 | 69 | json: 70 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 71 | @echo 72 | @echo "Build finished; now you can process the JSON files." 73 | 74 | htmlhelp: 75 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 76 | @echo 77 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 78 | ".hhp project file in $(BUILDDIR)/htmlhelp." 79 | 80 | qthelp: 81 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 82 | @echo 83 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 84 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 85 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JSDES.qhcp" 86 | @echo "To view the help file:" 87 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JSDES.qhc" 88 | 89 | devhelp: 90 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 91 | @echo 92 | @echo "Build finished." 93 | @echo "To view the help file:" 94 | @echo "# mkdir -p $$HOME/.local/share/devhelp/JSDES" 95 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JSDES" 96 | @echo "# devhelp" 97 | 98 | epub: 99 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 100 | @echo 101 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 102 | 103 | latex: 104 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 105 | @echo 106 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 107 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 108 | "(use \`make latexpdf' here to do that automatically)." 109 | 110 | latexpdf: 111 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 112 | @echo "Running LaTeX files through pdflatex..." 113 | make -C $(BUILDDIR)/latex all-pdf 114 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 115 | 116 | text: 117 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 118 | @echo 119 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 120 | 121 | man: 122 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 123 | @echo 124 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 125 | 126 | changes: 127 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 128 | @echo 129 | @echo "The overview file is in $(BUILDDIR)/changes." 130 | 131 | linkcheck: 132 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 133 | @echo 134 | @echo "Link check complete; look for any errors in the above output " \ 135 | "or in $(BUILDDIR)/linkcheck/output.txt." 136 | 137 | doctest: 138 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 139 | @echo "Testing of doctests in the sources finished, look at the " \ 140 | "results in $(BUILDDIR)/doctest/output.txt." 141 | -------------------------------------------------------------------------------- /queuing/src/queue-offline.js: -------------------------------------------------------------------------------- 1 | class ServerModel { 2 | constructor(sim, random) { 3 | this.nservers = 1; 4 | this.mu = 1; 5 | this.infinite = true; 6 | this.maxqlen = 0; 7 | this.sim = sim; 8 | this.random = random; 9 | } 10 | 11 | start() { 12 | this.entity = this.sim.addEntity(ServerEntity, null, this.random, this.nservers, this.mu); 13 | } 14 | 15 | connect(dest) { 16 | this.entity.dest = dest.entity; 17 | } 18 | 19 | printStats(printf) { 20 | const service = this.entity.facility; 21 | const qd = service.queueStats().durationSeries; 22 | const qs = service.queueStats().sizeSeries; 23 | const sd = service.systemStats().durationSeries; 24 | const ss = service.systemStats().sizeSeries; 25 | const usage = service.usage() / this.sim.time() * 100; 26 | printf(`Queue ${this.name}`); 27 | printf(`\tArrival = ${qd.count()}`); 28 | printf(`\tServer Usage = ${usage.toFixed(2)}%`); 29 | printf(`\tTime spent in queue = ${qd.average().toFixed(3)}`); 30 | printf(`\tTime spent in system = ${sd.average().toFixed(3)}`); 31 | printf(`\tSize of queue = ${qs.average().toFixed(3)}`); 32 | printf(`\tCustomers in system = ${ss.average().toFixed(3)}`); 33 | } 34 | } 35 | 36 | class SourceModel { 37 | constructor(sim, random) { 38 | this.lambda = 0.25; 39 | this.sim = sim; 40 | this.random = random; 41 | } 42 | 43 | start() { 44 | this.entity = this.sim.addEntity(SourceEntity, null, this.random, this.lambda); 45 | } 46 | 47 | connect(dest) { 48 | this.entity.dest = dest.entity; 49 | } 50 | } 51 | 52 | class SplitterModel { 53 | constructor(sim, random) { 54 | this.prob = 0.5; 55 | this.sim = sim; 56 | this.random = random; 57 | } 58 | 59 | start() { 60 | this.entity = this.sim.addEntity(SplitterEntity, null, this.random, this.prob); 61 | } 62 | 63 | connect(dest, channel) { 64 | this.entity.dest[channel] = dest.entity; 65 | } 66 | } 67 | 68 | class SinkModel { 69 | constructor(sim, random) { 70 | this.entity = null; 71 | this.sim = sim; 72 | } 73 | 74 | start() { 75 | this.entity = this.sim.addEntity(SinkEntity); 76 | 77 | } 78 | 79 | printStats(printf) { 80 | const p = this.entity.population; 81 | printf(`Sink ${this.name}`); 82 | printf(`\tDepartures = ${p.durationSeries.count()}`); 83 | printf(`\tPopulation = ${p.sizeSeries.average()}`); 84 | printf(`\tStay Duration = ${p.durationSeries.average()}`); 85 | } 86 | } 87 | 88 | /***************************************************/ 89 | 90 | class ServerEntity extends Sim.Entity { 91 | start(random, nservers, mu) { 92 | this.random = random; 93 | this.mu = mu; 94 | this.facility = new Sim.Facility('queue'); 95 | } 96 | 97 | arrive(stamp) { 98 | const duration = this.random.exponential(this.mu); 99 | const ro = this.useFacility(this.facility, duration); 100 | if (this.dest) { 101 | ro.done(this.dest.arrive, this.dest, stamp); 102 | } 103 | } 104 | } 105 | 106 | /*-------------------------*/ 107 | class SourceEntity extends Sim.Entity { 108 | start(random, lambda) { 109 | this.random = random; 110 | this.lambda = lambda; 111 | this.setTimer(0).done(this.traffic); 112 | } 113 | 114 | traffic() { 115 | if (!this.dest) return; 116 | this.dest.arrive(this.time()); 117 | 118 | this.generated ++; 119 | 120 | const duration = this.random.exponential(this.lambda); 121 | 122 | this.setTimer(duration).done(this.traffic); 123 | } 124 | } 125 | 126 | /*-------------------------*/ 127 | 128 | class SinkEntity extends Sim.Entity { 129 | start() { 130 | this.population = new Sim.Population(); 131 | } 132 | 133 | arrive(stamp) { 134 | if (!stamp) stamp = 0; 135 | this.population.enter(stamp); 136 | this.population.leave(stamp, this.time()); 137 | } 138 | } 139 | 140 | /*-------------------------*/ 141 | class SplitterEntity extends Sim.Entity{ 142 | start(random, prob) { 143 | this.random = random; 144 | this.prob = prob; 145 | } 146 | 147 | arrive(stamp) { 148 | const r = this.random.uniform(0.0, 1.0); 149 | if (r < this.prob) { 150 | if (this.dest[0]) this.dest[0].arrive(stamp); 151 | } else { 152 | if (this.dest[1]) this.dest[1].arrive(stamp); 153 | } 154 | } 155 | } 156 | 157 | /***************************************/ 158 | function QueueSimulator(jsontext) { 159 | const json = JSON.parse(jsontext); 160 | 161 | let until = 5000, seed = 1234; 162 | if (json.until) until = json.until; 163 | if (json.seed) seed = json.seed; 164 | 165 | const sim = new Sim(); 166 | const random = new Random(seed); 167 | 168 | const len = json.objects.length; 169 | const dict = {}; 170 | const ModelFactory = {queue: ServerModel, source: SourceModel, 171 | splitter: SplitterModel, sink: SinkModel}; 172 | 173 | for (var i = len - 1; i >= 0; i--) { 174 | var conf = json.objects[i]; 175 | var model; 176 | if (conf.type === 'queue') model = new ServerModel(sim, random); 177 | else if (conf.type === 'source') model = new SourceModel(sim, random); 178 | else if (conf.type === 'sink') model = new SinkModel(sim, random); 179 | else if (conf.type === 'splitter') model = new SplitterModel(sim, random); 180 | else throw `Cannot create model for ${conf.name}`; 181 | 182 | model.name = conf.name; 183 | // for (prop in conf.model) model[prop] = conf.model[prop]; 184 | dict[conf.name] = model; 185 | model.start(); 186 | } 187 | 188 | for (var i = len - 1; i >= 0; i--) { 189 | var conf = json.objects[i]; 190 | if (!conf.out) continue; 191 | 192 | const from = dict[conf.name]; 193 | if (!from) continue; 194 | 195 | if (conf.out instanceof Array) { 196 | for (let j = conf.out.length - 1; j >= 0; j--) { 197 | var to = dict[conf.out[j]]; 198 | if (to) from.connect(to, j); 199 | } 200 | } else { 201 | var to = dict[conf.out]; 202 | if (to) from.connect(to); 203 | } 204 | } 205 | 206 | sim.simulate(until); 207 | 208 | for (modelname in dict) { 209 | var model = dict[modelname]; 210 | if (model.printStats) model.printStats(print); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /ui/panel.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | Sim Panel test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 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 |

Server 1

72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
Metric MeanStd Dev
Arrivals--
Server Utilization--
Time spent in queue--
Time spent in system--
Size of queue--
Customers in system--
81 |
82 |
83 |

Monitor

84 | 85 | 86 | 87 | 88 |
MetricMeanStd Dev
Arrivals--
Inter-arrival (sec)--
89 |
90 |
91 | 92 | More here. 93 |
94 |
95 |
96 | 97 | 98 |
99 |
100 |
101 | 102 |
103 |
104 |
105 | 106 | 107 |
108 |
109 |
110 | 111 |
112 |
113 |
114 | 115 | 116 |
117 |
118 |
119 | 120 |
121 | 122 |
123 | 124 |
125 |

Save the following to a text file.

126 | 127 |
128 |
129 |

Copy the text below

130 | 131 |
132 | 133 |
134 | 135 |
136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /queuing/src/image_view.js: -------------------------------------------------------------------------------- 1 | class ImageView { 2 | constructor(canvas, type, name, x, y, hasIn, hasOut) { 3 | this.canvas = canvas; 4 | this.type = type; 5 | this.name = name; 6 | 7 | 8 | if (type === 'queue') { 9 | this.width = 116 * 0.8; 10 | this.height = 55 * 0.8; 11 | this.image = canvas.image('images/server.png', x, y, this.width, this.height); 12 | 13 | } else if (type === 'source') { 14 | this.image = canvas.image('images/customers.png', x, y, 34, 34); 15 | this.width = 34; 16 | this.height = 34; 17 | } else if (type === 'sink') { 18 | this.width = 32; 19 | this.height = 32; 20 | this.image = canvas.image('images/door_out.png', x, y, this.width, this.height); 21 | } 22 | this.x = x; 23 | this.y = y; 24 | this.hasIn = hasIn; 25 | this.hasOut = hasOut; 26 | 27 | this.text = canvas.text(x, y, this.name); 28 | this.counters = canvas.text(x, y, ''); 29 | this.counters.hide(); 30 | 31 | this.image.attr({cursor: 'move'}); 32 | this.image.view = this; 33 | this.image.animate({scale: "1.2 1.2"}, 200, function () { 34 | this.animate({scale: "1 1"}, 200); 35 | }); 36 | 37 | if (this.hasOut) { 38 | this.arrow = canvas.image("images/orange-arrow.gif", x, y, 12, 12); 39 | this.arrow.view = this; 40 | this.arrow.drag( 41 | function (dx, dy) { 42 | this.attr({x: this.ox + dx, y: this.oy + dy}); 43 | this.paper.connection(this.conn); 44 | }, 45 | function () { 46 | this.conn = this.paper.connection(this.view.image, this, "#000"); 47 | this.ox = this.attr("x"); 48 | this.oy = this.attr("y"); 49 | }, 50 | function () { 51 | this.conn.line.remove(); 52 | this.conn = null; 53 | 54 | const views = QueueApp.views; 55 | const len = views.length; 56 | const x = this.attr('x'); 57 | const y = this.attr('y'); 58 | 59 | for (let i = len - 1; i >= 0; i--) { 60 | const obj = views[i]; 61 | if (obj.acceptDrop(x, y)) { 62 | this.hide(); 63 | this.view.connect(obj); 64 | return; 65 | } 66 | } 67 | 68 | const view = this.view; 69 | this.attr({x: view.x + view.width + 2, y: view.y + view.height / 2 - 6}); 70 | }); 71 | } 72 | 73 | // move 74 | this.moveto(x, y); 75 | 76 | // Set up event listeners 77 | this.image.drag( 78 | function (dx, dy) { 79 | const view = this.view; 80 | view.moveto(view.ox + dx, view.oy + dy); 81 | }, 82 | function () { 83 | const view = this.view; 84 | view.ox = view.x; 85 | view.oy = view.y; 86 | }, 87 | () => { 88 | 89 | }); 90 | 91 | this.image.dblclick(function () { 92 | this.view.model.showSettings(); 93 | }); 94 | } 95 | 96 | moveto(x, y) { 97 | var len; 98 | var i; 99 | let dot; 100 | 101 | if (x > 600 - this.width || y > 400 - this.height || x < 0 || y < 0) { 102 | return; 103 | } 104 | 105 | this.x = x; 106 | this.y = y; 107 | 108 | this.image.attr({x, y}); 109 | this.text.attr({x: this.x + this.width / 2, y: this.y + this.height + 5}); 110 | this.counters.attr({x: this.x + this.width / 2, y: this.y + this.height + 20}); 111 | if (this.arrow) { 112 | this.arrow.attr({x: this.x + this.width + 2, y: this.y + this.height / 2 - 6}); 113 | } 114 | 115 | if (this.hasIn) { 116 | var len = QueueApp.views.length; 117 | for (var i = len - 1; i >= 0; i--) { 118 | QueueApp.views[i].moveConnection(this); 119 | } 120 | } 121 | 122 | if (this.arrow && this.arrow.conn) { 123 | this.canvas.connection(this.arrow.conn); 124 | } 125 | } 126 | 127 | connect(to) { 128 | const conn = this.canvas.connection(this.image, to.dropObject(), "#000"); 129 | conn.line.attr({'stroke-width': 3, 'stroke': '#F7D68A'}); 130 | conn.fromView = this; 131 | conn.toView = to; 132 | this.arrow.conn = conn; 133 | this.arrow.hide(); 134 | this.model.dest = to.model; 135 | } 136 | 137 | unlink() { 138 | let i, len, index; 139 | 140 | len = QueueApp.models.length; 141 | for (i = len - 1; i >= 0; i--) { 142 | if (QueueApp.models[i] === this.model) { 143 | index = i; 144 | break; 145 | } 146 | } 147 | 148 | if (index) QueueApp.models.splice(index, 1); 149 | 150 | 151 | if (this.model) this.model.unlink(); 152 | this.disconnect(); 153 | len = QueueApp.views.length; 154 | for (i = len - 1; i >= 0; i--) { 155 | QueueApp.views[i].disconnect(this); 156 | if (QueueApp.views[i] === this) index = i; 157 | } 158 | 159 | QueueApp.views.splice(index, 1); 160 | 161 | this.image.remove(); 162 | if (this.arrow) this.arrow.remove(); 163 | this.counters.remove(); 164 | this.text.remove(); 165 | 166 | } 167 | 168 | disconnect(dest) { 169 | if (this.arrow && this.arrow.conn && (!dest || this.arrow.conn.toView === dest)) { 170 | this.arrow.conn.line.remove(); 171 | this.arrow.conn = null; 172 | this.arrow.attr({x: this.x + this.width + 2, y: this.y + this.height / 2 - 6}); 173 | this.arrow.show(); 174 | this.model.dest = null; 175 | } 176 | } 177 | 178 | dropObject() { 179 | return this.image; 180 | } 181 | 182 | acceptDrop(x, y) { 183 | if (!this.hasIn) return false; 184 | return (x > (this.x - 10) && x < (this.x + this.width + 10) 185 | && y > (this.y - 10) && y < (this.y + this.height + 10)); 186 | } 187 | 188 | moveConnection(dest) { 189 | if (this.arrow && this.arrow.conn && this.arrow.conn.toView === dest) { 190 | this.canvas.connection(this.arrow.conn); 191 | } 192 | } 193 | 194 | jsonify() { 195 | const json = { 196 | x: this.x, 197 | y: this.y, 198 | type: this.type, 199 | name: this.name}; 200 | 201 | if (this.arrow && this.arrow.conn) { 202 | json.out = this.arrow.conn.toView.name; 203 | } 204 | 205 | if (this.model) { 206 | json.model = this.model.jsonify(); 207 | } 208 | 209 | return json; 210 | } 211 | 212 | showCounters(incoming, outgoing) { 213 | /* 214 | var msg = ''; 215 | 216 | if (!isNaN(incoming)) msg += 'In: ' + incoming; 217 | if (!isNaN(outgoing)) msg += ' Out: ' + outgoing; 218 | this.counters.attr({text: msg}); 219 | this.counters.show(); 220 | */ 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /ui/queue-offline.js: -------------------------------------------------------------------------------- 1 | load('../src/sim.js'); 2 | load('../src/stats.js'); 3 | load('../src/queues.js'); 4 | load('../src/random.js'); 5 | load('../src/request.js'); 6 | 7 | var model = '{"until":25000,"seed":1234,"version":"1.0","objects":[{"x":139,"y":229,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":259,"y":148,"type":"queue","name":"queue_1","model":{"nservers":1,"mu":1,"infinite":true,"maxqlen":0}}]}'; 8 | 9 | 10 | /***************************************************/ 11 | 12 | function ServerModel(sim, random) { 13 | this.nservers = 1; 14 | this.mu = 1; 15 | this.infinite = true; 16 | this.maxqlen = 0; 17 | this.sim = sim; 18 | this.random = random; 19 | } 20 | 21 | ServerModel.prototype.start = function () { 22 | this.entity = this.sim.addEntity(ServerEntity, this.random, this.nservers, this.mu); 23 | } 24 | 25 | ServerModel.prototype.connect = function (dest) { 26 | this.entity.dest = dest.entity; 27 | }; 28 | 29 | ServerModel.prototype.printStats = function (printf) { 30 | var service = this.entity.facility; 31 | var qd = service.queueStats().durationSeries; 32 | var qs = service.queueStats().sizeSeries; 33 | var sd = service.systemStats().durationSeries; 34 | var ss = service.systemStats().sizeSeries; 35 | var usage = service.usage() / this.sim.time() * 100; 36 | printf("Queue " + this.name); 37 | printf("\tArrival = " + qd.count()); 38 | printf("\tServer Usage = " + usage.toFixed(2) + "%"); 39 | printf("\tTime spent in queue = " + qd.average().toFixed(3)); 40 | printf("\tTime spent in system = " + sd.average().toFixed(3)); 41 | printf("\tSize of queue = " + qs.average().toFixed(3)); 42 | printf("\tCustomers in system = " + ss.average().toFixed(3)); 43 | }; 44 | 45 | 46 | /*-------------------------*/ 47 | function SourceModel(sim, random) { 48 | this.lambda = 0.25; 49 | this.sim = sim; 50 | this.random = random; 51 | } 52 | 53 | SourceModel.prototype.start = function () { 54 | this.entity = this.sim.addEntity(SourceEntity, this.random, this.lambda); 55 | }; 56 | 57 | SourceModel.prototype.connect = function (dest) { 58 | this.entity.dest = dest.entity; 59 | }; 60 | 61 | /*-------------------------*/ 62 | function SplitterModel() { 63 | this.prob = 0.5; 64 | this.sim = sim; 65 | this.random = random; 66 | } 67 | 68 | SplitterModel.prototype.start = function () { 69 | this.entity = this.sim.addEntity(SplitterEntity, this.random, this.prob); 70 | }; 71 | 72 | SplitterModel.prototype.connect = function (dest, channel) { 73 | this.entity.dest[channel] = dest.entity; 74 | }; 75 | 76 | /*-------------------------*/ 77 | function MonitorModel(sim, random) { 78 | this.sim = sim; 79 | } 80 | 81 | MonitorModel.prototype.start = function () { 82 | this.entity = this.sim.addEntity(MonitorEntity); 83 | }; 84 | 85 | MonitorModel.prototype.connect = function (dest) { 86 | this.entity.dest = dest.entity; 87 | }; 88 | 89 | MonitorModel.prototype.printStats = function (printf) { 90 | var m = this.entity.monitor; 91 | 92 | printf("Monitor " + this.name); 93 | printf("\tArrivals = " + m.count().toFixed(3)); 94 | printf("\tInterarrival = " + m.average().toFixed(3)); 95 | }; 96 | 97 | /***************************************************/ 98 | 99 | var ServerEntity = { 100 | start: function (random, nservers, mu) { 101 | this.random = random; 102 | this.mu = mu; 103 | this.facility = new Sim.Facility('queue'); 104 | }, 105 | 106 | arrive: function (from) { 107 | var duration = this.random.exponential(this.mu); 108 | var ro = this.useFacility(this.facility, duration); 109 | if (this.dest) { 110 | ro.done(this.dest.arrive, this.dest, this); 111 | } 112 | } 113 | }; 114 | 115 | /*-------------------------*/ 116 | var SourceEntity = { 117 | start: function (random, lambda) { 118 | this.random = random; 119 | this.lambda = lambda; 120 | this.setTimer(0).done(this.traffic); 121 | }, 122 | 123 | traffic: function () { 124 | if (!this.dest) return; 125 | 126 | var duration = this.random.exponential(this.lambda); 127 | 128 | this.setTimer(duration) 129 | .done(this.dest.arrive, this.dest, this) 130 | .done(this.traffic); 131 | } 132 | }; 133 | 134 | /*-------------------------*/ 135 | var MonitorEntity = { 136 | start: function () { 137 | this.monitor = new Sim.TimeSeries(); 138 | }, 139 | 140 | arrive: function () { 141 | this.monitor.record(1, this.time()); 142 | if (this.dest) this.dest.arrive(); 143 | } 144 | }; 145 | 146 | /*-------------------------*/ 147 | var SplitterEntity = { 148 | start: function (random, prob) { 149 | this.random = random; 150 | this.prob = prob; 151 | }, 152 | 153 | arrive: function () { 154 | var r = this.random.uniform(0.0, 1.0); 155 | if (r < this.prob) { 156 | if (this.dest[0]) this.dest[0].arrive(); 157 | } else { 158 | if (this.dest[1]) this.dest[1].arrive(); 159 | } 160 | } 161 | }; 162 | 163 | 164 | function QueueApp(json) { 165 | var until = 5000, seed = 1234; 166 | if (json.until) until = json.until; 167 | if (json.seed) seed = json.seed; 168 | 169 | var sim = new Sim(); 170 | var random = new Random(seed); 171 | 172 | var len = json.objects.length; 173 | var dict = {}; 174 | var ModelFactory = {queue: ServerModel, source: SourceModel, 175 | splitter: SplitterModel, monitor: MonitorModel}; 176 | 177 | for (var i = len - 1; i >= 0; i--) { 178 | var conf = json.objects[i]; 179 | var model; 180 | if (conf.type === 'queue') model = new ServerModel(sim, random); 181 | else if (conf.type === 'source') model = new SourceModel(sim, random); 182 | else if (conf.type === 'monitor') model = new MonitorModel(sim, random); 183 | else if (conf.type === 'splitter') model = new SplitterModel(sim, random); 184 | else throw "Cannot create model for " + conf.name; 185 | 186 | model.name = conf.name; 187 | // for (prop in conf.model) model[prop] = conf.model[prop]; 188 | dict[conf.name] = model; 189 | model.start(); 190 | } 191 | 192 | for (var i = len - 1; i >= 0; i--) { 193 | var conf = json.objects[i]; 194 | if (!conf.out) continue; 195 | 196 | var from = dict[conf.name]; 197 | if (!from) continue; 198 | 199 | if (conf.out instanceof Array) { 200 | for (var j = conf.out.length - 1; j >= 0; j--) { 201 | var to = dict[conf.out[j]]; 202 | if (to) from.connect(to, j); 203 | } 204 | } else { 205 | var to = dict[conf.out]; 206 | if (to) from.connect(to); 207 | } 208 | } 209 | 210 | sim.simulate(until); 211 | 212 | for (modelname in dict) { 213 | var model = dict[modelname]; 214 | if (model.printStats) model.printStats(print); 215 | } 216 | } 217 | 218 | QueueApp(JSON.parse(model)); -------------------------------------------------------------------------------- /queuing/src/models.js: -------------------------------------------------------------------------------- 1 | const MODELS = { 2 | 'model_mm1': { 3 | model: '{"until":14400,"seed":1234,"version":"1.0","objects":[{"x":102,"y":136,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":252,"y":93,"type":"queue","name":"queue_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 4 | }, 5 | 6 | 'model_mm1feedback': { 7 | model: '{"until":14400,"seed":1234,"version":"1.0","objects":[{"x":399,"y":171,"type":"sink","name":"sink_1","model":null},{"x":257,"y":186,"type":"splitter","name":"splitter_1","out":["queue_1","sink_1"],"model":{"prob":"0.1"}},{"x":102,"y":136,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":210,"y":83,"type":"queue","name":"queue_1","out":"splitter_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 8 | }, 9 | 10 | 'model_backforth': { 11 | model: '{"until":28800,"seed":1234,"version":"1.0","objects":[{"x":496,"y":128,"type":"sink","name":"sink_1","model":null},{"x":241,"y":187,"type":"queue","name":"queue_2","out":"splitter_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":379,"y":193,"type":"splitter","name":"splitter_2","out":["queue_1","sink_1"],"model":{"prob":0.5}},{"x":377,"y":93,"type":"splitter","name":"splitter_1","out":["sink_1","queue_2"],"model":{"prob":0.5}},{"x":228,"y":87,"type":"queue","name":"queue_1","out":"splitter_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":91,"y":84,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}}]}' 12 | }, 13 | 14 | 'model_waterfall': { 15 | model: '{"until":14400,"seed":1234,"version":"1.0","objects":[{"x":82,"y":29,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":558,"y":341,"type":"sink","name":"sink_1","model":null},{"x":440,"y":281,"type":"queue","name":"queue_4","out":"sink_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":342,"y":211,"type":"queue","name":"queue_3","out":"queue_4","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":241,"y":144,"type":"queue","name":"queue_2","out":"queue_3","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":140,"y":76,"type":"queue","name":"queue_1","out":"queue_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 16 | }, 17 | 18 | 'model_compile': { 19 | model: '{"until":28800,"seed":1234,"version":"1.0","objects":[{"x":526,"y":246,"type":"sink","name":"sink_1","model":null},{"x":272,"y":192,"type":"splitter","name":"splitter_3","out":["queue_1","queue_2"],"model":{"prob":0.5}},{"x":107,"y":36,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":456,"y":171,"type":"splitter","name":"splitter_2","out":["queue_1","sink_1"],"model":{"prob":0.5}},{"x":336,"y":187,"type":"queue","name":"queue_2","out":"splitter_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":212,"y":63,"type":"queue","name":"queue_1","out":"splitter_3","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 20 | }, 21 | 22 | 'model_freeway': { 23 | model: '{"until":28800,"seed":1234,"version":"1.0","objects":[{"x":380,"y":2,"type":"source","name":"source_4","out":"queue_4","model":{"lambda":0.25}},{"x":255,"y":80,"type":"source","name":"source_3","out":"queue_3","model":{"lambda":0.25}},{"x":115,"y":181,"type":"source","name":"source_2","out":"queue_2","model":{"lambda":0.25}},{"x":8,"y":229,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":561,"y":105,"type":"sink","name":"sink_4","model":null},{"x":510,"y":198,"type":"sink","name":"sink_3","model":null},{"x":374,"y":287,"type":"sink","name":"sink_2","model":null},{"x":204,"y":336,"type":"sink","name":"sink_1","model":null},{"x":139,"y":287,"type":"splitter","name":"splitter_3","out":["queue_2","sink_1"],"model":{"prob":0.5}},{"x":299,"y":213,"type":"splitter","name":"splitter_2","out":["queue_3","sink_2"],"model":{"prob":0.5}},{"x":430,"y":137,"type":"splitter","name":"splitter_1","out":["queue_4","sink_3"],"model":{"prob":0.5}},{"x":463,"y":43,"type":"queue","name":"queue_4","out":"sink_4","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":327,"y":132,"type":"queue","name":"queue_3","out":"splitter_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":194,"y":207,"type":"queue","name":"queue_2","out":"splitter_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":31,"y":281,"type":"queue","name":"queue_1","out":"splitter_3","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 24 | }, 25 | 26 | 'model_staytillend': { 27 | model: '{"until":28800,"seed":1234,"version":"1.0","objects":[{"x":554,"y":349,"type":"sink","name":"sink_5","model":null},{"x":515,"y":287,"type":"splitter","name":"splitter_4","out":["sink_4","sink_5"],"model":{"prob":0.5}},{"x":420,"y":216,"type":"splitter","name":"splitter_3","out":["sink_3","queue_4"],"model":{"prob":0.5}},{"x":319,"y":144,"type":"splitter","name":"splitter_2","out":["sink_2","queue_3"],"model":{"prob":0.5}},{"x":212,"y":83,"type":"splitter","name":"splitter_1","out":["sink_1","queue_2"],"model":{"prob":0.5}},{"x":561,"y":242,"type":"sink","name":"sink_4","model":null},{"x":457,"y":164,"type":"sink","name":"sink_3","model":null},{"x":362,"y":94,"type":"sink","name":"sink_2","model":null},{"x":65,"y":27,"type":"source","name":"source_1","out":"queue_1","model":{"lambda":0.25}},{"x":258,"y":28,"type":"sink","name":"sink_1","model":null},{"x":414,"y":281,"type":"queue","name":"queue_4","out":"splitter_4","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":316,"y":211,"type":"queue","name":"queue_3","out":"splitter_3","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":222,"y":139,"type":"queue","name":"queue_2","out":"splitter_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":112,"y":76,"type":"queue","name":"queue_1","out":"splitter_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 28 | }, 29 | 30 | 'model_winner': { 31 | model: '{"until":28800,"seed":1234,"version":"1.0","objects":[{"x":350,"y":293,"type":"source","name":"source_2","out":"queue_3","model":{"lambda":0.25}},{"x":57,"y":288,"type":"source","name":"source_1","out":"queue_2","model":{"lambda":0.25}},{"x":539,"y":250,"type":"sink","name":"sink_3","model":null},{"x":294,"y":239,"type":"sink","name":"sink_2","model":null},{"x":371,"y":24,"type":"sink","name":"sink_1","model":null},{"x":225,"y":203,"type":"splitter","name":"splitter_3","out":["queue_1","sink_2"],"model":{"prob":0.5}},{"x":480,"y":211,"type":"splitter","name":"splitter_2","out":["queue_1","sink_3"],"model":{"prob":0.5}},{"x":380,"y":205,"type":"queue","name":"queue_3","out":"splitter_2","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":109,"y":198,"type":"queue","name":"queue_2","out":"splitter_3","model":{"nservers":1,"mu":"1","maxqlen":"-1"}},{"x":256,"y":85,"type":"queue","name":"queue_1","out":"sink_1","model":{"nservers":1,"mu":"1","maxqlen":"-1"}}]}' 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /queuing/src/splitter_view.js: -------------------------------------------------------------------------------- 1 | class SplitterView { 2 | constructor(canvas, type, name, x, y, hasIn, hasOut) { 3 | this.canvas = canvas; 4 | this.type = type; 5 | this.name = name; 6 | 7 | this.hidden = [canvas.rect(x, y, 10, 10), canvas.rect(x, y, 10, 10)]; 8 | this.width = 41 * 0.7; 9 | this.height = 48 * 0.7; 10 | this.image = canvas.image('images/splitter.png', x, y, this.width, this.height); 11 | 12 | 13 | this.x = x; 14 | this.y = y; 15 | 16 | this.hidden[0].attr({'stroke-width': '0'}); 17 | this.hidden[1].attr({'stroke-width': '0'}); 18 | this.image.attr({cursor: 'move'}); 19 | this.image.view = this; 20 | this.image.animate({scale: "1.2 1.2"}, 200, function () { 21 | this.animate({scale: "1 1"}, 200); 22 | }); 23 | 24 | this.arrows = [null, null]; 25 | this.counters = canvas.text(x, y, ''); 26 | 27 | for (let i = 0; i < 2; i ++) { 28 | const arrow = canvas.image("images/orange-arrow.gif", x, y, 12, 12); 29 | arrow.view = this; 30 | arrow.id = i; 31 | arrow.drag( 32 | function (dx, dy) { 33 | this.attr({x: this.ox + dx, y: this.oy + dy}); 34 | this.paper.connection(this.conn); 35 | }, 36 | function () { 37 | const from = this.view.hidden[this.id]; 38 | this.conn = this.paper.connection(from, this, "#000"); 39 | this.ox = this.attr("x"); 40 | this.oy = this.attr("y"); 41 | }, 42 | function () { 43 | this.conn.line.remove(); 44 | this.conn = null; 45 | 46 | const views = QueueApp.views; 47 | const len = views.length; 48 | const x = this.attr('x'); 49 | const y = this.attr('y'); 50 | 51 | for (let i = len - 1; i >= 0; i--) { 52 | const obj = views[i]; 53 | if (obj.acceptDrop(x, y)) { 54 | this.hide(); 55 | this.view.connect(obj, this.id); 56 | return; 57 | } 58 | } 59 | 60 | const view = this.view; 61 | if (this.id === 0) { 62 | this.attr({x: view.x + view.width + 2, y: view.y + 5}); 63 | } else { 64 | this.attr({x: view.x + view.width + 2, y: view.y + view.height - 15}); 65 | } 66 | }); 67 | this.arrows[i] = arrow; 68 | } 69 | 70 | // move 71 | this.moveto(x, y); 72 | 73 | // Set up event listeners 74 | this.image.drag( 75 | function (dx, dy) { 76 | const view = this.view; 77 | view.moveto(view.ox + dx, view.oy + dy); 78 | }, 79 | function () { 80 | const view = this.view; 81 | view.ox = view.x; 82 | view.oy = view.y; 83 | }, 84 | () => { 85 | 86 | }); 87 | 88 | this.image.dblclick(function () { 89 | this.view.model.showSettings(); 90 | }); 91 | } 92 | 93 | moveto(x, y) { 94 | var len; 95 | var i; 96 | let dot; 97 | 98 | if (x > 600 - this.width || y > 400 - this.height || x < 0 || y < 0) { 99 | return; 100 | } 101 | 102 | this.x = x; 103 | this.y = y; 104 | 105 | this.image.attr({x, y}); 106 | 107 | this.hidden[0].attr({x: this.x + this.width - 20, 108 | y: this.y + 5}); 109 | this.hidden[1].attr({x: this.x + this.width - 20, 110 | y: this.y + this.height - 15}); 111 | 112 | 113 | this.arrows[0].attr({x: this.x + this.width + 2, y: this.y + 5}); 114 | this.arrows[1].attr({x: this.x + this.width + 2, y: this.y + this.height - 15}); 115 | this.counters.attr({x: this.x + this.width / 2, y: this.y + this.height + 5}) 116 | 117 | var len = QueueApp.views.length; 118 | for (var i = len - 1; i >= 0; i--) { 119 | QueueApp.views[i].moveConnection(this); 120 | } 121 | 122 | if (this.arrows[0].conn) this.canvas.connection(this.arrows[0].conn); 123 | if (this.arrows[1].conn) this.canvas.connection(this.arrows[1].conn); 124 | } 125 | 126 | connect(to, channel) { 127 | const conn = this.canvas.connection(this.hidden[channel], to.dropObject(), "#000"); 128 | conn.line.attr({'stroke-width': 3, 'stroke': '#F7D68A'}); 129 | conn.fromView = this; 130 | conn.toView = to; 131 | this.arrows[channel].conn = conn; 132 | this.arrows[channel].hide(); 133 | this.model.dest[channel] = to.model; 134 | } 135 | 136 | unlink() { 137 | var i; 138 | var len; 139 | let index; 140 | 141 | len = QueueApp.models.length; 142 | for (i = len - 1; i >= 0; i--) { 143 | if (QueueApp.models[i] === this.model) { 144 | index = i; 145 | break; 146 | } 147 | } 148 | 149 | if (index) QueueApp.models.splice(index, 1); 150 | if (this.model) this.model.unlink(); 151 | this.disconnect(); 152 | var len = QueueApp.views.length; 153 | for (var i = len - 1; i >= 0; i--) { 154 | QueueApp.views[i].disconnect(this); 155 | if (QueueApp.views[i] === this) index = i; 156 | } 157 | 158 | QueueApp.views.splice(index, 1); 159 | 160 | this.image.remove(); 161 | this.arrows[0].remove(); 162 | this.arrows[1].remove(); 163 | this.hidden[0].remove(); 164 | this.hidden[0].remove(); 165 | this.counters.remove(); 166 | } 167 | 168 | disconnect(dest) { 169 | for (let i = 0; i < 2; i ++) { 170 | const arrow = this.arrows[i]; 171 | if (arrow && arrow.conn && (!dest || arrow.conn.toView === dest)) { 172 | arrow.conn.line.remove(); 173 | arrow.conn = null; 174 | 175 | if (i === 0) { 176 | arrow.attr({x: this.x + this.width + 2, y: this.y + 5}); 177 | } else { 178 | arrow.attr({x: this.x + this.width + 2, y: this.y + this.height - 15}); 179 | } 180 | arrow.show(); 181 | } 182 | } 183 | } 184 | 185 | dropObject() { 186 | return this.image; 187 | } 188 | 189 | acceptDrop(x, y) { 190 | return (x > (this.x - 10) && x < (this.x + this.width + 10) 191 | && y > (this.y - 10) && y < (this.y + this.height + 10)); 192 | } 193 | 194 | moveConnection(dest) { 195 | for (let i = 0; i < 2; i ++) { 196 | const arrow = this.arrows[i]; 197 | if (arrow && arrow.conn && arrow.conn.toView === dest) { 198 | this.canvas.connection(arrow.conn); 199 | } 200 | } 201 | } 202 | 203 | jsonify() { 204 | const json = { 205 | x: this.x, 206 | y: this.y, 207 | type: this.type, 208 | name: this.name, 209 | out: [null, null]}; 210 | 211 | 212 | for (let i = 0; i < 2; i ++) { 213 | const arrow = this.arrows[i]; 214 | if (arrow.conn) json.out[i] = arrow.conn.toView.name; 215 | } 216 | 217 | if (this.model) { 218 | json.model = this.model.jsonify(); 219 | } 220 | 221 | return json; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /docs/examples/buffet-restaurant.rst: -------------------------------------------------------------------------------- 1 | .. _example-buffet-restaurant: 2 | 3 | ==================== 4 | Buffet Restaurant 5 | ==================== 6 | 7 | The System 8 | ============= 9 | 10 | In this example, we will model as buffet-style restaurant. Customer arrive at restaurant, pick up food from buffet area (we will will assume only one food item: salad), reach cashier for payment and leave the restaurant. The salad buffet is a finite resource, and is replenished every few minutes by the chef. In this restaurant the customers may have to wait at two places: 11 | 12 | * At the buffet. If the salad is all gone, customers have to wait until the chef brings in more. 13 | * At the cashier. If there are other customers ahead in line. 14 | 15 | The customizable parameters for this restaurant model are: 16 | 17 | 1. Salad preparation time. Time taken by chef to prepare the next round of fresh salad. We will assume this is a constant time interval. 18 | 2. Customer arrival rate. We will assume that the arrivals is a Poisson process. 19 | 3. Time taken by cashier per customer. We will assume this to be exponentially distributed. 20 | 4. The capacity of salad buffet. This is a constant and will not change over the course of simulation. 21 | 22 | **Simulation Objectives**. We would like to find out: 23 | 24 | * The average time spent by customers at the restaurant? 25 | * The average number of customers inside the restaurant? 26 | 27 | Modeling with SIM.JS 28 | ====================== 29 | 30 | We observe first that the system has two resources: 31 | 32 | 1. The salad buffet. We will model this as :ref:`resources-buffer`. 33 | 2. The cashier. We will model her as :ref:`resources-facility`. 34 | 35 | There will be two entities in the system: the chef and the customers. As in the earlier example, we will model customers as one entity object. 36 | 37 | We will use :ref:`statistics-population` to record the arrival and departure of customers. 38 | 39 | The following code lists all the global variables we will need in our simulation: 40 | 41 | .. code-block:: js 42 | 43 | var sim = new Sim.Sim(); 44 | var stats = new Sim.Population(); 45 | var cashier = new Sim.Facility('Cashier'); 46 | var buffet = new Sim.Buffer('Buffet', BuffetCapacity); 47 | var random = new Random(Seed); 48 | 49 | Lets start with the Entity Prototype for Chef first. The chef replenishes the salad buffer every *PreparationTime* interval. The code is very simple: 50 | 51 | .. code-block:: js 52 | 53 | class Chef extends Sim.Entity { 54 | start() { 55 | this.putBuffer(buffet, BuffetCapacity - buffet.current()); 56 | this.setTimer(PreparationTime).done(this.start); 57 | } 58 | }; 59 | 60 | Note here that the chef fills only the empty portion in the buffet. 61 | 62 | Next, let us look at the Customer entity. This entity prototype will generate request for all customers, where the time interval between customer arrivals is exponentially distributed. We will first look at the start function, which is somewhat similar to the start function of Chef. The customer will order (this.order(), which we will see next), and the function is called again after an exponentially distributed random delay: 63 | 64 | .. code-block:: js 65 | 66 | class Customer extends Sim.Entity { 67 | start() { 68 | this.order(); 69 | 70 | var nextCustomerAt = random.exponential (1.0 / MeanArrival); 71 | this.setTimer(nextCustomerAt).done(this.start); 72 | }, 73 | ... 74 | 75 | The :func:`Customer.order` function models the actions of customers inside the restaurant. First we will log the arrival of customer (line 3) and record in the statistics object (line 6). The customer then request to *get* one unit from the buffer (line 8) and will execute the anonymous function (argument to :func:`done` function) when the request is satisfied. The request may be satisfied immediately, if the buffer not empty, or wait otherwise. In the callback function, we log again that the customer has cleared the buffer stage and will now proceed to the cashier (line 10). The service time at cashier is also exponential distributed (line 13), and we use the :func:`this.useFacility` function to request service from the cashier (line 14). The callback function here will log that the customer will not leave the restaurant (line 16) and we also record this time in the statistics (line 20). Note also that we are using the :func:`Request.setData` function to remember the arrival time (which is read later on from :attr:`this.callbackData` attribute). 76 | 77 | .. code-block:: js 78 | :linenos: 79 | 80 | order() { 81 | // Logging 82 | sim.log("Customer ENTER at " + this.time()); 83 | 84 | // statistics 85 | stats.enter(this.time()); 86 | 87 | this.getBuffer(buffet, 1).done(() => { 88 | // Logging 89 | sim.log("Customer at CASHIER " + this.time() 90 | + " (entered at " + this.callbackData + ")"); 91 | 92 | var serviceTime = random.exponential(1.0 / CashierTime); 93 | this.useFacility(cashier, serviceTime).done(() => { 94 | // Logging 95 | sim.log("Customer LEAVE at " + this.time() 96 | + " (entered at " + this.callbackData + ")"); 97 | 98 | // Statistics 99 | stats.leave(this.callbackData, this.time()); 100 | }).setData(this.callbackData); 101 | }).setData(this.time()); 102 | } 103 | 104 | Finally, we create entities (lines 1 and 2), optionally set a logger function (lines 5-7), start the simulation (line 9) and report back the statistics (line 11). 105 | 106 | .. code-block:: js 107 | :linenos: 108 | 109 | sim.addEntity(Customer); 110 | sim.addEntity(Chef); 111 | 112 | 113 | sim.setLogger((msg) => { 114 | document.write(msg); 115 | }); 116 | 117 | sim.simulate(Simtime); 118 | 119 | return [stats.durationSeries.average(), 120 | stats.durationSeries.deviation(), 121 | stats.sizeSeries.average(), 122 | stats.sizeSeries.deviation()]; 123 | 124 | `View the complete source code `_. 125 | 126 | Running Simulation 127 | ====================== 128 | This javascript code can be executed where ever javascript can run. This includes: 129 | 130 | * As a script in HTML page on a web browser. 131 | * Via Web browser JavaScript debuggers such as Mozilla Firebug, Safari's Developer tools etc. 132 | * With `Rhino `_. 133 | * With ``jrunscript``. 134 | * and so on... 135 | 136 | We will run our model as a web page on a web browser. For this we have created the following web page: 137 | 138 | .. code-block:: js 139 | 140 | 141 | 142 | Tutorial: Customers at a Buffet Restaurant 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Buffer Restaurant in Action 151 | ============================= 152 | 153 | You can `play with this simulation model `_. Try out different values of input parameters and compare the output statistics of model. -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "mocha": true, 7 | "browser": true 8 | }, 9 | "ecmaFeatures": { 10 | "arrowFunctions": true, 11 | "blockBindings": true, 12 | "classes": true, 13 | "defaultParams": true, 14 | "destructuring": true, 15 | "forOf": true, 16 | "generators": false, 17 | "modules": true, 18 | "objectLiteralComputedProperties": true, 19 | "objectLiteralDuplicateProperties": false, 20 | "objectLiteralShorthandMethods": true, 21 | "objectLiteralShorthandProperties": true, 22 | "spread": true, 23 | "superInFunctions": true, 24 | "templateStrings": true, 25 | "jsx": true 26 | }, 27 | "rules": { 28 | // possible errors 29 | "comma-dangle": [2, "never"], 30 | "no-cond-assign": [2, "always"], 31 | "no-console": [2], 32 | "no-constant-condition": [2], 33 | "no-control-regex": [2], 34 | "no-debugger": [2], 35 | "no-dupe-args": [2], 36 | "no-dupe-keys": [2], 37 | "no-duplicate-case": [2], 38 | "no-empty": [2], 39 | "no-empty-character-class": [2], 40 | "no-ex-assign": [2], 41 | "no-extra-boolean-cast": [2], 42 | "no-extra-parens": [0], 43 | "no-extra-semi": [2], 44 | "no-func-assign": [2], 45 | "no-inner-declarations": [2], 46 | "no-invalid-regexp": [2], 47 | "no-irregular-whitespace": [2], 48 | "no-negated-in-lhs": [2], 49 | "no-obj-calls": [2], 50 | "no-regex-spaces": [2], 51 | "no-sparse-arrays": [2], 52 | "no-unexpected-multiline": [2], 53 | "no-unreachable": [2], 54 | "use-isnan": [2], 55 | "valid-jsdoc": [2, { 56 | "requireReturn": false, 57 | "requireParamDescription": false, 58 | "requireReturnDescription": false 59 | }], 60 | "valid-typeof": [2], 61 | 62 | // best practices 63 | "accessor-pairs": [2], 64 | "array-callback-return": [2], 65 | "block-scoped-var": [2], 66 | "complexity": [1, 8], 67 | "consistent-return": [0], 68 | "curly": [2, "multi-line"], 69 | "default-case": [2], 70 | "dot-location": [2, "property"], 71 | "dot-notation": [2], 72 | "eqeqeq": [2], 73 | "guard-for-in": [0], 74 | "no-alert": [2], 75 | "no-caller": [2], 76 | "no-case-declarations": [2], 77 | "no-div-regex": [2], 78 | "no-else-return": [2], 79 | "no-empty-function": [2], 80 | "no-empty-pattern": [2], 81 | "no-eq-null": [2], 82 | "no-eval": [2], 83 | "no-extend-native": [2], 84 | "no-extra-bind": [2], 85 | "no-extra-label": [2], 86 | "no-fallthrough": [2], 87 | "no-floating-decimal": [2], 88 | "no-implicit-coercion": [0], 89 | "no-implicit-globals": [2], 90 | "no-implied-eval": [2], 91 | "no-invalid-this": [2], 92 | "no-iterator": [2], 93 | "no-labels": [2], 94 | "no-lone-blocks": [2], 95 | "no-loop-func": [2], 96 | "no-multi-spaces": [2], 97 | "no-multi-str": [2], 98 | "no-native-reassign": [2], 99 | "no-new-func": [2], 100 | "no-new-wrappers": [2], 101 | "no-octal": [2], 102 | "no-octal-escape": [2], 103 | "no-param-reassign": [0], 104 | "no-process-env": [0], 105 | "no-proto": [2], 106 | "no-redeclare": [2], 107 | "no-return-assign": [2], 108 | "no-script-url": [2], 109 | "no-self-assign": [2], 110 | "no-self-compare": [2], 111 | "no-sequences": [2], 112 | "no-throw-literal": [2], 113 | "no-unmodified-loop-condition": [2], 114 | "no-unused-expressions": [2, { "allowShortCircuit": true }], 115 | "no-unused-labels": [2], 116 | "no-useless-call": [2], 117 | "no-useless-concat": [2], 118 | "no-void": [2], 119 | "no-warning-comments": [0], 120 | "no-with": [2], 121 | "radix": [2], 122 | "vars-on-top": [0], 123 | "wrap-iife": [2, "inside"], 124 | "yoda": [2, "never"], 125 | 126 | // strict 127 | "strict": [0], 128 | 129 | // variables 130 | "init-declarations": [0], 131 | "no-catch-shadow": [2], 132 | "no-delete-var": [2], 133 | "no-label-var": [2], 134 | "no-shadow": [2], 135 | "no-shadow-restricted-names": [2], 136 | "no-undef": [2], 137 | "no-undef-init": [2], 138 | "no-undefined": [2], 139 | "no-unused-vars": [2], 140 | "no-use-before-define": [2], 141 | 142 | // node.js 143 | "callback-return": [0], 144 | "global-require": [0], 145 | "handle-callback-err": [2], 146 | "no-mixed-requires": [2], 147 | "no-new-require": [2], 148 | "no-path-concat": [2], 149 | "no-process-exit": [2], 150 | "no-restricted-imports": [0], 151 | "no-restricted-modules": [0], 152 | "no-sync": [0], 153 | 154 | // stylistic 155 | "array-bracket-spacing": [2, "never"], 156 | "block-spacing": [2], 157 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 158 | "camelcase": [2, { "properties": "never" }], 159 | "comma-spacing": [2, { 160 | "before": false, 161 | "after": true 162 | }], 163 | "comma-style": [2, "last"], 164 | "computed-property-spacing": [2, "never"], 165 | "consistent-this": [2, "_this"], 166 | "eol-last": [2], 167 | "func-names": [0], 168 | "func-style": [0], 169 | "indent": [2, 2], 170 | "jsx-quotes": [2, "prefer-single"], 171 | "key-spacing": [2, { 172 | "beforeColon": false, 173 | "afterColon": true 174 | }], 175 | "keyword-spacing": [2], 176 | "linebreak-style": [2, "unix"], 177 | "lines-around-comment": [2, { 178 | "beforeBlockComment": true 179 | }], 180 | "max-depth": [2, 4], 181 | "max-len": [2, 120, 4], 182 | "max-nested-callbacks": [2, 4], 183 | "max-params": [0], 184 | "max-statements": [0], 185 | "new-cap": [2], 186 | "new-parens": [2], 187 | "newline-after-var": [2], 188 | "newline-per-chained-call": [0], 189 | "no-array-constructor": [2], 190 | "no-bitwise": [0], 191 | "no-continue": [0], 192 | "no-inline-comments": [0], 193 | "no-lonely-if": [2], 194 | "no-mixed-spaces-and-tabs": [2], 195 | "no-multiple-empty-lines": [2], 196 | "no-negated-condition": [0], 197 | "no-nested-ternary": [2], 198 | "no-new-object": [2], 199 | "no-plusplus": [0], 200 | "no-spaced-func": [2], 201 | "no-ternary": [0], 202 | "no-trailing-spaces": [2], 203 | "no-underscore-dangle": [0], 204 | "no-unneeded-ternary": [2], 205 | "no-whitespace-before-property": [2], 206 | "object-curly-spacing": [2, "always"], 207 | "one-var": [2, { 208 | "uninitialized": "always", 209 | "initialized": "never" 210 | }], 211 | "one-var-declaration-per-line": [0], 212 | "operator-assignment": [0], 213 | "operator-linebreak": [2, "before"], 214 | "padded-blocks": [0], 215 | "quote-props": [2, "as-needed"], 216 | "quotes": [2, "single"], 217 | "require-jsdoc": [0], 218 | "semi": [2, "always"], 219 | "semi-spacing": [2, { 220 | "before": false, 221 | "after": true 222 | }], 223 | "sort-imports": [0], 224 | "sort-vars": [0], 225 | "space-before-blocks": [2, "always"], 226 | "space-before-function-paren": [2, { 227 | "anonymous": "always", 228 | "named": "never" 229 | }], 230 | "space-in-parens": [2, "never"], 231 | "space-infix-ops": [2], 232 | "space-unary-ops": [2], 233 | "spaced-comment": [2, "always"], 234 | "wrap-regex": [2], 235 | 236 | // ecmascript6 237 | "arrow-body-style": [0], 238 | "arrow-parens": [2], 239 | "arrow-spacing": [2, { "before": true, "after": true }], 240 | "constructor-super": [2], 241 | "generator-star-spacing": [2, "after"], 242 | "no-class-assign": [2], 243 | "no-confusing-arrow": [0], 244 | "no-const-assign": [2], 245 | "no-dupe-class-members": [2], 246 | "no-new-symbol": [2], 247 | "no-this-before-super": [2], 248 | "no-useless-constructor": [2], 249 | "no-var": [2], 250 | "object-shorthand": [0], 251 | "prefer-arrow-callback": [0], 252 | "prefer-const": [2], 253 | "prefer-reflect": [0], 254 | "prefer-rest-params": [0], 255 | "prefer-spread": [0], 256 | "prefer-template": [2], 257 | "require-yield": [2], 258 | "template-curly-spacing": [2, "never"] 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # SIM.JS documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Jun 21 14:41:02 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | import cloud_sptheme as csp 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ----------------------------------------------------- 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be extensions 29 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 30 | extensions = [] 31 | #extensions = ['sphinx.ext.pngmath'] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix of source filenames. 37 | source_suffix = '.rst' 38 | 39 | # The encoding of source files. 40 | #source_encoding = 'utf-8-sig' 41 | 42 | # The master toctree document. 43 | master_doc = 'index' 44 | 45 | # General information about the project. 46 | project = u'SIM.JS' 47 | copyright = u'2011, Maneesh Varshney' 48 | 49 | # The version info for the project you're documenting, acts as replacement for 50 | # |version| and |release|, also used in various other places throughout the 51 | # built documents. 52 | # 53 | # The short X.Y version. 54 | version = '0.25' 55 | # The full version, including alpha/beta/rc tags. 56 | release = '0.25' 57 | 58 | # The language for content autogenerated by Sphinx. Refer to documentation 59 | # for a list of supported languages. 60 | #language = None 61 | 62 | # There are two options for replacing |today|: either, you set today to some 63 | # non-false value, then it is used: 64 | #today = '' 65 | # Else, today_fmt is used as the format for a strftime call. 66 | #today_fmt = '%B %d, %Y' 67 | 68 | # List of patterns, relative to source directory, that match files and 69 | # directories to ignore when looking for source files. 70 | exclude_patterns = ['_build'] 71 | 72 | # The reST default role (used for this markup: `text`) to use for all documents. 73 | #default_role = None 74 | 75 | # If true, '()' will be appended to :func: etc. cross-reference text. 76 | #add_function_parentheses = True 77 | 78 | # If true, the current module name will be prepended to all description 79 | # unit titles (such as .. function::). 80 | #add_module_names = True 81 | 82 | # If true, sectionauthor and moduleauthor directives will be shown in the 83 | # output. They are ignored by default. 84 | #show_authors = False 85 | 86 | # The name of the Pygments (syntax highlighting) style to use. 87 | pygments_style = 'sphinx' 88 | 89 | # A list of ignored prefixes for module index sorting. 90 | #modindex_common_prefix = [] 91 | 92 | 93 | # -- Options for HTML output --------------------------------------------------- 94 | 95 | #html_theme = "cloud" 96 | #html_theme_options = { "roottarget": "index" } 97 | #html_theme_path = [csp.get_theme_dir()] 98 | 99 | html_theme_options = {"externalrefs": "true"} 100 | html_sidebars = { 101 | '**': ['localtoc.html', 'relations.html', 'searchbox.html'], 102 | 'index': [], 103 | } 104 | html_use_index = False 105 | 106 | rst_prolog = """ 107 | .. meta:: 108 | :description: General Purpose Discrete Event Simulation Library in JavaScript 109 | :keywords: discrete event simulation, simulation, javascript, web simulation, javascript simulator 110 | 111 | .. title:: SIM.JS | Discrete Event Simulation in JavaScript 112 | """ 113 | 114 | # The theme to use for HTML and HTML Help pages. See the documentation for 115 | # a list of builtin themes. 116 | #html_theme = 'default' 117 | 118 | # Theme options are theme-specific and customize the look and feel of a theme 119 | # further. For a list of options available for each theme, see the 120 | # documentation. 121 | #html_theme_options = {"externalrefs": "true"} 122 | 123 | # Add any paths that contain custom themes here, relative to this directory. 124 | #html_theme_path = [] 125 | 126 | # The name for this set of Sphinx documents. If None, it defaults to 127 | # " v documentation". 128 | #html_title = None 129 | html_title = "" 130 | 131 | # A shorter title for the navigation bar. Default is the same as html_title. 132 | #html_short_title = None 133 | html_short_title = "Home" 134 | 135 | # The name of an image file (relative to this directory) to place at the top 136 | # of the sidebar. 137 | #html_logo = None 138 | 139 | # The name of an image file (within the static path) to use as favicon of the 140 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 141 | # pixels large. 142 | #html_favicon = None 143 | 144 | # Add any paths that contain custom static files (such as style sheets) here, 145 | # relative to this directory. They are copied after the builtin static files, 146 | # so a file named "default.css" will overwrite the builtin "default.css". 147 | html_static_path = ['_static'] 148 | 149 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 150 | # using the given strftime format. 151 | #html_last_updated_fmt = '%b %d, %Y' 152 | 153 | # If true, SmartyPants will be used to convert quotes and dashes to 154 | # typographically correct entities. 155 | #html_use_smartypants = True 156 | 157 | # Custom sidebar templates, maps document names to template names. 158 | #html_sidebars = {} 159 | 160 | # Additional templates that should be rendered to pages, maps page names to 161 | # template names. 162 | #html_additional_pages = {} 163 | 164 | # If false, no module index is generated. 165 | #html_domain_indices = True 166 | 167 | # If false, no index is generated. 168 | #html_use_index = True 169 | 170 | # If true, the index is split into individual pages for each letter. 171 | #html_split_index = False 172 | 173 | # If true, links to the reST sources are added to the pages. 174 | #html_show_sourcelink = True 175 | html_show_sourcelink = False 176 | 177 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 178 | #html_show_sphinx = True 179 | 180 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 181 | #html_show_copyright = True 182 | 183 | # If true, an OpenSearch description file will be output, and all pages will 184 | # contain a tag referring to it. The value of this option must be the 185 | # base URL from which the finished HTML is served. 186 | #html_use_opensearch = '' 187 | 188 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 189 | #html_file_suffix = None 190 | 191 | # Output file base name for HTML help builder. 192 | htmlhelp_basename = 'SIMJSdoc' 193 | 194 | 195 | # -- Options for LaTeX output -------------------------------------------------- 196 | 197 | # The paper size ('letter' or 'a4'). 198 | #latex_paper_size = 'letter' 199 | 200 | # The font size ('10pt', '11pt' or '12pt'). 201 | #latex_font_size = '10pt' 202 | 203 | # Grouping the document tree into LaTeX files. List of tuples 204 | # (source start file, target name, title, author, documentclass [howto/manual]). 205 | latex_documents = [ 206 | ('index', 'SIMJS.tex', u'SIM.JS Documentation', 207 | u'Maneesh Varshney', 'manual'), 208 | ] 209 | 210 | # The name of an image file (relative to this directory) to place at the top of 211 | # the title page. 212 | #latex_logo = None 213 | 214 | # For "manual" documents, if this is true, then toplevel headings are parts, 215 | # not chapters. 216 | #latex_use_parts = False 217 | 218 | # If true, show page references after internal links. 219 | #latex_show_pagerefs = False 220 | 221 | # If true, show URL addresses after external links. 222 | #latex_show_urls = False 223 | 224 | # Additional stuff for the LaTeX preamble. 225 | #latex_preamble = '' 226 | 227 | # Documents to append as an appendix to all manuals. 228 | #latex_appendices = [] 229 | 230 | # If false, no module index is generated. 231 | #latex_domain_indices = True 232 | 233 | 234 | # -- Options for manual page output -------------------------------------------- 235 | 236 | # One entry per manual page. List of tuples 237 | # (source start file, name, description, authors, manual section). 238 | man_pages = [ 239 | ('index', 'simjs', u'SIM.JS Documentation', 240 | [u'Maneesh Varshney'], 1) 241 | ] 242 | -------------------------------------------------------------------------------- /docs/events.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Timers, Events and Messages 3 | ============================= 4 | 5 | .. default-domain:: js 6 | 7 | This section covers the various mechanisms by which entities can plan actions for themselves at different time instances, as well as coordinate with other entities. 8 | 9 | :ref:`events-timers` allow entities to execute functions after a delay. 10 | 11 | :ref:`events-events` are objects that can help synchronize actions among multiple entities. 12 | 13 | :ref:`events-messages` are objects that entities can send to each other. 14 | 15 | .. _events-timers: 16 | 17 | Timers 18 | ======== 19 | 20 | Timers are mechanisms for *delayed executions* of functions. An entity can specify a JavaScript function and a duration (in simulated time) after which this function must be executed. 21 | 22 | The :func:`setTimer` function in the Entity Prototype provides an API to execute function after a timeout. 23 | 24 | Some examples: 25 | 26 | .. code-block:: js 27 | :linenos: 28 | 29 | var Entity = { 30 | start: function () { 31 | // Execute an anonymous function after 10 seconds 32 | this.setTimer(10).done(function () { 33 | document.write("The simulation time must be 10 now."); 34 | }); 35 | 36 | // Execute a local function after 20 seconds 37 | this.setTimer(20).done(this.specialHandler); 38 | }, 39 | specialHandler: function () { 40 | document.write("Special Handler: executed every 20 seconds"); 41 | 42 | // Recursively calling a function (will not overflow on stack) 43 | this.setTimer(20).done(this.specialHandler); 44 | }, 45 | onTimeout: function () { 46 | document.write("Default handler: time now must be 30"); 47 | } 48 | } 49 | 50 | The :func:`setTimer` function takes one argument -- the duration after which the timer must expire and some action must be taken. 51 | 52 | The setTimer function returns a ``Request`` object. Refer to :ref:`request-main` documentation on how to further modify the setTimer request. 53 | 54 | If the timeout value is 0, the action function will be called *after* the current function has finished executing. This way, there will be recursive function calls and no chance of stack overflow in case of recursive delayed functions. 55 | 56 | .. code-block:: js 57 | 58 | start: function () { 59 | // Action functions are called *after* the current function has returned 60 | this.setTimer(0).done(function () { 61 | document.write("I will print as second line."); 62 | }); 63 | 64 | document.write("I will print as first line."); 65 | } 66 | 67 | .. note:: 68 | The action function is executed in context of the entity. That is, it will be equivalent to *actionFunction*.\ ``call``\ (*entity*). 69 | 70 | .. _events-events: 71 | 72 | Events 73 | ========= 74 | Events are external objects that start out in *passive* state, and at some point in time will be *activated* or *fired*. Entities 'attach' themselves to events and wait until the event is fired. There are two ways in which entities can attach with events: 75 | 76 | 1. *Wait for Event*. All entities that wait for events will be notified when the event fires. 77 | 2. *Queue for Event*. Entities join a queue, and only the front entity will be notified when the event fires. Once notified, the entity is removed from the queue, and the next in queue entity will be notified when the event fires the second time. 78 | 79 | The events can fire in two ways: 80 | 81 | 1. *Flash fire*. The event fires `for an instant` (more technically: for zero simulated time duration). When the event fires, it notifies all entities that were waiting and one entity in the queue, and then reverts back to passive state. Any request (wait or queue) after this will have to wait until the next time the event is fired. 82 | 2. *Sustained fire*. The event fires and remains in activated state for an indefinite period until explicitly reset to passive state. When the event fires, it notifies all entities that were waiting and *all* entities in the queue. Any request (wait or queue) coming after the event is fired, and before the event is reset, will be serviced immediately. 83 | 84 | An example of flash fire would be clock changing the date 'at the stroke of midnight'. Only the entities that were already waiting before the midnight will be notified. Any entity that requested the event after midnight will have to wait until the next fire event (midnight next night). The event itself can be considered to have happened in zero time. 85 | 86 | An example of sustained fire would be traffic lights. When the traffic light is red (passive state), the entities (cars) will wait. Once the lights are green (fired) they remain in fired state until explicitly reset to passive state. Any request arriving during the period when the event is activated will be services immediately. 87 | 88 | An event object is created as: 89 | 90 | .. code-block:: js 91 | 92 | var event = new Sim.Event(name) 93 | 94 | 95 | .. js:class:: Sim.Event([name]) 96 | 97 | ``name`` is an optional parameter used for identifying the event in statistics reporting and tracing. 98 | 99 | The event will start out in passive state. 100 | 101 | The events are fired by :func:`~Sim.Event.fire` function and reset to passive state by :func:`~Sim.Event.clear` function. 102 | 103 | .. js:function:: Sim.Event.fire([keepFired]) 104 | 105 | ``keepFired`` (boolean) is an optional argument to indicate that the event must remain in fired state (the sustained fire mode). The default value is false. 106 | 107 | .. js:function:: Sim.Event.clear() 108 | 109 | Reset the event to passive state. This function has no effect if the event is already in passive state. 110 | 111 | The entities can wait or queue on events by :func:`waitEvent` and :func:`queueEvent`, respectively, as defined in the :ref:`entity-prototype` section. 112 | 113 | An example demonstrating the behavior of waitEvent and queueEvent: 114 | 115 | .. code-block:: js 116 | 117 | var barrier = new Sim.Event('Barrier'); 118 | var funnel = new Sim.Event('Funnel'); 119 | class Entity extends Sim.Entity { 120 | start() { 121 | this.waitEvent(barrier).done(() => { 122 | document.write("This line is printed by all entities."); 123 | }); 124 | 125 | this.queueEvent(funnel).done(() => { 126 | document.write("This line is printed by one entity only"); 127 | }); 128 | 129 | if (this.master) { 130 | this.setTimer(10) 131 | .done(barrier.fire, barrier) 132 | .done(funnel.fire, funnel); 133 | } 134 | } 135 | } 136 | 137 | var sim = new Sim.Sim(); 138 | var entities = []; 139 | for (var i = 0; i < NUM_ENTITIES; i++) { 140 | entities.push(sim.addEntity(Entity)); 141 | } 142 | entities[0].master = true; 143 | sim.simulate(SIMTIME); 144 | 145 | An example demonstrating the behavior of flash and sustained event fire: 146 | 147 | .. code-block:: js 148 | 149 | var sustained = new Sim.Event('Sustained Event'); 150 | var flash = new Sim.Event('Flash Event'); 151 | class Entity extends Sim.Entity { 152 | start() { 153 | // one second before fire 154 | this.setTimer(9).done(() => { 155 | this.waitEvent(sustained).done(() => document.write("Notified at time 10.")); 156 | 157 | this.waitEvent(flash).done(() => document.write("Notified at time 10.")); 158 | }); 159 | 160 | // one second after fire 161 | this.setTimer(11).done(() => { 162 | this.waitEvent(sustained).done(() => document.write("Notified at time 11.")); 163 | 164 | this.waitEvent(flash).done(() => document.write("Will not be notified :(")); 165 | }); 166 | 167 | // Trigger both events at time = 10 168 | this.setTimer(10) 169 | .done(() => sustained.fire(true)); 170 | .done(flash.fire, flash); 171 | } 172 | } 173 | 174 | 175 | 176 | .. _events-messages: 177 | 178 | Messages 179 | ========== 180 | 181 | Messages are objects that entities can send to each other. The messages can be any JavaScript type -- numbers, string, functions, arrays, objects etc. 182 | 183 | Entities send messages using the :func:`send` ``Extended Entity Prototype`` function (see also :ref:`entity-prototype`). The signature of this function is: 184 | 185 | .. js:function:: send(message, delay[, entities]) 186 | 187 | Sends the ``message`` to other entities after a ``delay``. ``entities`` can be: 188 | 189 | * omitted or null. The message is sent to *all* entities (excluding self). 190 | * Entity object: The message is sent to the entity object. 191 | * Array of entity objects: The message is sent to all entities in the array. 192 | 193 | This function does not return anything. 194 | 195 | As an example, consider a ping-pong game played by two players where they send a string back and forth to each other. Before resending the string, each player appends his/her name to the string. We will model the two players as entities and the string as a message. 196 | 197 | .. code-block:: js 198 | 199 | class Player extends Sim.Entity{ 200 | start() { 201 | if (this.firstServer) { 202 | // Send the string to other player with delay = 0. 203 | this.send("INITIAL", 0, this.opponent); 204 | } 205 | }, 206 | onMessage(sender, message) { 207 | // Receive message, add own name and send back 208 | var newMessage = message + this.name; 209 | this.send(newMessage, HOLDTIME, sender); 210 | } 211 | }; 212 | 213 | var sim = new Sim.Sim(); 214 | var jack = sim.addEntity(Player); 215 | var jill = sim.addEntity(Player); 216 | 217 | jack.name = "Jack"; 218 | jill.name = "Jill"; 219 | 220 | jack.firstServer = true; 221 | jack.opponent = jill; 222 | 223 | sim.simulate(SIMTIME); -------------------------------------------------------------------------------- /docs/entities.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Simulator and Entity 3 | ============================== 4 | 5 | Simulator and Entities are the core of Discrete Event Simulation. Entities are the *actors* in the system -- they request resources, communicate with other entities and so on. Simulator is the backbone that provides the necessary framework, including the notion of *simulated clock*. The basics of discrete event simulation are discussed in :ref:`introduction-simjs`. 6 | 7 | .. _entity-simulator: 8 | 9 | Simulator 10 | ========== 11 | 12 | The SIM.JS library provides a global class variable :class:`Sim`, an instance of which defines one simulation process. 13 | 14 | The steps in creating a simulation run are quite simple: 15 | 16 | 1. Create an object of :class:`Sim` class. 17 | 2. Create entity objects via :func:`~Sim.Sim.addEntity` function call. This function takes a ``Sim.Entity class/prototype`` (see :ref:`entity-entity` below), uses it to create a Sim.Entity, and attaches it to the simulation. 18 | 3. Start the simulation by calling :func:`~Sim.Sim.simulate`. 19 | 20 | API Reference 21 | --------------- 22 | 23 | .. js:class:: Sim.Sim() 24 | 25 | Creates an instance of discrete event simulator. 26 | 27 | .. js:function:: Sim.Sim.addEntity(entityPrototype) 28 | 29 | Adds an entity in the simulation environment. The entity object inherits from the ``entityPrototype`` object. The simulator also adds several other API function in the prototype. See :ref:`entity-entity` for further details. 30 | 31 | .. js:function:: Sim.Sim.simulate(duration) 32 | 33 | Starts the simulation. ``duration`` is the time until which the simulation runs. 34 | 35 | .. js:function:: Sim.Sim.setLogger(loggerFn) 36 | 37 | Assigns a function that will be called by the simulator when logging messages. 38 | 39 | .. js:function:: Sim.Sim.log(message) 40 | 41 | Logs a message using a logger function defined in :func:`~Sim.Sim.setLogger`. 42 | 43 | .. _entity-entity: 44 | 45 | Sim.Entity 46 | ========= 47 | 48 | Entities are the actors in the simulation. SIM.JS uses the JavaScript's *prototype inheritance* concept to differentiate between the behavior representation (similar to *class* in C++ or Java) and the runtime representation (similar to *objects* in C++ or Java). 49 | 50 | As programmers, we create a ``Sim.Entity Prototype`` JavaScript object. This is like any other JavaScript object, except for the following constraints: 51 | 52 | * The object must have a ``start()`` function. This function is called by the simulator when the simulation starts. 53 | * There are reserved function and attribute names (see :ref:`entity-prototype` for a complete list) that must not be used in the object. 54 | 55 | An example of a very simple ``Entity Prototype`` object could be: 56 | 57 | .. code-block:: js 58 | 59 | class EntityPrototype extends Sim.Entity { 60 | start() { 61 | // This function is necessary! 62 | // This function is called by simulator when simulation starts. 63 | document.write("the simulation has started!"). 64 | } 65 | }; 66 | 67 | Think of ``Entity Prototype`` objects as *classes* in languages like C++ or Java. This class will be used to create *objects*, which are the runtime representation of entities. We call these runtime objects as ``Entity Objects``, which are *instances* of ``Entity Prototypes``. 68 | 69 | The ``Entity Objects`` are created by the :func:`Sim.Sim.addEntity` function: 70 | 71 | .. code-block:: js 72 | 73 | // Create entity object from the entity prototype object 74 | var entityObject = sim.addEntity(EntityPrototype); 75 | 76 | // More than one entity objects can be created by same entity prototype object 77 | var anotherEntityObject = sim.addEntity(EntityPrototype); 78 | 79 | The :func:`Sim.Sim.addEntity` function performs three actions: 80 | 81 | 1. *Extends* the ``Entity Prototype`` object by adding new functions and attributes to the original prototype object. :ref:`entity-prototype` lists these functions and attributes. 82 | 2. *Creates* a new object which inherits the ``Extended Entity Prototype``. 83 | 3. Assigns a unique integer value to the :attr:`id` attribute of the object. 84 | 85 | The entire process is illustrated in the diagram below: 86 | 87 | .. image:: images/entity-prototype.png 88 | 89 | The input to the :func:`Sim.Sim.addEntity` function is ``Entity Prototype`` object. This is an object that we have written to model the components of system for our discrete simulation problem. 90 | 91 | The simulator adds other useful functions and attributes (see below for complete list) to the ``Entity Prototype`` object. We call this object as ``Extended Entity Prototype``. 92 | 93 | The simulator then creates an object (the ``Entity Object``) which inherits from the ``Extended Entity Prototype`` object (for example, via the *Object.create* function on platforms where it is supported). 94 | 95 | This new ``Entity Object`` is returned to the user program. 96 | 97 | Entity Prototype 98 | ------------------ 99 | 100 | As noted earlier, the ``Entity Prototype`` object must define :func:`start` function. This function is called by the simulator when the simulation starts. It is this function where the entities initialize their state and schedule future events in the simulator. 101 | 102 | The prototype object may optionally have a :func:`finalize` function. This function is called when the simulation terminates. 103 | 104 | The prototype object may optionally have a :func:`onMessage` function. This function is called when some other entity has sent a :ref:`Message `. 105 | 106 | 107 | .. _entity-prototype: 108 | 109 | Extended Entity Prototype API 110 | --------------------------------- 111 | 112 | The SIM.JS library adds following functions and attributes to the ``Entity Prototype`` object. 113 | 114 | .. note:: The function and attribute names listed below should be treated as "reserved keywords" when writing the ``Entity Prototype`` code. 115 | 116 | These functions and attributes are added when :func:`Sim.Sim.addEntity` function is called. For example, 117 | 118 | .. code-block:: js 119 | 120 | class EntityPrototype extends Sim.Entity { 121 | start() { 122 | var now = this.time(); // Where did we get this time() function from? See below.. 123 | document.write("The time now is " + now); 124 | } 125 | }; 126 | 127 | assert(EntityPrototype.time === undefined); // the object does not have a time method (yet)! 128 | 129 | var obj = sim.addEntity(EntityPrototype); // create an object from prototype 130 | 131 | 132 | // Since obj inherits from the extended Sim.Entity prototype, it will have methods 133 | // defined in EntityPrototype as well as those Sim.Entity defines. 134 | assert(obj.start instanceof Function); 135 | assert(obj.time instanceof Function); 136 | 137 | .. js:attribute:: id 138 | 139 | The unique id of the entity. The ``id`` will be unique for entity objects even if they are derived from the same prototype. 140 | 141 | .. js:function:: time() 142 | 143 | Returns the current simulation time. 144 | 145 | .. js:function:: setTimer(delay) 146 | 147 | Sets an internal timer that expires after ``delay`` duration. This function returns a :ref:`Request ` object. 148 | 149 | .. seealso:: :ref:`events-timers`. 150 | 151 | .. js:function:: waitEvent(event) 152 | 153 | Waits on the ``event`` :ref:`Event `. This function returns a :ref:`Request ` object. 154 | 155 | The difference between :func:`waitEvent` and :func:`queueEvent` is that when the event triggers (or fires), *all* waiting entities are notified, and only one queued entity (the one at the head of the queue) is notified. 156 | 157 | .. seealso:: :ref:`events-events`. 158 | 159 | .. js:function:: queueEvent(event) 160 | 161 | Queue for the ``event`` :ref:`Event `. The function returns a :ref:`Request ` object. 162 | 163 | The difference between :func:`waitEvent` and :func:`queueEvent` is that when the event triggers (or fires), *all* waiting entities are notified, and only one (the one at the head of the queue) is notified. 164 | 165 | .. seealso:: :ref:`events-events`. 166 | 167 | .. js:function:: send(message, delay[, entities]) 168 | 169 | Sends the ``message`` to other entities after a ``delay``. ``entities`` can be: 170 | 171 | * omitted or null. The message is sent to *all* entities (excluding self). 172 | * Entity object: The message is send to the entity object. 173 | * Array of entity objects: The message is sent to all entities in array. 174 | 175 | This function does not return any value. 176 | 177 | .. seealso:: :ref:`events-messages`. 178 | 179 | .. js:function:: useFacility(facility, duration) 180 | 181 | Request to use the ``facility`` for ``duration`` duration. This function returns a :ref:`Request ` object. 182 | 183 | .. seealso:: :ref:`resources-facility`. 184 | 185 | .. js:function:: putBuffer(buffer, amount) 186 | 187 | Request to put ``amount`` quantity of tokens in the ``buffer``. This function returns a :ref:`Request ` object. 188 | 189 | .. seealso:: :ref:`resources-buffer`. 190 | 191 | .. js:function:: getBuffer(buffer, amount) 192 | 193 | Request to retrieve ``amount`` quantity of tokens from the ``buffer``. This function returns a :ref:`Request ` object. 194 | 195 | .. seealso:: :ref:`resources-buffer`. 196 | 197 | .. js:function:: putStore(store, object) 198 | 199 | Request to store ``object`` in the ``store``. ``object`` can be any JavaScript value (numbers, strings, functions, objects, arrays etc). This function returns a :ref:`Request ` object. 200 | 201 | .. seealso:: :ref:`resources-store`. 202 | 203 | .. js:function:: getStore(store[, filter]) 204 | 205 | Request to retrieve object from the ``store``. If the filter function is supplied then the first object (in FIFO order) that matches the filter is retrieved; otherwise the first object in FIFO order is retrieved. The retrieved object can be accessed via the :attr:`this.callbackMessage` attribute in the callback function. This returns a :ref:`Request ` object. 206 | 207 | .. seealso:: :ref:`resources-store`. --------------------------------------------------------------------------------