| 1 | package desmoj.core.simulator; |
| 2 | |
| 3 | import java.util.LinkedList; |
| 4 | import java.util.List; |
| 5 | |
| 6 | /** |
| 7 | * All objects that want to be handled by the scheduler must extend this class. |
| 8 | * These are events (including external events), entities and SimProcesses. All |
| 9 | * functionality common to Schedulable objects are encapsulated here. |
| 10 | * |
| 11 | * @version DESMO-J, Ver. 2.3.3 copyright (c) 2011 |
| 12 | * @author Tim Lechler |
| 13 | * |
| 14 | * Licensed under the Apache License, Version 2.0 (the "License"); you |
| 15 | * may not use this file except in compliance with the License. You may |
| 16 | * obtain a copy of the License at |
| 17 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 18 | * |
| 19 | * Unless required by applicable law or agreed to in writing, software |
| 20 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 21 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 22 | * implied. See the License for the specific language governing |
| 23 | * permissions and limitations under the License. |
| 24 | * |
| 25 | */ |
| 26 | public class Schedulable extends ModelComponent { |
| 27 | |
| 28 | /** |
| 29 | * A list containing all scheduled entries for this Schedulable in future. |
| 30 | */ |
| 31 | protected List<EventNote> _schedule; |
| 32 | |
| 33 | /** |
| 34 | * Constructs a Schedulable object with the given name. Note that to |
| 35 | * identify the multiple objects that can be created of one type of |
| 36 | * Schedulable, each has a unique number added to its name as a suffix. The |
| 37 | * numbers represent the amount of objects being created with the same name. |
| 38 | * |
| 39 | * @param name |
| 40 | * java.lang.String : The name of the new Schedulable object |
| 41 | * @param owner |
| 42 | * Model : The owner of this Schedulable object |
| 43 | */ |
| 44 | public Schedulable(Model owner, String name, boolean showInTrace) { |
| 45 | |
| 46 | // create modelcomponent with the checked and registered name |
| 47 | super(owner, owner.getExperiment().getNameCatalog() |
| 48 | .registeredName(name), showInTrace); |
| 49 | this._schedule = new LinkedList<EventNote>(); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | /** |
| 54 | * Removes all events scheduled for this entity from the event-list. |
| 55 | * The Entity will not become active until scheduled again. |
| 56 | */ |
| 57 | public void cancel() |
| 58 | { |
| 59 | if (!isScheduled()) { |
| 60 | sendWarning("Can't cancel Schedulable! Command ignored.", |
| 61 | "Schedulable : " + getName() + " Method: void cancel()", |
| 62 | "The Schedulable to be canceled is not scheduled.", |
| 63 | "Use method isSchedule() to test if the Schedulable " |
| 64 | + "is scheduled and thus can be canceled or not."); |
| 65 | return; // was already scheduled |
| 66 | } |
| 67 | |
| 68 | // removes all scheduled events for this entity by using global event-list |
| 69 | while(!_schedule.isEmpty()) |
| 70 | { |
| 71 | //EventNote firstNote = this.getModel().getExperiment().getScheduler().evList.firstNote(); |
| 72 | EventNote firstNote = _schedule.get(0); |
| 73 | |
| 74 | if (currentlySendTraceNotes()) { |
| 75 | TimeInstant time = firstNote.getTime(); |
| 76 | if (this == current()) { |
| 77 | if (time == presentTime()) |
| 78 | sendTraceNote("cancels scheduled Event " + firstNote.getEvent() + " for itself, which was scheduled now"); |
| 79 | else |
| 80 | sendTraceNote("cancels scheduled Event " + firstNote.getEvent() + " for itself, which was scheduled at " + time); |
| 81 | } else { |
| 82 | if (time == presentTime()) |
| 83 | sendTraceNote("cancels scheduled Event " + firstNote.getEvent() + " for " + this.getName() + ", which was scheduled now"); |
| 84 | else |
| 85 | sendTraceNote("cancels scheduled Event " + firstNote.getEvent() + " for " + this.getName() + ", which was scheduled at " + time); |
| 86 | } |
| 87 | } |
| 88 | this.getModel().getExperiment().getScheduler().evList.remove(firstNote); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | |
| 93 | /** |
| 94 | * Returns a list of EventNote associated to this Entity object. If the |
| 95 | * Entity object is not currently scheduled, <code>null</code> will be |
| 96 | * returned. Remind that all different Event classes can be included. |
| 97 | * |
| 98 | * @return List<EventNote> : The event-notes associated to the entity or |
| 99 | * <code>null</code> if Entity is not currently scheduled |
| 100 | */ |
| 101 | List<EventNote> getEventNotes() |
| 102 | { |
| 103 | return _schedule; |
| 104 | |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Shows if this Schedulable is the currently active object. |
| 109 | * |
| 110 | * @return boolean :<code>true</code> if this Schedulable is the currently |
| 111 | * active, <code>false</code> otherwise |
| 112 | */ |
| 113 | public boolean isCurrent() { |
| 114 | |
| 115 | return this.equals(getModel().getExperiment().getScheduler() |
| 116 | .getCurrentSchedulable()); |
| 117 | |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Tests if this entity has already been scheduled. |
| 122 | * |
| 123 | * @return boolean :<code>true</code> if already scheduled, |
| 124 | * <code>false</code> otherwise |
| 125 | */ |
| 126 | public boolean isScheduled() { |
| 127 | |
| 128 | // Not associated to EventNote if not scheduled |
| 129 | return (!_schedule.isEmpty()); |
| 130 | |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Removes an event-note from the internal list |
| 135 | * |
| 136 | * * @param note |
| 137 | * EventNote : The <code>EventNote to be removed</code> |
| 138 | */ |
| 139 | void removeEventNote(EventNote note) |
| 140 | { |
| 141 | _schedule.remove(note); // only removes Event in local list |
| 142 | |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Allows to rename a Schedulable object while keeping its internally added |
| 147 | * serial number to keep track of the individual object. Note that invalid |
| 148 | * string parameters will result in renaming the Schedulable to 'unnamed'. |
| 149 | * |
| 150 | * @param newName |
| 151 | * java.lang.String : The Schedulable's new name |
| 152 | */ |
| 153 | public void rename(String newName) { |
| 154 | |
| 155 | super.rename(getModel().getExperiment().getNameCatalog() |
| 156 | .registeredName(newName)); |
| 157 | |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * Re-schedules the Schedulable by shifting all EventNote by a specified <code> |
| 162 | * TimeSpan</code>. |
| 163 | * |
| 164 | * @param dt |
| 165 | * TimeSpan : The offset to the current simulation time at which |
| 166 | * this Schedulable is to be re-scheduled |
| 167 | */ |
| 168 | public void reSchedule(TimeSpan dt) |
| 169 | { |
| 170 | if (dt == null) { |
| 171 | sendWarning("Can't reSchedule Schedulable! Command ingnored.", |
| 172 | "Schedulable : " + getName() |
| 173 | + " Method: reSchedule(TimeSpan dt)", |
| 174 | "The simulation time given as parameter is a null " |
| 175 | + "reference.", |
| 176 | "Be sure to have a valid TimeSpan reference before calling " |
| 177 | + "this method."); |
| 178 | return; // no proper parameter |
| 179 | } |
| 180 | |
| 181 | if (!isScheduled()) { |
| 182 | sendWarning("Can't reSchedule Schedulable! Command ingnored.", |
| 183 | "Schedulable : " + getName() |
| 184 | + " Method: reSchedule(TimeSpan dt)", |
| 185 | "The Schedulable is not scheduled, thus unable to be " |
| 186 | + "reScheduled..", |
| 187 | "Be sure that the Schedulable is currently scheduled " |
| 188 | + "before calling this method."); |
| 189 | return; // no proper parameter |
| 190 | } |
| 191 | |
| 192 | // create list with new events and remove old events |
| 193 | EventNote newNote = null; |
| 194 | List<EventNote> oldNotes = new LinkedList<EventNote>(_schedule); |
| 195 | List<EventNote> newNotes = new LinkedList<EventNote>(); |
| 196 | for (EventNote oldNote : oldNotes) |
| 197 | { |
| 198 | newNote = oldNote.copy(); // copy EventNote |
| 199 | newNote.setTime(TimeOperations.add(oldNote.getTime(), dt)); // shift it |
| 200 | newNotes.add(newNote); // save it to new list |
| 201 | this.getModel().getExperiment().getScheduler().evList.remove(oldNote); // remove original event-note local and global |
| 202 | |
| 203 | if (currentlySendTraceNotes()) { |
| 204 | TimeInstant timeOld = oldNote.getTime(); |
| 205 | TimeInstant timeNew = newNote.getTime(); |
| 206 | if (this == current()) { |
| 207 | if (timeOld == presentTime()) |
| 208 | sendTraceNote("reschedules " + newNote.getEvent() + " for itself, which was scheduled now, to " + timeNew); |
| 209 | else |
| 210 | sendTraceNote("reschedules " + newNote.getEvent() + " for itself, which was scheduled at " + timeOld + ", to " + timeNew); |
| 211 | } else { |
| 212 | if (timeOld == presentTime()) |
| 213 | sendTraceNote("reschedules " + newNote.getEvent() + " for " + this.getName() + ", which was scheduled now, to " + timeNew); |
| 214 | else |
| 215 | sendTraceNote("reschedules " + newNote.getEvent() + " for " + this.getName() + ", which was scheduled at " + timeOld + ", to " + timeNew); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | } |
| 220 | |
| 221 | //insert temp to schedule |
| 222 | for (EventNote ev : newNotes) |
| 223 | { |
| 224 | // insert to GLOBAL list, which inserts to local one |
| 225 | this.getModel().getExperiment().getScheduler().evList.insert(ev); |
| 226 | } |
| 227 | |
| 228 | } |
| 229 | |
| 230 | /** |
| 231 | * Re-schedules the Schedulable to some other point in simulation time |
| 232 | * (which should be different form the instant at which is a scheduled |
| 233 | * at the moment). |
| 234 | * |
| 235 | * @param time |
| 236 | * TimeInstant : The simulation time at which |
| 237 | * this entity is to be re-scheduled |
| 238 | */ |
| 239 | public void reSchedule(TimeInstant time) { |
| 240 | |
| 241 | if (time == null) { |
| 242 | sendWarning("Can't reSchedule enitty! Command ingnored.", |
| 243 | "Entity : " + getName() |
| 244 | + " Method: reSchedule(TimeInstant time)", |
| 245 | "The simulation time given as parameter is a null " |
| 246 | + "reference.", |
| 247 | "Be sure to have a valid TimeSpan reference before calling " |
| 248 | + "this method."); |
| 249 | return; // no proper parameter |
| 250 | } |
| 251 | |
| 252 | if (!isScheduled()) { |
| 253 | sendWarning("Can't reSchedule Schedulable! Command ingnored.", |
| 254 | "Entity : " + getName() |
| 255 | + " Method: reSchedule(TimeInstant time)", |
| 256 | "The Entity is not scheduled, thus unable to be " |
| 257 | + "reScheduled.", |
| 258 | "Be sure that the entity is currently scheduled " |
| 259 | + "before calling this method."); |
| 260 | return; // no proper parameter |
| 261 | } |
| 262 | |
| 263 | if (_schedule.size()>1) { |
| 264 | sendWarning("Can't reSchedule Entity! Command ingnored.", |
| 265 | "Entity : " + getName() |
| 266 | + " Method: reSchedule(TimeInstant time)", |
| 267 | "The Entity is scheduled more then once, thus unable to be " |
| 268 | + "reScheduled.", |
| 269 | "Be sure that the entity is only scheduled once" |
| 270 | + "before calling this method."); |
| 271 | return; // no proper parameter |
| 272 | } |
| 273 | |
| 274 | if (TimeInstant.isBefore(time, this.presentTime())) { |
| 275 | sendWarning( |
| 276 | "Can't reschedule Entity at given time! " |
| 277 | + "Command ignored.", |
| 278 | "Entity : " |
| 279 | + getName() |
| 280 | + " Method: reSchedule(TimeInstant time)", |
| 281 | "The instant given is in the past.", |
| 282 | "To reschedule an entity, use a TimeInstant no earlier than the present time. " |
| 283 | + "The present time can be obtained using the " |
| 284 | + "presentTime() method"); |
| 285 | return; |
| 286 | // I can't be rescheduled, TimeInstant has already passed. |
| 287 | } |
| 288 | |
| 289 | if (currentlySendTraceNotes()) { |
| 290 | if (this == current()) { |
| 291 | if (time == presentTime()) |
| 292 | sendTraceNote("reschedules itself now"); |
| 293 | else |
| 294 | sendTraceNote("reschedules itself at " |
| 295 | + time); |
| 296 | } else { |
| 297 | if (time == presentTime()) |
| 298 | sendTraceNote("reschedules '" + getName() + "' now"); |
| 299 | else |
| 300 | sendTraceNote("reschedules '" + getName() + "' at " |
| 301 | + time); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | getModel().getExperiment().getScheduler().reSchedule(this, time); |
| 306 | |
| 307 | if (currentlySendDebugNotes()) { |
| 308 | sendDebugNote("reschedules on EventList<br>" |
| 309 | + getModel().getExperiment().getScheduler().toString()); |
| 310 | } |
| 311 | |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * @deprecated Use reSchedule(TimeSpan dt). |
| 316 | * Re-schedules the Schedulable at some other point in simulation time than |
| 317 | * it already had been scheduled before. |
| 318 | * |
| 319 | * @param dt |
| 320 | * TimeSpan : The offset to the current simulation time at which |
| 321 | * this Schedulable is to be re-scheduled |
| 322 | */ |
| 323 | public void reSchedule(SimTime dt) { |
| 324 | |
| 325 | reSchedule(SimTime.toTimeSpan(dt)); |
| 326 | |
| 327 | } |
| 328 | |
| 329 | /** |
| 330 | * @deprecated Returns the point of simulation time this Schedulable is scheduled at. |
| 331 | * Returns null, if the entity has not been scheduled and thus can not return an |
| 332 | * appropriate point of simulation time. |
| 333 | * |
| 334 | * Use scheduleNext() for next TimeInstant where an event-note lists this entity. |
| 335 | * |
| 336 | * @return TimeInstant : The point of simulation time this Schedulable is |
| 337 | * scheduled at or <code>null</code> otherwise |
| 338 | */ |
| 339 | public TimeInstant scheduledAt() { |
| 340 | |
| 341 | return scheduledNext(); //use scheduleNext() |
| 342 | |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Returns the next point of time this entity is scheduled. |
| 347 | * |
| 348 | * @return TimeInstant : The point of simulation time the next Entity |
| 349 | * is schedule or <code>null</code> otherwise |
| 350 | */ |
| 351 | public TimeInstant scheduledNext() |
| 352 | { |
| 353 | if (!isScheduled()) |
| 354 | { |
| 355 | return null; // if not scheduled, there is no point of time |
| 356 | } |
| 357 | else |
| 358 | { |
| 359 | return _schedule.get(0).getTime(); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | |
| 364 | /** |
| 365 | * Adds an event-note to the Schedulable. |
| 366 | */ |
| 367 | void addEventNote(EventNote note) |
| 368 | { |
| 369 | if (!_schedule.contains(note)) { |
| 370 | _schedule.add(note); |
| 371 | java.util.Collections.sort(_schedule); |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | /** |
| 376 | * Creates and returns a copy of this Schedulable. |
| 377 | * Note that subclasses have to implement the interface |
| 378 | * </code>java.lang.Cloneable</code> to actually use this method as |
| 379 | * otherwise, a </code>CloneNotSupportedException</code> will be thrown. |
| 380 | * |
| 381 | * @return Schedulable : A copy of this Schedulable. |
| 382 | */ |
| 383 | protected Schedulable clone() throws CloneNotSupportedException { |
| 384 | Schedulable c = (Schedulable) super.clone(); |
| 385 | NameCatalog nc = this.getModel().getExperiment().getNameCatalog(); |
| 386 | c.rename(nc.getNameWithoutSuffix(this.getName())); |
| 387 | c._schedule = new LinkedList<EventNote>(); |
| 388 | return c; |
| 389 | } |
| 390 | } |