1 | package desmoj.core.simulator; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.LinkedList; |
5 | import java.util.List; |
6 | import org.apache.commons.collections.map.ReferenceMap; |
7 | |
8 | import desmoj.core.report.ReportManager; |
9 | import desmoj.core.report.Reporter; |
10 | |
11 | /** |
12 | * The model is supposed to carry references to all static modelcomponents |
13 | * associated to a model. These are modelcomponents like distributions and |
14 | * statistic counters. Note that placing all essential components inside the |
15 | * model class allows you to use object orientation to reuse this model to |
16 | * implement different versions of it using subclassing for changes necessary |
17 | * for using other modelling paradigms. This way models can be easily |
18 | * reimplemented following the process-, event-, transaction- or activity- |
19 | * oriented paradigm. Note that transaction- and activity-oriented modelling |
20 | * requires the higher process synchronization mechanisms (Res, Bin, WaitQueue, |
21 | * CondQueue) by Soenke Claassen. |
22 | * <p> |
23 | * The modelcomponents belonging to a model have to be instantiated in the |
24 | * <code>init()</code> method. They must not be instantiated in the model's |
25 | * constructor since most components need access to a valid experiment object. |
26 | * This can only be accessed after a model has been connected to an experiment |
27 | * using method <code>connectToExperiment(Experiment exp)</code> which |
28 | * implicitly calls the <code>init()</code> method after receiving the valid |
29 | * reference to an experiment. |
30 | * |
31 | * @version DESMO-J, Ver. 2.3.3 copyright (c) 2011 |
32 | * @author Tim Lechler |
33 | * |
34 | * Licensed under the Apache License, Version 2.0 (the "License"); |
35 | * you may not use this file except in compliance with the License. You |
36 | * may obtain a copy of the License at |
37 | * http://www.apache.org/licenses/LICENSE-2.0 |
38 | * |
39 | * Unless required by applicable law or agreed to in writing, software |
40 | * distributed under the License is distributed on an "AS IS" |
41 | * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
42 | * or implied. See the License for the specific language governing |
43 | * permissions and limitations under the License. |
44 | * |
45 | */ |
46 | public abstract class Model extends Reportable { |
47 | |
48 | /** |
49 | * The experiment this model is connected to. |
50 | */ |
51 | private Experiment _myExperiment; |
52 | |
53 | /** |
54 | * The map with all entities and their identifiers. |
55 | */ |
56 | private ReferenceMap _entityMap; |
57 | |
58 | /** |
59 | * True if this is the main model (not a submodel to another model), false |
60 | * for submodels. |
61 | */ |
62 | boolean isMainModel; |
63 | |
64 | /** |
65 | * The current number of the created entities. |
66 | */ |
67 | private long _numberOfEntitiesCreated; |
68 | |
69 | /** |
70 | * Dynamic List containing all associated submodels. |
71 | */ |
72 | private ArrayList<Model> _subModels; |
73 | |
74 | /** |
75 | * Dynamic List containing all reportable components of this model. |
76 | */ |
77 | private ArrayList<Reportable> _reportables; |
78 | |
79 | /** |
80 | * The parameter manager |
81 | */ |
82 | private ModelParameterManager _paramManager; |
83 | |
84 | /** |
85 | * Constructs a model, with the given name and parameters for report and |
86 | * trace files. |
87 | * |
88 | * @param name |
89 | * java.lang.String : The name of this model |
90 | * @param owner |
91 | * Model : The main model this model is associated to |
92 | * @param showInTrace |
93 | * boolean : Flag for showing this model in trace-files. Set it |
94 | * to <code>true</code> if model should show up in trace, |
95 | * <code>false</code> if model should not be shown in trace. |
96 | */ |
97 | public Model(Model owner, String name, boolean showInReport, |
98 | boolean showInTrace) { |
99 | |
100 | super(owner, name, showInReport, showInTrace); // create a reportable |
101 | |
102 | // init data structures |
103 | _subModels = new ArrayList<Model>(); // create empty Vector for subModels |
104 | _reportables = new ArrayList<Reportable>(); // create empty Vector for reportables |
105 | |
106 | if (owner != null) { // check if this is a submodel |
107 | this.isMainModel = false; |
108 | owner.registerSubModel(this); |
109 | // register as submodel at owner model |
110 | } else { |
111 | this.isMainModel = true; |
112 | } |
113 | |
114 | _paramManager = new ParameterManager(); // create ParameterManager |
115 | |
116 | _numberOfEntitiesCreated = 0L; |
117 | |
118 | _entityMap = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); |
119 | } |
120 | |
121 | /** |
122 | * Checks the given modelcomponent for compatibility. Needed exclusively |
123 | * when submodels are present that want to share modelcomponents between |
124 | * them. The standard implementation only checks if the modelcomponent |
125 | * <code>other</code> belongs to <code>this</code> model. For use with |
126 | * multiple models, the user has to specify which other models are |
127 | * compatible to this model by overloading this method. |
128 | * |
129 | * @return boolean : Returns <code>true</code> if the given modelcomponent |
130 | * is compatible to this model, <code>false</code> otherwise. |
131 | * @param other |
132 | * ModelComponent : The modelcomponent that needs to be checked |
133 | * for compatibility to this model. |
134 | */ |
135 | protected boolean checkCompatibility(ModelComponent other) { |
136 | |
137 | return (this == other.getModel()); // check for same owner |
138 | |
139 | } |
140 | |
141 | /** |
142 | * Connects this model to an experiment. The given experiment must not |
143 | * already be connected to some other model. Otherwise an errormessage will |
144 | * be given and the experiment will be stopped. Since a model that is not |
145 | * yet connected has no access to the experiment's messaging subsystem, |
146 | */ |
147 | public void connectToExperiment(Experiment exp) { |
148 | |
149 | if (exp == null) { |
150 | sendWarning("Can not connect to a <code>null</code> Experiment! " |
151 | + "Command ignored.", "Model : " + getName() |
152 | + " Method: void connectToExperiment" + "(Experiment exp)", |
153 | "The experiment reference passed is a null reference!", |
154 | "Make sure to use valid references only."); |
155 | return; // no connection possible. |
156 | } |
157 | |
158 | if (!isMainModel()) { // only main model should register at experiment |
159 | sendWarning("Can not connect to Experiment " + exp.getName() + "! " |
160 | + "Command ignored.", "Model : " + getName() |
161 | + " Method: void connectToExperiment" + "(Experiment exp)", |
162 | "The model to be connected is already submodel to model : " |
163 | + getModel().getName(), |
164 | "Only ,main models are allowed to be connected to an " |
165 | + "experiment"); |
166 | return; // model is no main model |
167 | } |
168 | |
169 | if (exp.isConnected()) { |
170 | sendWarning("Can not connect to Experiment " + exp.getName() + "! " |
171 | + "Command ignored.", "Model : " + getName() |
172 | + " Method: void connectToExperiment" + "(Experiment exp)", |
173 | "The experiment to connect to is itself already connected " |
174 | + "to model : " + exp.getModel(), |
175 | "Only one main model can be connected to an experiment at " |
176 | + "a time."); |
177 | return; // no connection possible. |
178 | } |
179 | |
180 | if (_myExperiment != null) { |
181 | sendWarning("Can not connect to Experiment " + exp.getName() + "! " |
182 | + "Command ignored.", "Model : " + getName() |
183 | + " Method: void connectToExperiment" + "(Experiment exp)", |
184 | "The model is already connected to Experiment : " |
185 | + _myExperiment.getName() + "!", |
186 | "A model can only be connected to just one Experiment."); |
187 | return; |
188 | } |
189 | |
190 | // set the experiment reference |
191 | _myExperiment = exp; |
192 | |
193 | // register at Experiment |
194 | _myExperiment.registerModel(this); |
195 | |
196 | // set the Experiment parameter at all submodels if necessary |
197 | if (!_subModels.isEmpty()) { |
198 | |
199 | for (Model m : _subModels) { |
200 | m.setConnectedExperiment(exp); |
201 | } |
202 | |
203 | } |
204 | |
205 | // initialize the components of the main model |
206 | init(); |
207 | // initialize the submodels recursively |
208 | doSubmodelInit(); |
209 | // reset the model and all submodels recursively |
210 | reset(); |
211 | |
212 | } |
213 | |
214 | /** |
215 | * Creates the default reporter associated with this model. The default |
216 | * reporter returned by this method just prints the model's description as |
217 | * implemented in method <code>description()</code> and appends the |
218 | * simulation time of the last reset. If the user's model offers more |
219 | * information, create a special class derived from class |
220 | * <code>desmoj.report.ModelReporter</code> to extract and present that |
221 | * extra information. |
222 | * |
223 | * @return Reportable : The reporter associated with this model |
224 | */ |
225 | public Reporter createReporter() { |
226 | |
227 | // has to be the special reporter designed for this model |
228 | return new desmoj.core.report.ModelReporter(this); |
229 | |
230 | } |
231 | |
232 | /** |
233 | * Should return the description of the model. Implement this method to |
234 | * document the model's intent, creator, date and all other information that |
235 | * needs to be published about this model in a report. Please use it |
236 | * generously, other people interested in your model will thank you. |
237 | * |
238 | * @return java.lang.String : The description of the model as a text String |
239 | */ |
240 | public abstract String description(); |
241 | |
242 | /** |
243 | * Implement this method to schedule the entities and/or events needed for |
244 | * your model to start in a defined setting. If a model is connected to an |
245 | * experiment and started without any initial schedules, it will not run |
246 | * since no events to process are on the scheduler's event-list. This method |
247 | * is called automatically by the framework when the experiment's |
248 | * <code>start()</code> method is called. |
249 | */ |
250 | public abstract void doInitialSchedules(); |
251 | |
252 | /** |
253 | * Calls the <code>init()</code> method of all registered |
254 | * submodels of the current model. Subsequently calls the |
255 | * <code>init()</code> method of all registered submodels |
256 | * of every submodel. |
257 | */ |
258 | void doSubmodelInit() { |
259 | |
260 | if (!this.hasSubModels()) |
261 | return; // no submodels registered here |
262 | else { |
263 | |
264 | for (Model m : _subModels) { // loop submodels |
265 | m.init(); // init submodel |
266 | m.doSubmodelInit(); // init submodels of submodels |
267 | } |
268 | } |
269 | } |
270 | |
271 | /** |
272 | * Calls the <code>doInitialSchedules()</code> method of all registered |
273 | * submodels of the current model. Subsequently calls the |
274 | * <code>doSubmodelSchedules()</code> method of all registered submodels |
275 | * of every submodel. |
276 | */ |
277 | void doSubmodelSchedules() { |
278 | |
279 | if (!this.hasSubModels()) |
280 | return; // no submodels registered here |
281 | else { |
282 | |
283 | for (Model m : _subModels) { // loop submodels |
284 | m.doInitialSchedules(); // initial schedules for the submodel |
285 | m.doSubmodelSchedules(); // initial schedules for potential submodels of the submodel |
286 | } |
287 | |
288 | } |
289 | } |
290 | |
291 | /** |
292 | * Returns the entity to the belonging identifier. |
293 | * @return Entity : The class will return the |
294 | * <code>Entity</code> or null if no such exists. |
295 | */ |
296 | public Entity getEntity(long ident) |
297 | { |
298 | return (Entity) _entityMap.get(Long.valueOf(ident)); |
299 | } |
300 | |
301 | /** |
302 | * Returns a list of this model's entities. |
303 | * |
304 | * @param includeSubmodels |
305 | * boolean : Do include (true) or exclude (false) submodels' entities in the list. |
306 | * |
307 | * @return List<Entity> : The class will return a list of all existing objects of |
308 | * <code>Entity</code> or null if no list exists. |
309 | */ |
310 | public List<Entity> getEntities(boolean includeSubmodels) |
311 | { |
312 | List<Entity> list = new LinkedList<Entity>(); |
313 | list.addAll(_entityMap.values()); |
314 | |
315 | if (includeSubmodels) { |
316 | for (Model submodel : this._subModels) { |
317 | list.addAll(submodel.getEntities(true)); |
318 | } |
319 | } |
320 | |
321 | return list; |
322 | } |
323 | |
324 | /** |
325 | * Returns the experiment that is connected to this model or |
326 | * <code>null</code> if the model is not yet connected to an experiment. |
327 | * |
328 | * @return Experiment : The experiment that this model is connected to or |
329 | * <code>null</code> if no connection is established |
330 | */ |
331 | public Experiment getExperiment() { |
332 | |
333 | if (this.isMainModel()) { |
334 | |
335 | return _myExperiment; |
336 | |
337 | } else { |
338 | |
339 | return this.getModel().getExperiment(); |
340 | |
341 | } |
342 | } |
343 | |
344 | /** |
345 | * Returns a copy of the dynamic list containing all reportable |
346 | * components of this model. |
347 | * |
348 | * @return reportables : A list containing all reportable |
349 | * components of this model. |
350 | */ |
351 | public List<Reportable> getReportables() { |
352 | return new ArrayList<Reportable>(this._reportables); |
353 | } |
354 | |
355 | /** |
356 | * Returns the Model's viewpoint of the ParameterManager |
357 | * |
358 | * @return ModelParameterManager : The ParameterManager |
359 | */ |
360 | public ModelParameterManager getParameterManager() |
361 | { |
362 | return _paramManager; |
363 | } |
364 | |
365 | /** |
366 | * Shows, if there are submodels registered with this model. |
367 | * |
368 | * @return boolean : True, if there is at least one submodel registered at |
369 | * this Model, flase if there are no submodels |
370 | */ |
371 | public boolean hasSubModels() { |
372 | |
373 | return !_subModels.isEmpty(); // checks for some submodels in vector |
374 | |
375 | } |
376 | |
377 | /** |
378 | * Implement this method to do initialization work for your model. All |
379 | * ModelComponents associated with this model are supposed to be |
380 | * instantiated in this method. |
381 | * <p> |
382 | * <em>Caution! Do not use the Model's constructor method to instantiate your |
383 | * modelcomponents!</em> |
384 | * <br> |
385 | * This prevents the modelcomponents to get contact to the associated |
386 | * Experiment since a Model can only be connected to an Experiment after |
387 | * itself has been instantiated correctly. Thus it is necessary to put all |
388 | * initialization and instantiation work into this method. Do not use this |
389 | * method to do the initial schedules needed for the scheduler to start |
390 | * with. Use method <code>doInitialSchedules()</code> instead. If there |
391 | * are no events scheduled in the event-list, the scheduler will stop the |
392 | * simulation immediately because there is nothing for him to do. If you are |
393 | * using submodels, take care that each of the submodels has its own |
394 | * <code>init</code> method properly set up. |
395 | */ |
396 | public abstract void init(); |
397 | |
398 | /** |
399 | * Initializes the model by calling method <code>init()</code> to set up |
400 | * all model related components as specified in that method. After |
401 | * initializing the model, the given ModelOption's <code>setOption()</code> |
402 | * method is called with this model as parameter. This enables the user to |
403 | * specify different model parameter settings for different experiments. It |
404 | * even allows automated parameter variation to give a small but handy aid |
405 | * for model optimization. Implement the ModelOptions to set the parameters |
406 | * for your model. Make sure to react properly on the ModelOptions given. |
407 | * Note that specifiying ModelOptions is up to the user. |
408 | * |
409 | * @param options |
410 | * ModelOptions : The parameter settings for this model |
411 | */ |
412 | public void init(ModelOptions options) { |
413 | |
414 | this.init(); // just call the other init after dealing with the |
415 | // options |
416 | options.setOptions(this); |
417 | |
418 | } |
419 | |
420 | /** |
421 | * Shows if this model has already been connected to an experiment. |
422 | * |
423 | * @return boolean : Is <code>true</code>, if model is connected to an |
424 | * experiment, <code>false</code> otherwise |
425 | */ |
426 | public boolean isConnected() { |
427 | |
428 | return (_myExperiment != null); // no experiment connected |
429 | |
430 | } |
431 | |
432 | /** |
433 | * Shows if this model is a main model and thus is not submodel of other |
434 | * models. |
435 | * |
436 | * @return boolean : Returns <code>true</code> if the model is a main |
437 | * model, <code>false</code> if it is a submodel of other models |
438 | */ |
439 | public boolean isMainModel() { |
440 | |
441 | return this.isMainModel; |
442 | |
443 | } |
444 | |
445 | /** |
446 | * Shows if this model is submodel to another model and thus is not the main |
447 | * models. |
448 | * |
449 | * @return boolean : Returns <code>true</code> if the model is a submodel |
450 | * of other models, <code>false</code> if it is a main model |
451 | */ |
452 | public boolean isSubModel() { |
453 | |
454 | return !this.isMainModel; |
455 | } |
456 | |
457 | /** |
458 | * Links an entity with an unique identification number. |
459 | * |
460 | * @param e |
461 | * Entity : The new Entity to link with an unique identification number. |
462 | * |
463 | * @return long : Unique identification number. |
464 | */ |
465 | long linkWithIdentNumber(Entity e) |
466 | { |
467 | _numberOfEntitiesCreated++; |
468 | _entityMap.put(_numberOfEntitiesCreated, e); |
469 | return _numberOfEntitiesCreated; |
470 | } |
471 | |
472 | /** |
473 | * Registers a reportable at this model. Reports can only be drawn, if the |
474 | * reportbale is registered at his model. |
475 | * |
476 | * @param r |
477 | * desmoj.core.simulator.Reportable : The reportable to be registered |
478 | */ |
479 | void register(Reportable r) { |
480 | |
481 | if (r == null) |
482 | return; // do not accept null values |
483 | else |
484 | _reportables.add(r); |
485 | |
486 | } |
487 | |
488 | /** |
489 | * Registers another model as submodel of this model. |
490 | * |
491 | * @param subModel |
492 | * Model : The model to be registerd as a submodel |
493 | */ |
494 | void registerSubModel(Model subModel) { |
495 | |
496 | if (subModel == null) { |
497 | sendWarning("Can't register Submodel!", "Model : " + getName() |
498 | + " Method: registerSubModel(Model " + "subModel)", |
499 | "The SubModel given as parameter is a null reference.", |
500 | "Be sure to have a valid SubModel reference before calling " |
501 | + "this method."); |
502 | return; // exit |
503 | } |
504 | |
505 | _subModels.add(subModel); |
506 | |
507 | } |
508 | |
509 | /** |
510 | * Produces a report about the model and all its related modelcomponents and |
511 | * submodels. It registers the model's reporter and all other reporters at |
512 | * the experiment's reportmanager to produce the report output. The |
513 | * reporters are ordered into the reportmanager returned with this model's |
514 | * reporter first, followed by all reportable's reporters. After this |
515 | * section, all submodel's reporters are inserted according to the order |
516 | * they were attached to this model. Each submodel's section is sorted using |
517 | * the criteria above. Thus all reporters a re separated by model to prevent |
518 | * confusion. |
519 | * |
520 | * @return Enumeration : The Enumeration carrying all reporters available |
521 | * from this model |
522 | */ |
523 | List<Reporter> report() { |
524 | |
525 | // create the reportmanager necessary for sorting all reporters |
526 | ReportManager repMan = new ReportManager(getName() + "_ReportManager"); |
527 | |
528 | // register this model's reporter |
529 | repMan.register(createReporter()); |
530 | |
531 | // register all modelcomponent's reporters |
532 | // in case they are set to produce a report output |
533 | for (Reportable r : _reportables) { // loop reportables |
534 | if (r.reportIsOn()) |
535 | repMan.register(r.createReporter()); |
536 | } |
537 | |
538 | // register all submodel's reporters |
539 | List<Reporter> subReporters; |
540 | // buffer for submodel-reportmanagers returned |
541 | |
542 | for (Model m : _subModels) { // loop submodels |
543 | |
544 | subReporters = m.report(); |
545 | |
546 | // add all reporters from the ordered enumeration at last position |
547 | // of repMan |
548 | for (Reporter r : subReporters) { |
549 | repMan.addLast(r); |
550 | } |
551 | } |
552 | |
553 | return repMan.elements(); |
554 | |
555 | } |
556 | |
557 | /** |
558 | * Resets the statistic counters of this model and of all its registered |
559 | * reportables. Also calls all submodels's <code>reset()</code> methods. |
560 | */ |
561 | public void reset() { |
562 | |
563 | // reset this model |
564 | super.reset(); // reset the own obs & resetAt variables |
565 | |
566 | // reset all reportables registered here |
567 | if (_reportables.isEmpty()) |
568 | return; // no reportables registered |
569 | else { |
570 | |
571 | for (Reportable r : _reportables) { // loop reportables |
572 | r.reset(); |
573 | // reset each registered |
574 | } |
575 | |
576 | } |
577 | |
578 | // reset all registered submodels |
579 | if (_subModels.isEmpty()) |
580 | return; // no reportables registered |
581 | else { |
582 | |
583 | for (Model m : _subModels) { // loop submodels |
584 | m.reset(); |
585 | // reset each registered |
586 | } |
587 | |
588 | } |
589 | |
590 | } |
591 | |
592 | /** |
593 | * Connects submodels to the experiment given. This method is called by the |
594 | * main model, when submodels register themselves at the main model. |
595 | * |
596 | * @param e |
597 | * desmoj.Experiment : The experiment to connect this model to |
598 | */ |
599 | void setConnectedExperiment(Experiment e) { |
600 | |
601 | if (e == null) { |
602 | sendWarning( |
603 | "Can not connect to experiment! Command ignored.", |
604 | "Model : " + getName() + " Method: " |
605 | + "void setConnectedExperiment (Experiment e)", |
606 | "The given Esperiment parameter contained a null reference", |
607 | "Be sure to always give valid parameters."); |
608 | return; // do nothing |
609 | } |
610 | |
611 | if (isSubModel()) { // this is a submodel |
612 | _myExperiment = e; |
613 | return; |
614 | } else { // this is no submodel |
615 | sendWarning("Can not connect to experiment! Command ignored.", |
616 | "Model : " + getName() + " Method: void " |
617 | + "method setConnectedExperiment" |
618 | + "(Experiment e)", |
619 | "This model is not a submodel.", |
620 | "Only submodels can be connected to an experiment using " |
621 | + "this method."); |
622 | return; // do nothing |
623 | } |
624 | |
625 | } |
626 | |
627 | /** |
628 | * Sets the current model to be the main model by setting the |
629 | * <code>owner</code> reference to itself. |
630 | */ |
631 | void setMain() { |
632 | |
633 | setOwner(this); |
634 | |
635 | } |
636 | |
637 | } |