/* * Copyright 2010 Aalto University, ComNet * Released under GPLv3. See LICENSE.txt for details. */ package core; import input.EventQueue; import input.ExternalEvent; import input.ScheduledUpdatesQueue; import interfaces.ConnectivityGrid; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; /** * World contains all the nodes and is responsible for updating their * location and connections. */ public class World { /** namespace of optimization settings ({@value})*/ public static final String SETTINGS_NS = "Optimization"; /** * Cell based optimization cell size multiplier -setting id ({@value}). * Single ConnectivityCell's size is the biggest radio range times this. * Larger values save memory and decrease startup time but may result in * slower simulation. * Default value is {@link #DEF_CON_CELL_SIZE_MULT}. * Smallest accepted value is 2. * @see ConnectivityGrid */ public static final String CELL_SIZE_MULT_S = "cellSizeMult"; /** * Should the order of node updates be different (random) within every * update step -setting id ({@value}). Boolean (true/false) variable. * Default is @link {@link #DEF_RANDOMIZE_UPDATES}. */ public static final String RANDOMIZE_UPDATES_S = "randomizeUpdateOrder"; /** default value for cell size multiplier ({@value}) */ public static final int DEF_CON_CELL_SIZE_MULT = 5; /** should the update order of nodes be randomized -setting's default value * ({@value}) */ public static final boolean DEF_RANDOMIZE_UPDATES = true; private int sizeX; private int sizeY; protected List eventQueues; protected double updateInterval; protected SimClock simClock; protected double nextQueueEventTime; protected EventQueue nextEventQueue; /** list of nodes; nodes are indexed by their network address */ public List hosts; protected boolean simulateConnections; /** nodes in the order they should be updated (if the order should be * randomized; null value means that the order should not be randomized) */ protected ArrayList updateOrder; /** is cancellation of simulation requested from UI */ protected boolean isCancelled; protected List updateListeners; /** Queue of scheduled update requests */ protected ScheduledUpdatesQueue scheduledUpdates; /** single ConnectivityCell's size is biggest radio range times this */ private int conCellSizeMult; /** * Constructor. */ public World(List hosts, int sizeX, int sizeY, double updateInterval, List updateListeners, boolean simulateConnections, List eventQueues) { this.hosts = hosts; this.sizeX = sizeX; this.sizeY = sizeY; this.updateInterval = updateInterval; this.updateListeners = updateListeners; this.simulateConnections = simulateConnections; this.eventQueues = eventQueues; this.simClock = SimClock.getInstance(); this.scheduledUpdates = new ScheduledUpdatesQueue(); this.isCancelled = false; setNextEventQueue(); initSettings(); } /** * Initializes settings fields that can be configured using Settings class */ private void initSettings() { Settings s = new Settings(SETTINGS_NS); boolean randomizeUpdates = DEF_RANDOMIZE_UPDATES; if (s.contains(RANDOMIZE_UPDATES_S)) { randomizeUpdates = s.getBoolean(RANDOMIZE_UPDATES_S); } if(randomizeUpdates) { // creates the update order array that can be shuffled this.updateOrder = new ArrayList(this.hosts); } else { // null pointer means "don't randomize" this.updateOrder = null; } if (s.contains(CELL_SIZE_MULT_S)) { conCellSizeMult = s.getInt(CELL_SIZE_MULT_S); } else { conCellSizeMult = DEF_CON_CELL_SIZE_MULT; } // check that values are within limits if (conCellSizeMult < 2) { throw new SettingsError("Too small value (" + conCellSizeMult + ") for " + SETTINGS_NS + "." + CELL_SIZE_MULT_S); } } /** * Moves hosts in the world for the time given time initialize host * positions properly. SimClock must be set to -time before * calling this method. * @param time The total time (seconds) to move */ public void warmupMovementModel(double time) { if (time <= 0) { return; } while(SimClock.getTime() < -updateInterval) { moveHosts(updateInterval); simClock.advance(updateInterval); } double finalStep = -SimClock.getTime(); moveHosts(finalStep); simClock.setTime(0); } /** * Goes through all event Queues and sets the * event queue that has the next event. */ public void setNextEventQueue() { EventQueue nextQueue = scheduledUpdates; double earliest = nextQueue.nextEventsTime(); /* find the queue that has the next event */ for (EventQueue eq : eventQueues) { if (eq.nextEventsTime() < earliest){ nextQueue = eq; earliest = eq.nextEventsTime(); } } this.nextEventQueue = nextQueue; this.nextQueueEventTime = earliest; } /** * Update (move, connect, disconnect etc.) all hosts in the world. * Runs all external events that are due between the time when * this method is called and after one update interval. */ public void update () { double runUntil = SimClock.getTime() + this.updateInterval; setNextEventQueue(); /* process all events that are due until next interval update */ while (this.nextQueueEventTime <= runUntil) { simClock.setTime(this.nextQueueEventTime); ExternalEvent ee = this.nextEventQueue.nextEvent(); ee.processEvent(this); updateHosts(); // update all hosts after every event setNextEventQueue(); } moveHosts(this.updateInterval); simClock.setTime(runUntil); updateHosts(); /* inform all update listeners */ for (UpdateListener ul : this.updateListeners) { ul.updated(this.hosts); } } /** * Updates all hosts (calls update for every one of them). If update * order randomizing is on (updateOrder array is defined), the calls * are made in random order. */ public void updateHosts() { if (this.updateOrder == null) { // randomizing is off for (int i=0, n = hosts.size();i < n; i++) { if (this.isCancelled) { break; } hosts.get(i).update(simulateConnections); } } else { // update order randomizing is on assert this.updateOrder.size() == this.hosts.size() : "Nrof hosts has changed unexpectedly"; Random rng = new Random(SimClock.getIntTime()); Collections.shuffle(this.updateOrder, rng); for (int i=0, n = hosts.size();i < n; i++) { if (this.isCancelled) { break; } this.updateOrder.get(i).update(simulateConnections); } } } /** * Moves all hosts in the world for a given amount of time * @param timeIncrement The time how long all nodes should move */ private void moveHosts(double timeIncrement) { for (int i=0,n = hosts.size(); i getHosts() { return this.hosts; } /** * Returns the x-size (width) of the world * @return the x-size (width) of the world */ public int getSizeX() { return this.sizeX; } /** * Returns the y-size (height) of the world * @return the y-size (height) of the world */ public int getSizeY() { return this.sizeY; } /** * Returns a node from the world by its address * @param address The address of the node * @return The requested node or null if it wasn't found */ public DTNHost getNodeByAddress(int address) { if (address < 0 || address >= hosts.size()) { throw new SimError("No host for address " + address + ". Address " + "range of 0-" + (hosts.size()-1) + " is valid"); } DTNHost node = this.hosts.get(address); assert node.getAddress() == address : "Node indexing failed. " + "Node " + node + " in index " + address; return node; } /** * Schedules an update request to all nodes to happen at the specified * simulation time. * @param simTime The time of the update */ public void scheduleUpdate(double simTime) { scheduledUpdates.addUpdate(simTime); } }