1 | package desmoj.core.simulator; |
2 | |
3 | import desmoj.core.statistic.StatisticObject; |
4 | |
5 | /** |
6 | * Provides the typical statistics common to all ModelComponents based on |
7 | * Queues. It is set abstract to prevent users to use it straight without |
8 | * deriving a class first because it only provides the functionality for |
9 | * statistical data extraction, not the functionality for actually queueing |
10 | * Entities. The statistical values provided are the queue's length and its |
11 | * elements' waiting times with minimum, maximum, mean and standard deviation |
12 | * for each. This class should be used when any type of ModelComponent using |
13 | * Queues is created. In combination with class QueueList an automatic |
14 | * insert/remove mechanism including search functionality with condition |
15 | * checking can be set up within a few lines of code. It also provides full |
16 | * automatic statistical data about the Queue used. |
17 | * |
18 | * @see QueueList |
19 | * |
20 | * @version DESMO-J, Ver. 2.3.3 copyright (c) 2011 |
21 | * @author Tim Lechler |
22 | * @author modified by Soenke Claassen |
23 | * |
24 | * Licensed under the Apache License, Version 2.0 (the "License"); you |
25 | * may not use this file except in compliance with the License. You may |
26 | * obtain a copy of the License at |
27 | * http://www.apache.org/licenses/LICENSE-2.0 |
28 | * |
29 | * Unless required by applicable law or agreed to in writing, software |
30 | * distributed under the License is distributed on an "AS IS" BASIS, |
31 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
32 | * implied. See the License for the specific language governing |
33 | * permissions and limitations under the License. |
34 | * |
35 | */ |
36 | public abstract class QueueBased extends desmoj.core.simulator.Reportable { |
37 | |
38 | /** |
39 | * Represents the value returned if for a given statistics no valid value |
40 | * can be returned. |
41 | */ |
42 | public static final double UNDEFINED = -1; |
43 | |
44 | /** |
45 | * Displays the current number of objects waiting inside the queue. |
46 | */ |
47 | private int _currentLength; |
48 | |
49 | /** |
50 | * Flag for letting the underlying queue implementation ( |
51 | * <code>QueueList</code>) show own warnings (<code>true</code>) or |
52 | * suppressing them (<code>false</code>). Is <code>false</code> by default |
53 | * but can be set to <code>true</code> for debugging purposes |
54 | */ |
55 | private boolean _qImpWarnings; |
56 | |
57 | /** |
58 | * Displays the minimum number of objects that have been waiting inside the |
59 | * queue since the last reset. |
60 | */ |
61 | private int _minimumLength; |
62 | |
63 | /** |
64 | * Displays the maximum number of objects that have been waiting inside the |
65 | * queue since the last reset. |
66 | */ |
67 | private int _maximumLength; |
68 | |
69 | /** |
70 | * Displays the number of objects that have passed the queue without waiting |
71 | * time. Thus the name "zeros" for zero waiting time. Value is valid for the |
72 | * span of time since the last reset. |
73 | */ |
74 | private long _zeros; |
75 | |
76 | /** |
77 | * Displays the sum of the queue length weighted over the time each object |
78 | * spent waiting in the queue. Value is valid for the span of time since the |
79 | * last reset. |
80 | */ |
81 | private double _wSumLength; |
82 | |
83 | /** |
84 | * Displays the squares of the sums of the queue length weighted over the |
85 | * time each object spent waiting in the queue. Value is valid for the span |
86 | * of time since the last reset. |
87 | */ |
88 | private double _wSumSquareLength; |
89 | /** |
90 | * The point in simulation time the queue was last accessed. Value is valid |
91 | * for the span of time since the last reset. |
92 | */ |
93 | private TimeInstant _lastAcc; |
94 | |
95 | /** |
96 | * The point in simulation time the queue's minimum length was recorded. |
97 | * Value is valid for the span of time since the last reset. |
98 | */ |
99 | private TimeInstant _minimumLengthAt; |
100 | |
101 | /** |
102 | * The point in simulation time the queue's maximum length was recorded. |
103 | * Value is valid for the span of time since the last reset. |
104 | */ |
105 | private TimeInstant _maximumLengthAt; |
106 | |
107 | /** |
108 | * The maximum time an object inside the queue spent waiting. Value is valid |
109 | * for the span of time since the last reset. |
110 | */ |
111 | private TimeSpan _maximumWaitTime; |
112 | |
113 | /** |
114 | * The point in simulation time the maximum waiting time of an object inside |
115 | * the queue was recorded at. Value is valid for the span of time since the |
116 | * last reset. |
117 | */ |
118 | private TimeInstant _maximumWaitTimeAt; |
119 | |
120 | /** |
121 | * The sum of the waiting times spent by all objects that have passed |
122 | * through the queue. Value is valid for the span of time since the last |
123 | * reset. |
124 | */ |
125 | private TimeSpan _sumWaitTime; |
126 | |
127 | /** |
128 | * The square of the sums of the waiting times spent by all objects that |
129 | * have passed through the queue. Value is valid for the span of time since |
130 | * the last reset. |
131 | */ |
132 | private double _sumSquareWaitTime; |
133 | |
134 | /** |
135 | * Defining a constant for the FIFO (First In First Out) service discipline |
136 | * of the underlying queue: An Entity inserted into the queue is removed |
137 | * after all entities already enqueued with the same priority. |
138 | */ |
139 | public final static int FIFO = 0; |
140 | |
141 | /** |
142 | * Defining a constant for the LIFO (Last In First Out) service discipline |
143 | * of the underlying queue: An Entity inserted into the queue is removed |
144 | * before all entities already enqueued with the same priority. |
145 | */ |
146 | public final static int LIFO = 1; |
147 | |
148 | /** |
149 | * Defining a constant for the random service discipline |
150 | * of the underlying queue: An Entity inserted into the queue may be removed |
151 | * before or after any other Entity with the same priority. |
152 | */ |
153 | public final static int RANDOM = 2; |
154 | |
155 | /** |
156 | * Represents the maximum number of entities in the queue (default is |
157 | * unlimited capacity). |
158 | */ |
159 | protected int queueLimit = Integer.MAX_VALUE; |
160 | |
161 | /** |
162 | * Creates a QueueBased object and initializes all statistical counters. If |
163 | * this standard constructor is used a queue with Fifo sort order and no |
164 | * limited capacity will be created. |
165 | * |
166 | * @param owner |
167 | * desmoj.Model : The model it belongs to |
168 | * @param name |
169 | * java.lang.String : The name for this QueueBased object |
170 | * @param showInReport |
171 | * boolean : Flag if values are shown in report |
172 | * @param showInTrace |
173 | * boolean : Flag if QueueBased writes trace messages |
174 | */ |
175 | public QueueBased(desmoj.core.simulator.Model owner, String name, |
176 | boolean showInReport, boolean showInTrace) { |
177 | |
178 | super(owner, name, showInReport, showInTrace); // create reportable |
179 | |
180 | // initialize all statistics |
181 | _currentLength = 0; // no one in here at starting time |
182 | _lastAcc = presentTime(); // time of last access is now |
183 | _minimumLength = _currentLength; |
184 | _maximumLength = _currentLength; |
185 | _zeros = 0; |
186 | _wSumSquareLength = _wSumLength = _sumSquareWaitTime = 0.0; |
187 | _sumWaitTime = _maximumWaitTime = new TimeSpan(0); |
188 | _maximumWaitTimeAt = _minimumLengthAt = _maximumLengthAt = presentTime(); |
189 | // reset points of simulation time |
190 | |
191 | _qImpWarnings = false; // no queue implementation warnings |
192 | |
193 | } |
194 | |
195 | /** |
196 | * Updates the statistics when a new element is inserted into the underlying |
197 | * queue. Note that this method must always be called whenever an insertion |
198 | * is made. If class <code>QueueList</code> is used in combination with a |
199 | * QueueBased, this method gets called automatically whenever a new Entity |
200 | * is inserted. |
201 | * |
202 | * @see QueueList |
203 | * @see QueueListFifo |
204 | * @see QueueListLifo |
205 | */ |
206 | protected void addItem() { |
207 | |
208 | updateStatistics(); |
209 | _currentLength++; |
210 | |
211 | if (_currentLength > _maximumLength) { // do we have a new record high? |
212 | _maximumLength = _currentLength; // yes, store the record length |
213 | _maximumLengthAt = presentTime(); // and write down the time for |
214 | // it |
215 | } |
216 | |
217 | } |
218 | |
219 | /** |
220 | * Returns the average length of the underlying queue since the last reset. |
221 | * Current length of that queue will be returned, if the time span since the |
222 | * last reset is smaller than the smallest distinguishable timespan epsilon. |
223 | * |
224 | * @return double : The average queue length since last reset or current |
225 | * length of queue if no distinguishable periode of time has passed |
226 | */ |
227 | public double averageLength() { |
228 | |
229 | TimeInstant now = presentTime(); // store current time |
230 | TimeSpan deltaTime = TimeOperations.diff(now, resetAt()); // time since |
231 | // last |
232 | // reset |
233 | if (TimeSpan.isEqual(deltaTime, TimeSpan.ZERO)) { |
234 | // has no time passed since the last reset? |
235 | |
236 | return UNDEFINED; // value is not defined |
237 | } |
238 | |
239 | // calculate the average length |
240 | double avgLength = (_wSumLength + (_currentLength * TimeOperations.diff( |
241 | now, _lastAcc).getTimeInEpsilon())) |
242 | / deltaTime.getTimeInEpsilon(); |
243 | // not nice to read, but it really does calculate the average!!! |
244 | |
245 | // round the average length |
246 | double rndAvgLength = StatisticObject.round(avgLength); |
247 | // return the rounded average length |
248 | |
249 | return rndAvgLength; |
250 | |
251 | } |
252 | |
253 | /** |
254 | * Returns the average waiting time of all objects who have exited the |
255 | * queue. Value is valid for the time span since the last reset. Returns 0 |
256 | * (zero) if no objects have exited the queue after the last reset. |
257 | * |
258 | * @return TimeSpan : Average waiting time of all objects since last reset |
259 | * or 0 if no objects have exited the queue |
260 | */ |
261 | public TimeSpan averageWaitTime() { |
262 | |
263 | double obs = getObservations(); // get and store number of observations |
264 | // return rounded average wait time |
265 | if (obs > 0){ |
266 | // calculate the resulting average wait time |
267 | TimeSpan avgWaitTime = TimeOperations.divide(_sumWaitTime, obs); |
268 | return avgWaitTime; |
269 | } |
270 | else |
271 | return TimeSpan.ZERO; // no observations -> zero TimeSpan value |
272 | |
273 | } |
274 | |
275 | /** |
276 | * Creates the reporter qualified to produce a report about a class that has |
277 | * been derived from QueueBased. This method is declared abstract since no |
278 | * real QueueBased is supposed to be instantiated and thus no rReporter can |
279 | * be defined here. Implement this method in the subclasses of QueueBased |
280 | * such as in class Queue. |
281 | * |
282 | * @see Queue |
283 | */ |
284 | public abstract desmoj.core.report.Reporter createReporter(); |
285 | |
286 | /** |
287 | * Updates the statistics when a new element is exiting the underlying |
288 | * queue. Note that this method must always be called whenever an object is |
289 | * taken from the queue. The simulation time parameter given provides the |
290 | * statistics with the information about the point of time the exiting |
291 | * object had enterd this queue. This is needed to calculate the waiting |
292 | * times. If a QueueBased is used in conjunction with class queuelist, this |
293 | * method is automatically called whenever an entity is taken from the |
294 | * queuelist to keep track of |
295 | * |
296 | * @param entryTime |
297 | * TimeInstant : Point of simulation time that the object now |
298 | * exiting the QueueBased had entered it |
299 | */ |
300 | protected void deleteItem(TimeInstant entryTime) { |
301 | |
302 | updateStatistics(); |
303 | TimeInstant now = presentTime(); // Store the actual simulation time |
304 | TimeSpan waitTime = TimeOperations.diff(now, entryTime); // time waited |
305 | // in |
306 | // queue |
307 | _sumWaitTime = TimeOperations.add(_sumWaitTime, waitTime); // update |
308 | // sum |
309 | |
310 | // calculate square of waitTimes and |
311 | _sumSquareWaitTime += waitTime.getTimeInEpsilon() |
312 | * waitTime.getTimeInEpsilon(); |
313 | |
314 | if (TimeSpan.isLonger(waitTime, _maximumWaitTime)) // do we have a new |
315 | // waiting record? |
316 | { |
317 | _maximumWaitTime = waitTime; // store new record |
318 | _maximumWaitTimeAt = now; // and the moment it happened |
319 | } |
320 | if (TimeSpan.isEqual(waitTime, new TimeSpan(0))) { |
321 | // waitTime was zero |
322 | _zeros++; |
323 | } |
324 | |
325 | if (_currentLength <= 0) { |
326 | sendWarning("Inconsistent Qeueue length", "QueueBased : " |
327 | + getName() + " Method: void activateAfter(TimeSpan dt)", |
328 | "Error in Statistic operations of Queues", |
329 | "Report information to DESMO-J designer Tim Lechler via eMail : " |
330 | + "1lechler@informatik.uni-hamburg.de"); |
331 | return; // bad stuff ?? We got more objects in the queue than |
332 | // registered!!! |
333 | } |
334 | |
335 | _currentLength--; |
336 | |
337 | if (_currentLength < _minimumLength) { |
338 | _minimumLength = _currentLength; |
339 | } |
340 | |
341 | incrementObservations(); |
342 | } |
343 | |
344 | /** |
345 | * Returns the maximum possible number of entities in the underlying queue. |
346 | * |
347 | * @return int : the maximum number of entities in the queue. |
348 | */ |
349 | public int getQueueLimit() { |
350 | return queueLimit; |
351 | } |
352 | |
353 | /** |
354 | * Returns the current length of the underlying queue. |
355 | * |
356 | * @return int : The current queue length, zero if empty. |
357 | */ |
358 | public int length() |
359 | { |
360 | |
361 | return _currentLength; |
362 | |
363 | } |
364 | |
365 | /** |
366 | * Returns the maximum length of the underlying queue since the last reset. |
367 | * |
368 | * @return int : The maximum queue length since last reset |
369 | */ |
370 | public int maxLength() { |
371 | |
372 | return _maximumLength; |
373 | |
374 | } |
375 | |
376 | /** |
377 | * Returns the point of simulation time with the maximum number of objects |
378 | * waiting inside the underlying queue. The value is valid for the period |
379 | * since the last reset. |
380 | * |
381 | * @return desmoj.TimeInstant : Point of time with maximum queue length |
382 | * since last reset |
383 | */ |
384 | public TimeInstant maxLengthAt() { |
385 | |
386 | return _maximumLengthAt; |
387 | |
388 | } |
389 | |
390 | /** |
391 | * Returns the maximum duration in simulation time that an object has spent |
392 | * waiting inside the underlying queue. The value is valid for the period |
393 | * since the last reset. |
394 | * |
395 | * @return desmoj.TimeSpan : Longest waiting time of an object in the queue |
396 | * since last reset |
397 | */ |
398 | public TimeSpan maxWaitTime() { |
399 | |
400 | return _maximumWaitTime; |
401 | |
402 | } |
403 | |
404 | /** |
405 | * Returns the point of simulation time when the object with the maximum |
406 | * waiting time exited the underlying queue. The value is valid for the |
407 | * period since the last reset. |
408 | * |
409 | * @return desmoj.TimeInstant : The point of simulation time when the object |
410 | * with the maximum waiting time exited the queue |
411 | */ |
412 | public TimeInstant maxWaitTimeAt() { |
413 | |
414 | return _maximumWaitTimeAt; |
415 | |
416 | } |
417 | |
418 | /** |
419 | * Returns the minimumn length of the underlying queue since the last reset. |
420 | * |
421 | * @return int : The minimum queue length since last reset |
422 | */ |
423 | public int minLength() { |
424 | |
425 | return _minimumLength; |
426 | |
427 | } |
428 | |
429 | /** |
430 | * Returns the point of simulation time with the minimum number of objects |
431 | * waiting inside the underlying queue. The value is valid for the period |
432 | * since the last reset. |
433 | * |
434 | * @return desmoj.TimeInstant : Point of time with minimum queue length |
435 | * since last reset |
436 | */ |
437 | public TimeInstant minLengthAt() { |
438 | |
439 | return _minimumLengthAt; |
440 | |
441 | } |
442 | |
443 | /** |
444 | * Returns a boolean flag telling if the underlying queue implementation |
445 | * should issue own warnings or not. The warnings from the queue |
446 | * implementation (<code>QueueList</code>) are needed for debugging |
447 | * purposes. |
448 | * |
449 | * @return boolean : Is <code>true</code> if the underlying queue |
450 | * implementation should issue warnings, <code>false</code> |
451 | * otherwise |
452 | */ |
453 | boolean qImpWarn() { |
454 | |
455 | return _qImpWarnings; |
456 | |
457 | } |
458 | |
459 | /** |
460 | * Resets all statistical counters to their default values. The mininum and |
461 | * maximum length of the queue are set to the current number of queued |
462 | * objects. |
463 | */ |
464 | public void reset() { |
465 | |
466 | super.reset(); // reset of Reportable |
467 | |
468 | _lastAcc = presentTime(); // time of last access is now |
469 | _minimumLength = _currentLength; |
470 | _maximumLength = _currentLength; |
471 | _zeros = 0; |
472 | _wSumLength = _wSumSquareLength = _sumSquareWaitTime = 0.0; |
473 | _sumWaitTime = _maximumWaitTime = new TimeSpan(0); |
474 | _maximumWaitTimeAt = _minimumLengthAt = _maximumLengthAt = presentTime(); |
475 | // reset points of simulation time |
476 | |
477 | } |
478 | |
479 | /** |
480 | * Method switches on warnings issued from the underlying queue |
481 | * implementation if parameter given is <code>true</code>. Warnings are |
482 | * suppressed if <code>false</code> is given. This method is used for |
483 | * internal debugging only. |
484 | * |
485 | * @param warnFlag |
486 | * boolean :<code>true</code> switches warnings on, |
487 | * <code>false</code> switches warnings off |
488 | */ |
489 | public void setQueueImpWarning(boolean warnFlag) { |
490 | |
491 | _qImpWarnings = warnFlag; |
492 | |
493 | } |
494 | |
495 | /** |
496 | * Returns the standard deviation of the queue's length. Value is weighted |
497 | * over time. |
498 | * |
499 | * @return double : The standard deviation for the queue's length weighted |
500 | * over time |
501 | */ |
502 | public double stdDevLength() { |
503 | |
504 | TimeInstant now = presentTime(); // store current time |
505 | TimeSpan deltaTime = TimeOperations.diff(now, resetAt()); // time since |
506 | // last |
507 | // reset |
508 | if (TimeSpan.isEqual(deltaTime, TimeSpan.ZERO)) // |
509 | // no time passed since the last reset |
510 | return UNDEFINED; // no valid data |
511 | else { |
512 | double len = _currentLength; // store and convert length |
513 | double mean = averageLength(); // get mean for queuelength |
514 | TimeSpan spanSinceLastAcess = TimeOperations.diff(now, _lastAcc); // time |
515 | // span |
516 | // since last |
517 | // access |
518 | return java.lang.Math.sqrt(java.lang.Math |
519 | .abs((_wSumSquareLength + (len * len * spanSinceLastAcess |
520 | .getTimeInEpsilon())) |
521 | / deltaTime.getTimeInEpsilon() - (mean * mean))); |
522 | } |
523 | |
524 | } |
525 | |
526 | /** |
527 | * Returns the standard deviation of the queue's objects waiting times. |
528 | * |
529 | * @return double : The standard deviation for the queue's objects waiting |
530 | * times |
531 | */ |
532 | public TimeSpan stdDevWaitTime() { |
533 | |
534 | if (getObservations() > 0) { |
535 | |
536 | double mean = averageWaitTime().getTimeInEpsilon(); // get avrg time |
537 | double obs = getObservations(); // number of obs exited |
538 | |
539 | return new TimeSpan(java.lang.Math.sqrt( // not nice but functual |
540 | java.lang.Math |
541 | .abs(((obs * _sumSquareWaitTime) - (mean * mean)) |
542 | / (obs * (obs - 1.0)))), TimeOperations |
543 | .getEpsilon()); // as simple as |
544 | // that |
545 | } else |
546 | return TimeSpan.ZERO; // no observations -> no values |
547 | |
548 | } |
549 | |
550 | /** |
551 | * Updates the parts of the statistics used by both addItem and deleteItem. |
552 | */ |
553 | protected void updateStatistics() { |
554 | |
555 | TimeInstant now = presentTime(); // store current time |
556 | TimeSpan deltaTime = TimeOperations.diff(now, _lastAcc); // get time |
557 | // since last |
558 | // xs |
559 | _wSumLength += _currentLength * deltaTime.getTimeInEpsilon(); // weighted |
560 | // length sum |
561 | _wSumSquareLength += _currentLength * _currentLength |
562 | * deltaTime.getTimeInEpsilon();// weighted square length sum |
563 | _lastAcc = now; |
564 | |
565 | } |
566 | |
567 | /** |
568 | * Returns the number of objects that have passed through the queue without |
569 | * spending time waiting. |
570 | * |
571 | * @return long : The number of elements who have passed the queue without |
572 | * waiting |
573 | */ |
574 | public long zeroWaits() { |
575 | |
576 | return _zeros; |
577 | |
578 | } |
579 | } |