package traffic3.simulator; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.awt.Color; import javax.swing.JComponent; import traffic3.objects.TrafficArea; import traffic3.objects.TrafficBlockade; import traffic3.objects.TrafficAgent; import traffic3.manager.TrafficManager; import rescuecore2.GUIComponent; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.ChangeSet; import rescuecore2.worldmodel.WorldModelListener; import rescuecore2.worldmodel.WorldModel; import rescuecore2.messages.Command; import rescuecore2.messages.control.KSUpdate; import rescuecore2.messages.control.KSCommands; import rescuecore2.log.Logger; import rescuecore2.misc.geometry.Point2D; import rescuecore2.standard.entities.Area; import rescuecore2.standard.entities.Blockade; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.PoliceForce; import rescuecore2.standard.entities.AmbulanceTeam; import rescuecore2.standard.entities.Civilian; import rescuecore2.standard.entities.FireBrigade; import rescuecore2.standard.entities.Edge; import rescuecore2.standard.entities.Refuge; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.standard.entities.StandardEntityURN; import rescuecore2.standard.messages.AKMove; import rescuecore2.standard.messages.AKLoad; import rescuecore2.standard.messages.AKUnload; import rescuecore2.standard.messages.AKRescue; import rescuecore2.standard.messages.AKClear; import rescuecore2.standard.messages.AKExtinguish; import rescuecore2.standard.components.StandardSimulator; import org.uncommons.maths.random.GaussianGenerator; import org.uncommons.maths.number.NumberGenerator; /** The Area model traffic simulator. */ public class TrafficSimulator extends StandardSimulator implements GUIComponent { private static final double STEP_TIME_MS = 100; private static final double REAL_TIME_S = 60; private static final int MICROSTEPS = (int)((1000.0 / STEP_TIME_MS) * REAL_TIME_S); private static final int RESCUE_AGENT_RADIUS = 500;//the size of agents bing private static final int CIVILIAN_RADIUS = 200; private static final double RESCUE_AGENT_VELOCITY_MEAN = 0.7; private static final double RESCUE_AGENT_VELOCITY_SD = 0.1; private static final double CIVILIAN_VELOCITY_MEAN = 0.2; private static final double CIVILIAN_VELOCITY_SD = 0.002; private static final Color FIRE_BRIGADE_COLOUR = Color.RED; private static final Color POLICE_FORCE_COLOUR = Color.BLUE; private static final Color AMBULANCE_TEAM_COLOUR = Color.WHITE; private static final Color CIVILIAN_COLOUR = Color.GREEN; private TrafficSimulatorGUI gui; private TrafficManager manager; /** Construct a new TrafficSimulator. */ public TrafficSimulator() { manager = new TrafficManager(); gui = new TrafficSimulatorGUI(manager); } @Override public JComponent getGUIComponent() { return gui; } @Override public String getGUIComponentName() { return "Traffic simulator"; } @Override protected void postConnect() { TrafficConstants.init(config); manager.clear(); for (StandardEntity next : model) { if (next instanceof Area) { convertAreaToTrafficArea((Area)next); } } NumberGenerator<Double> agentVelocityGenerator = new GaussianGenerator(RESCUE_AGENT_VELOCITY_MEAN, RESCUE_AGENT_VELOCITY_SD, config.getRandom()); NumberGenerator<Double> civilianVelocityGenerator = new GaussianGenerator(CIVILIAN_VELOCITY_MEAN, CIVILIAN_VELOCITY_SD, config.getRandom()); for (StandardEntity next : model) { if (next instanceof Human) { convertHuman((Human)next, agentVelocityGenerator, civilianVelocityGenerator); } if (next instanceof Blockade) { convertBlockade((Blockade)next); } } model.addWorldModelListener(new WorldModelListener<StandardEntity>() { @Override public void entityAdded(WorldModel<? extends StandardEntity> model, StandardEntity e) { if (e instanceof Blockade) { convertBlockade((Blockade)e); } } @Override public void entityRemoved(WorldModel<? extends StandardEntity> model, StandardEntity e) { if (e instanceof Blockade) { Blockade b = (Blockade)e; TrafficBlockade block = manager.getTrafficBlockade(b); block.getArea().removeBlockade(block); manager.remove(block); } } }); gui.initialise(); manager.cacheInformation(model); } @Override protected void processCommands(KSCommands c, ChangeSet changes) { long start = System.currentTimeMillis(); Logger.info("Timestep " + c.getTime()); // Clear all cached blockade information for (TrafficArea next : manager.getAreas()) { next.clearBlockadeCache(); } // Clear all destinations and position history for (TrafficAgent agent : manager.getAgents()) { agent.clearPath(); agent.clearPositionHistory(); agent.setMobile(true); } for (Command next : c.getCommands()) { if (next instanceof AKMove) { handleMove((AKMove)next); } if (next instanceof AKLoad) { handleLoad((AKLoad)next, changes); } if (next instanceof AKUnload) { handleUnload((AKUnload)next, changes); } if (next instanceof AKRescue) { handleRescue((AKRescue)next, changes); } if (next instanceof AKClear) { handleClear((AKClear)next, changes); } if (next instanceof AKExtinguish) { handleExtinguish((AKExtinguish)next, changes); } } // Any agents that are dead or in ambulances are immobile // Civilians that are injured are immobile // Agents that are buried are immobile // Civilians in refuges are immobile for (StandardEntity next : model) { if (next instanceof Human) { Human h = (Human)next; if (h.isHPDefined() && h.getHP() <= 0) { Logger.debug("Agent " + h + " is dead"); manager.getTrafficAgent(h).setMobile(false); } if (h.isPositionDefined() && (model.getEntity(h.getPosition()) instanceof AmbulanceTeam)) { Logger.debug("Agent " + h + " is in an ambulance"); manager.getTrafficAgent(h).setMobile(false); } if (h.isBuriednessDefined() && h.getBuriedness() > 0) { Logger.debug("Agent " + h + " is buried"); manager.getTrafficAgent(h).setMobile(false); } if (h instanceof Civilian && h.isDamageDefined() && h.getDamage() > 0) { Logger.debug("Agent " + h + " is injured"); manager.getTrafficAgent(h).setMobile(false); } if (h instanceof Civilian && h.isPositionDefined() && (model.getEntity(h.getPosition()) instanceof Refuge)) { Logger.debug("Agent " + h + " is in a refuge"); manager.getTrafficAgent(h).setMobile(false); } } } timestep(); for (TrafficAgent agent : manager.getAgents()) { // Update position and positionHistory for agents that were not loaded or unloaded Human human = agent.getHuman(); if (!agent.isMobile()) { human.undefinePositionHistory(); human.setTravelDistance(0); changes.addChange(human, human.getPositionHistoryProperty()); changes.addChange(human, human.getTravelDistanceProperty()); continue; } Point2D[] history = agent.getPositionHistory().toArray(new Point2D[0]); int[] historyArray = new int[history.length * 2]; for (int i = 0; i < history.length; ++i) { historyArray[i * 2] = (int)history[i].getX(); historyArray[(i * 2) + 1] = (int)history[i].getY(); } double x = agent.getX(); double y = agent.getY(); TrafficArea location = agent.getArea(); if (location != null) { human.setPosition(location.getArea().getID()); // Logger.debug(human + " new position: " + human.getPosition()); changes.addChange(human, human.getPositionProperty()); } human.setX((int)x); human.setY((int)y); human.setPositionHistory(historyArray); human.setTravelDistance((int)agent.getTravelDistance()); changes.addChange(human, human.getXProperty()); changes.addChange(human, human.getYProperty()); changes.addChange(human, human.getPositionHistoryProperty()); changes.addChange(human, human.getTravelDistanceProperty()); } long end = System.currentTimeMillis(); Logger.info("Timestep " + c.getTime() + " took " + (end - start) + " ms"); } @Override protected void handleUpdate(KSUpdate u) { super.handleUpdate(u); } private void convertAreaToTrafficArea(Area area) { manager.register(new TrafficArea(area)); } private void convertBlockade(Blockade b) { Logger.debug("Converting blockade: " + b.getFullDescription()); Area a = (Area)model.getEntity(b.getPosition()); Logger.debug("Area: " + a); TrafficArea area = manager.getTrafficArea(a); Logger.debug("Traffic area: " + area); TrafficBlockade block = new TrafficBlockade(b, area); manager.register(block); area.addBlockade(block); } private void convertHuman(Human h, NumberGenerator<Double> agentVelocityGenerator, NumberGenerator<Double> civilianVelocityGenerator) { double radius = 0; double velocityLimit = 0; if (h instanceof FireBrigade || h instanceof PoliceForce || h instanceof AmbulanceTeam) { radius = RESCUE_AGENT_RADIUS; velocityLimit = agentVelocityGenerator.nextValue(); } else if (h instanceof Civilian) { radius = CIVILIAN_RADIUS; velocityLimit = civilianVelocityGenerator.nextValue(); } else { throw new IllegalArgumentException("Unrecognised agent type: " + h + " (" + h.getClass().getName() + ")"); } TrafficAgent agent = new TrafficAgent(h, manager, radius, velocityLimit); agent.setLocation(h.getX(), h.getY()); manager.register(agent); } private void handleMove(AKMove move) { Human human = (Human)model.getEntity(move.getAgentID()); TrafficAgent agent = manager.getTrafficAgent(human); EntityID current = human.getPosition(); if (current == null) { Logger.warn("Rejecting move: Agent position is not defined"); return; } Entity currentEntity = model.getEntity(human.getPosition()); if (!(currentEntity instanceof Area)) { Logger.warn("Rejecting move: Agent position is not an area: " + currentEntity); return; } Area currentArea = (Area)currentEntity; List<EntityID> list = move.getPath(); List<PathElement> steps = new ArrayList<PathElement>(); // Check that all elements refer to Area instances and build the list of target points // Target points between areas are the midpoint of the shared edge for (Iterator<EntityID> it = list.iterator(); it.hasNext();) { EntityID next = it.next(); if (next.equals(current)) { continue; } Entity e = model.getEntity(next); if (!(e instanceof Area)) { Logger.warn("Rejecting move: Entity ID " + next + " is not an area: " + e); return; } Edge edge = currentArea.getEdgeTo(next); if (edge == null) { Logger.warn("Rejecting move: Entity ID " + next + " is not adjacent to " + currentArea); return; } Area a = (Area)e; int x = (edge.getStartX() + edge.getEndX()) / 2; int y = (edge.getStartY() + edge.getEndY()) / 2; Point2D edgePoint = new Point2D(x, y); Point2D centrePoint = new Point2D(currentArea.getX(), currentArea.getY()); steps.add(new PathElement(next, edge.getLine(), edgePoint, centrePoint)); current = next; currentArea = a; } int targetX = move.getDestinationX(); int targetY = move.getDestinationY(); if (targetX == -1 && targetY == -1) { targetX = currentArea.getX(); targetY = currentArea.getY(); } else if (list.isEmpty()) { Logger.warn("Rejecting move: Path is empty"); return; } steps.add(new PathElement(current, null, new Point2D(targetX, targetY))); agent.setPath(steps); } // Return the loaded civilian (if any) private Civilian handleLoad(AKLoad load, ChangeSet changes) { EntityID agentID = load.getAgentID(); EntityID targetID = load.getTarget(); Entity agent = model.getEntity(agentID); Entity target = model.getEntity(targetID); if (agent == null) { Logger.warn("Rejecting load command from agent " + agentID + ": agent does not exist"); return null; } if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting load command from agent " + agentID + ": agent type is " + agent.getURN()); return null; } if (target == null) { Logger.warn("Rejecting load command from agent " + agentID + ": target does not exist " + targetID); return null; } if (!(target instanceof Civilian)) { Logger.warn("Rejecting load command from agent " + agentID + ": target " + targetID + " is of type " + target.getURN()); return null; } AmbulanceTeam at = (AmbulanceTeam)agent; Civilian h = (Civilian)target; if (at.isHPDefined() && at.getHP() <= 0) { Logger.warn("Rejecting load command from agent " + agentID + ": agent is dead"); return null; } if (at.isBuriednessDefined() && at.getBuriedness() > 0) { Logger.warn("Rejecting load command from agent " + agentID + ": agent is buried"); return null; } if (h.isBuriednessDefined() && h.getBuriedness() > 0) { Logger.warn("Rejecting load command from agent " + agentID + ": target " + targetID + " is buried"); return null; } if (!h.isPositionDefined() || !at.isPositionDefined() || !h.getPosition().equals(at.getPosition())) { Logger.warn("Rejecting load command from agent " + agentID + ": target is non-adjacent " + targetID); return null; } if (h.getID().equals(at.getID())) { Logger.warn("Rejecting load command from agent " + agentID + ": tried to load self"); return null; } // Is there something already loaded? for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) { Civilian c = (Civilian)e; if (c.isPositionDefined() && agentID.equals(c.getPosition())) { Logger.warn("Rejecting load command from agent " + agentID + ": agent already has civilian " + c.getID() + " loaded"); return null; } } // All checks passed: do the load h.setPosition(agentID); h.undefineX(); h.undefineY(); changes.addChange(h, h.getPositionProperty()); changes.addChange(h, h.getXProperty()); changes.addChange(h, h.getYProperty()); manager.getTrafficAgent(at).setMobile(false); manager.getTrafficAgent(h).setMobile(false); Logger.debug(at + " loaded " + h); return h; } // Return the unloaded civilian (if any) private Civilian handleUnload(AKUnload unload, ChangeSet changes) { EntityID agentID = unload.getAgentID(); Entity agent = model.getEntity(agentID); if (agent == null) { Logger.warn("Rejecting unload command from agent " + agentID + ": agent does not exist"); return null; } if (!(agent instanceof AmbulanceTeam)) { Logger.warn("Rejecting unload command from agent " + agentID + ": agent type is " + agent.getURN()); return null; } AmbulanceTeam at = (AmbulanceTeam)agent; if (!at.isPositionDefined() || !at.isXDefined() || !at.isYDefined()) { Logger.warn("Rejecting unload command from agent " + agentID + ": could not locate agent"); return null; } if (at.isHPDefined() && at.getHP() <= 0) { Logger.warn("Rejecting unload command from agent " + agentID + ": agent is dead"); return null; } if (at.isBuriednessDefined() && at.getBuriedness() > 0) { Logger.warn("Rejecting unload command from agent " + agentID + ": agent is buried"); return null; } // Is there something loaded? Civilian target = null; Logger.debug("Looking for civilian carried by " + agentID); for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) { Civilian c = (Civilian)e; Logger.debug(c + " is at " + c.getPosition()); if (c.isPositionDefined() && agentID.equals(c.getPosition())) { target = c; Logger.debug("Found civilian " + c); break; } } if (target == null) { Logger.warn("Rejecting unload command from agent " + agentID + ": agent is not carrying any civilians"); return null; } // All checks passed target.setPosition(at.getPosition()); target.setX(at.getX()); target.setY(at.getY()); changes.addChange(target, target.getPositionProperty()); changes.addChange(target, target.getXProperty()); changes.addChange(target, target.getYProperty()); for (TrafficAgent trafficAgent : manager.getAgents()) { if (trafficAgent.getHuman() == target) { trafficAgent.setLocation(at.getX(), at.getY()); trafficAgent.clearPath(); } } manager.getTrafficAgent(at).setMobile(false); manager.getTrafficAgent(target).setMobile(false); Logger.debug(at + " unloaded " + target); return target; } private void handleClear(AKClear clear, ChangeSet changes) { // Agents clearing roads are not mobile EntityID agentID = clear.getAgentID(); Entity agent = model.getEntity(agentID); if (agent instanceof Human) { manager.getTrafficAgent((Human)agent).setMobile(false); Logger.debug(agent + " is clearing"); } } private void handleRescue(AKRescue rescue, ChangeSet changes) { // Agents rescueing civilians are not mobile EntityID agentID = rescue.getAgentID(); Entity agent = model.getEntity(agentID); if (agent instanceof Human) { manager.getTrafficAgent((Human)agent).setMobile(false); Logger.debug(agent + " is rescueing"); } } private void handleExtinguish(AKExtinguish ex, ChangeSet changes) { // Agents extinguishing fires are not mobile EntityID agentID = ex.getAgentID(); Entity agent = model.getEntity(agentID); if (agent instanceof Human) { manager.getTrafficAgent((Human)agent).setMobile(false); Logger.debug(agent + " is extinguishing"); } } private void timestep() { long start = System.currentTimeMillis(); for (TrafficAgent agent : manager.getAgents()) { agent.beginTimestep(); } long pre = System.currentTimeMillis(); Logger.debug("Running " + MICROSTEPS + " microsteps"); for (int i = 0; i < MICROSTEPS; i++) { microstep(); } long post = System.currentTimeMillis(); for (TrafficAgent agent : manager.getAgents()) { agent.endTimestep(); } long end = System.currentTimeMillis(); if (manager.getAgents().size() != 0) { Logger.debug("Pre-timestep took " + (pre - start) + " ms (average " + ((pre - start) / manager.getAgents().size()) + "ms per agent)"); Logger.debug("Microsteps took: " + (post - pre) + "ms (average " + ((post - pre) / MICROSTEPS) + "ms)"); Logger.debug("Post-timestep took " + (end - post) + " ms (average " + ((end - post) / manager.getAgents().size()) + "ms per agent)"); } Logger.debug("Total time: " + (end - start) + "ms"); } private void microstep() { for (TrafficAgent agent : manager.getAgents()) { agent.step(STEP_TIME_MS); } gui.refresh(); } }