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 |
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`.
--------------------------------------------------------------------------------