diff --git a/modules/clear/src/clear/ClearSimulator.java b/modules/clear/src/clear/ClearSimulator.java new file mode 100644 index 0000000000000000000000000000000000000000..34687f47c77020fb8f01335b7f0ccaa1ed11dd06 --- /dev/null +++ b/modules/clear/src/clear/ClearSimulator.java @@ -0,0 +1,377 @@ +package clear; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import rescuecore2.log.Logger; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.messages.AKClear; +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +/** + * The area model clear simulator. This simulator processes AKClear messages. + */ +public class ClearSimulator extends StandardSimulator { + + private static final String SIMULATOR_NAME = "Area Model Clear Simulator"; + + private static final String REPAIR_RATE_KEY = "clear.repair.rate"; + private static final String REPAIR_RAD_KEY = "clear.repair.rad"; + private static final String REPAIR_DISTANCE_KEY = "clear.repair.distance"; + + // Converts square mm to square m. + private static final double REPAIR_COST_FACTOR = 0.000001; + + private int repairRate; + private int repairRadius; + private int repairDistance; + + @Override + public String getName() { + return SIMULATOR_NAME; + } + + + @Override + protected void postConnect() { + this.repairRate = config.getIntValue(REPAIR_RATE_KEY); + this.repairRadius = config.getIntValue(REPAIR_RAD_KEY); + this.repairDistance = config.getIntValue(REPAIR_DISTANCE_KEY); + } + + + @Override + protected void processCommands(KSCommands c, ChangeSet changes) { + long start = System.currentTimeMillis(); + int time = c.getTime(); + Logger.info("Timestep " + time); + Map<Blockade, Integer> partiallyCleared = new HashMap<>(); + Set<EntityID> cleared = new HashSet<>(); + for (Command command : c.getCommands()) { + if ((command instanceof AKClear clear) && isValid(clear, cleared)) { + Logger.debug("Processing " + clear); + EntityID blockadeID = clear.getTarget(); + Blockade blockade = (Blockade) model.getEntity(blockadeID); + Area area = (Area) model.getEntity(blockade.getPosition()); + int cost = blockade.getRepairCost(); + Logger.debug("Blockade repair cost: " + cost); + Logger.debug("Blockade repair rate: " + this.repairRate); + if (this.repairRate >= cost) { + // Remove the blockade entirely + List<EntityID> ids = new ArrayList<>(area.getBlockades()); + ids.remove(blockadeID); + area.setBlockades(ids); + model.removeEntity(blockadeID); + changes.addChange(area, area.getBlockadesProperty()); + changes.entityDeleted(blockadeID); + partiallyCleared.remove(blockade); + cleared.add(blockadeID); + Logger.debug("Cleared " + blockade); + } else { + // Update the repair cost + if (!partiallyCleared.containsKey(blockade)) { + partiallyCleared.put(blockade, cost); + } + cost -= this.repairRate; + blockade.setRepairCost(cost); + changes.addChange(blockade, blockade.getRepairCostProperty()); + } + } else if ((command instanceof AKClearArea clearArea) + && (isValid(clearArea))) { + processClearArea(clearArea, changes); + Logger.debug("Processing " + clearArea); + } + } + // Shrink partially cleared blockades + for (Map.Entry<Blockade, Integer> next : partiallyCleared.entrySet()) { + Blockade b = next.getKey(); + double original = next.getValue(); + double current = b.getRepairCost(); + // d is the new size relative to the old size + double d = current / original; + Logger.debug("Partially cleared " + b); + Logger.debug("Original repair cost: " + original); + Logger.debug("New repair cost: " + current); + Logger.debug("Proportion left: " + d); + int[] apexes = b.getApexes(); + double cx = b.getX(); + double cy = b.getY(); + // Move each apex towards the centre + for (int i = 0; i < apexes.length; i += 2) { + double x = apexes[i]; + double y = apexes[i + 1]; + double dx = x - cx; + double dy = y - cy; + // Shift both x and y so they are now d * dx from the centre + double newX = cx + (dx * d); + double newY = cy + (dy * d); + apexes[i] = (int) newX; + apexes[i + 1] = (int) newY; + } + b.setApexes(apexes); + changes.addChange(b, b.getApexesProperty()); + } + long end = System.currentTimeMillis(); + Logger.info("Timestep " + time + " took " + (end - start) + " ms"); + } + + + private void processClearArea(AKClearArea clear, ChangeSet changes) { + PoliceForce agent = (PoliceForce) model.getEntity(clear.getAgentID()); + int targetX = clear.getDestinationX(); + int targetY = clear.getDestinationY(); + + int length = this.repairDistance; + + Map<Blockade, java.awt.geom.Area> blockades = new HashMap<>(); + for (StandardEntity entity : model.getObjectsInRange(agent.getX(), + agent.getY(), length)) { + if ((entity instanceof Area area) && (area.isBlockadesDefined())) { + for (EntityID blockadeID : area.getBlockades()) { + Blockade blockade = (Blockade) model.getEntity(blockadeID); + if (blockade != null) { + if (blockade.getShape() == null) { + Logger.debug("Blockade Shape is null"); + } + blockades.put(blockade, + new java.awt.geom.Area(blockade.getShape())); + } + } + } + } + + int counter = 0; + int min = 0; + int max = 2 * length; + while (true) { + counter++; + length = (min + max) / 2; + java.awt.geom.Area area = Geometry.getClearArea(agent, targetX, targetY, + length, this.repairRadius); + + double firstSurface = Geometry.surface(area); + for (java.awt.geom.Area blockade : blockades.values()) + area.subtract(blockade); + double surface = Geometry.surface(area); + double clearedSurface = firstSurface - surface; + + if ((clearedSurface * REPAIR_COST_FACTOR) > this.repairRate) { + max = length; + } else if ((counter != 1) && (counter < 15) && ((max - min) > 5)) { + min = length; + } else { + break; + } + } + + java.awt.geom.Area area = Geometry.getClearArea(agent, targetX, targetY, + length, this.repairRadius); + for (Map.Entry<Blockade, java.awt.geom.Area> entry : blockades.entrySet()) { + Blockade blockade = entry.getKey(); + java.awt.geom.Area blockadeArea = entry.getValue(); + Road road = (Road) model.getEntity(blockade.getPosition()); + double firstSurface = Geometry.surface(blockadeArea); + blockadeArea.subtract(area); + double surface = Geometry.surface(blockadeArea); + if (surface < firstSurface) { + changes.addChange(blockade, blockade.getApexesProperty()); + List<int[]> areas = Geometry.getAreas(blockadeArea); + if (areas.size() == 1) { + Blockade backupBlockade = blockade; + blockade = updateBlockadeApexes(blockade, areas.get(0)); + if (blockade == null) { + blockade = backupBlockade; + areas.clear(); + } else { + changes.addChange(blockade, blockade.getApexesProperty()); + changes.addChange(blockade, blockade.getXProperty()); + changes.addChange(blockade, blockade.getYProperty()); + changes.addChange(blockade, blockade.getRepairCostProperty()); + } + } + if (areas.size() != 1) { + try { + List<EntityID> newIDs = requestNewEntityIDs(areas.size()); + Iterator<EntityID> it = newIDs.iterator(); + List<Blockade> newBlockades = new ArrayList<>(); + if (!areas.isEmpty()) + Logger.debug("Creating new blockade objects for " + + blockade.getID().getValue() + " " + areas.size()); + for (int[] apexes : areas) { + EntityID id = it.next(); + Blockade b = makeBlockade(id, apexes, road.getID()); + if (b != null) + newBlockades.add(b); + } + List<EntityID> existing = road.getBlockades(); + List<EntityID> ids = new ArrayList<>(); + if (existing != null) + ids.addAll(existing); + for (Blockade b : newBlockades) { + ids.add(b.getID()); + } + ids.remove(blockade.getID()); + road.setBlockades(ids); + changes.addAll(newBlockades); + + model.removeEntity(blockade.getID()); + changes.addChange(road, road.getBlockadesProperty()); + changes.entityDeleted(blockade.getID()); + } catch (InterruptedException e) { + Logger.error("Interrupted while requesting IDs"); + } + } + } + } + } + + + private Blockade updateBlockadeApexes(Blockade blockade, int[] apexes) { + List<Point2D> points = GeometryTools2D.vertexArrayToPoints(apexes); + if (points.size() >= 2) { + Point2D centroid = GeometryTools2D.computeCentroid(points); + blockade.setApexes(apexes); + blockade.setX((int) centroid.getX()); + blockade.setY((int) centroid.getY()); + int cost = (int) (GeometryTools2D.computeArea(points) + * REPAIR_COST_FACTOR); + if (cost != 0) { + blockade.setRepairCost(cost); + return blockade; + } + } + + return null; + } + + + private Blockade makeBlockade(EntityID id, int[] apexes, EntityID roadID) { + Blockade blockade = new Blockade(id); + blockade.setPosition(roadID); + return updateBlockadeApexes(blockade, apexes); + } + + + private boolean isValid(AKClear clear, Set<EntityID> cleared) { + // Check Target + StandardEntity target = model.getEntity(clear.getTarget()); + if (target == null) { + Logger + .info("Rejecting clear command " + clear + ": target does not exist"); + return false; + } else if (cleared.contains(clear.getTarget())) { + Logger.info("Ignoring clear command " + clear + + ": target already cleared in this timestep"); + return false; + } else if (!(target instanceof Blockade)) { + Logger.info( + "Rejecting clear command " + clear + ": target is not a blockade"); + return false; + } + + // Check Agent + StandardEntity agent = model.getEntity(clear.getAgentID()); + if (agent == null) { + Logger + .info("Rejecting clear command " + clear + ": agent does not exist"); + return false; + } else if (!(agent instanceof PoliceForce)) { + Logger.info( + "Rejecting clear command " + clear + ": agent is not a PoliceForce"); + return false; + } + + // Check PoliceForce + PoliceForce police = (PoliceForce) agent; + StandardEntity agentPosition = police.getPosition(model); + if (agentPosition == null) { + Logger.info( + "Rejecting clear command " + clear + ": could not locate the agent"); + return false; + } else if (!police.isHPDefined() || police.getHP() <= 0) { + Logger.info("Rejecting clear command " + clear + ": agent is dead"); + return false; + } else if (police.isBuriednessDefined() && police.getBuriedness() > 0) { + Logger.info("Rejecting clear command " + clear + ": agent is buried"); + return false; + } + + // Check Blockade + Blockade targetBlockade = (Blockade) target; + if (!targetBlockade.isPositionDefined()) { + Logger.info( + "Rejecting clear command " + clear + ": blockade position undefined"); + return false; + } else if (!targetBlockade.isRepairCostDefined()) { + Logger.info( + "Rejecting clear command " + clear + ": blockade has no repair cost"); + return false; + } + + // Check Any Blockade to Clear + Point2D agentLocation = new Point2D(police.getX(), police.getY()); + double bestDistance = Double.MAX_VALUE; + for (Line2D line : GeometryTools2D.pointsToLines( + GeometryTools2D.vertexArrayToPoints(targetBlockade.getApexes()), + true)) { + Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, + agentLocation); + double distance = GeometryTools2D.getDistance(agentLocation, closest); + if (distance < this.repairDistance) { + return true; + } else if (bestDistance > distance) { + bestDistance = distance; + } + } + Logger.info("Rejecting clear command " + clear + + ": agent is not adjacent to a target: closest blockade is " + + bestDistance); + return false; + } + + + private boolean isValid(AKClearArea clear) { + StandardEntity agent = model.getEntity(clear.getAgentID()); + if (agent == null) { + Logger + .info("Rejecting clear command " + clear + ": agent does not exist"); + return false; + } else if (!(agent instanceof PoliceForce)) { + Logger.info("Rejecting clear command " + clear + + ": agent is not a police officer"); + return false; + } + + // Check PoliceForce + PoliceForce police = (PoliceForce) agent; + StandardEntity agentPosition = police.getPosition(model); + if (!(agentPosition instanceof Area)) { + Logger.info( + "Rejecting clear command " + clear + " : could not locate agent"); + return false; + } else if (!police.isHPDefined() || police.getHP() <= 0) { + Logger.info("Rejecting clear command " + clear + " : agent is dead"); + return false; + } else if (police.isBuriednessDefined() && police.getBuriedness() > 0) { + Logger.info("Rejecting clear command " + clear + " : agent is buried"); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/modules/clear/src/clear/Geometry.java b/modules/clear/src/clear/Geometry.java new file mode 100644 index 0000000000000000000000000000000000000000..69953e337eaa8baef2072f343d15f8cd45926e2b --- /dev/null +++ b/modules/clear/src/clear/Geometry.java @@ -0,0 +1,99 @@ +package clear; + +import java.awt.Polygon; +import java.awt.geom.Area; +import java.awt.geom.PathIterator; +import java.util.ArrayList; +import java.util.List; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.standard.entities.Human; + +public class Geometry { + + public static Area getClearArea(Human agent, int targetX, int targetY, + int clearLength, int clearRad) { + Vector2D agentToTarget = new Vector2D(targetX - agent.getX(), + targetY - agent.getY()); + + if (agentToTarget.getLength() > clearLength) + agentToTarget = agentToTarget.normalised().scale(clearLength); + agentToTarget = agentToTarget.normalised() + .scale(agentToTarget.getLength() + 510); + + Vector2D backAgent = (new Vector2D(agent.getX(), agent.getY())) + .add(agentToTarget.normalised().scale(-510)); + Line2D line = new Line2D(backAgent.getX(), backAgent.getY(), + agentToTarget.getX(), agentToTarget.getY()); + + Vector2D dir = agentToTarget.normalised().scale(clearRad); + Vector2D perpend1 = new Vector2D(-dir.getY(), dir.getX()); + Vector2D perpend2 = new Vector2D(dir.getY(), -dir.getX()); + + rescuecore2.misc.geometry.Point2D points[] = new rescuecore2.misc.geometry.Point2D[] { + line.getOrigin().plus(perpend1), line.getEndPoint().plus(perpend1), + line.getEndPoint().plus(perpend2), line.getOrigin().plus(perpend2)}; + int[] xPoints = new int[points.length]; + int[] yPoints = new int[points.length]; + for (int i = 0; i < points.length; i++) { + xPoints[i] = (int) points[i].getX(); + yPoints[i] = (int) points[i].getY(); + } + return new Area(new Polygon(xPoints, yPoints, points.length)); + } + + + public static double surface(Area area) { + PathIterator iter = area.getPathIterator(null); + + double sum_all = 0; + while (!iter.isDone()) { + List<double[]> points = new ArrayList<double[]>(); + while (!iter.isDone()) { + double point[] = new double[2]; + int type = iter.currentSegment(point); + iter.next(); + if (type == PathIterator.SEG_CLOSE) { + if (points.size() > 0) + points.add(points.get(0)); + break; + } + points.add(point); + } + + double sum = 0; + for (int i = 0; i < points.size() - 1; i++) + sum += points.get(i)[0] * points.get(i + 1)[1] + - points.get(i)[1] * points.get(i + 1)[0]; + + sum_all += Math.abs(sum) / 2; + } + + return sum_all; + } + + + public static List<int[]> getAreas(Area area) { + PathIterator iter = area.getPathIterator(null); + List<int[]> areas = new ArrayList<int[]>(); + ArrayList<Integer> list = new ArrayList<Integer>(); + while (!iter.isDone()) { + double point[] = new double[2]; // x, y + int type = iter.currentSegment(point); + if (type == PathIterator.SEG_CLOSE) { + if (list.size() > 0) { + int[] newArea = new int[list.size()]; + for (int i = 0; i < list.size(); i++) + newArea[i] = list.get(i); + areas.add(newArea); + list = new ArrayList<Integer>(); + } + } else { + list.add((int) point[0]); + list.add((int) point[1]); + } + iter.next(); + } + return areas; + } +} \ No newline at end of file diff --git a/modules/collapse/src/collapse/CSBuilding.java b/modules/collapse/src/collapse/CSBuilding.java new file mode 100644 index 0000000000000000000000000000000000000000..65ef6534b8d27dd305ae7f0c95dfe6a91a07cf60 --- /dev/null +++ b/modules/collapse/src/collapse/CSBuilding.java @@ -0,0 +1,89 @@ +package collapse; + +import rescuecore2.standard.entities.Building; + +/** + * Collapse Simulator Building (CSBuilding) is a wrapper for the Standard + * Building class that contains extra variables created, updated and used by the + * Collapse Simulator only. This class is created in order to prevented + * unnecessary changes to the Standard Building class. + * + * @author Salim + * + */ +public class CSBuilding { + /** + * The reference to the real building class + */ + private final Building real; + + /** + * Collapse Ratio shows the percent that the building has been collapsed so + * far. + */ + private float collapsedRatio = 0; + /** + * This shows whether the building has fire damage in the last cycle or not + */ + private boolean hasFireDamage = false; + + public CSBuilding(Building building) { + real = building; + } + + /** + * Returns the building's collapse ratio + * + * @return + */ + public float getCollapsedRatio() { + return collapsedRatio; + } + + /** + * Changes the collapse ratio of the building to the input ratio + * + * @param collapsedRatio + * is a float + */ + + public void setCollapsedRatio(float collapsedRatio) { + this.collapsedRatio = collapsedRatio; + } + + /** + * Adds the input ratio to the building's collapse ratio + * + * @param ratio + * is a float that represents the increased value of the collapse + * ratio + */ + public void increaseCollapseRatio(float ratio) { + setCollapsedRatio(getCollapsedRatio() + ratio); + + } + + public Building getReal() { + return real; + } + + /** + * Returns the extent that is still possible to collapse. + * + * @return a float representing the extent + */ + public double getRemainingToCollapse(double floorHeight) { + return floorHeight * real.getFloors() * (1 - getCollapsedRatio()); + } + + public boolean hasFireDamage() { + return false; + } + + public void setHasFireDamage(boolean hasFireDamage) { + this.hasFireDamage = hasFireDamage; + } + public double getTotalCollapse(double floorHeight){ + return floorHeight*real.getFloors(); + } +} diff --git a/modules/collapse/src/collapse/CollapseSimulator.java b/modules/collapse/src/collapse/CollapseSimulator.java new file mode 100755 index 0000000000000000000000000000000000000000..3ce6fa189bd8526692032d5d61be7b8bbf80698c --- /dev/null +++ b/modules/collapse/src/collapse/CollapseSimulator.java @@ -0,0 +1,864 @@ +package collapse; + +import rescuecore2.config.Config; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.KSAfterShocksInfo; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModelListener; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.log.Logger; +import rescuecore2.GUIComponent; + +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardEntityConstants; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.Edge; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.StandardWorldModel; + +import org.uncommons.maths.random.GaussianGenerator; +import org.uncommons.maths.random.ContinuousUniformGenerator; +import org.uncommons.maths.number.NumberGenerator; +import org.uncommons.maths.Maths; + +import java.util.Map; +import java.util.HashMap; +import java.util.HashSet; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; + +import java.awt.geom.Path2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.PathIterator; + +import javax.swing.JComponent; + +/** + * A simple collapse simulator. + */ +public class CollapseSimulator extends StandardSimulator implements + GUIComponent { + private static final String CONFIG_PREFIX = "collapse."; + private static final String DESTROYED_SUFFIX = ".p-destroyed"; + private static final String SEVERE_SUFFIX = ".p-severe"; + private static final String MODERATE_SUFFIX = ".p-moderate"; + private static final String SLIGHT_SUFFIX = ".p-slight"; + private static final String NONE_SUFFIX = ".p-none"; + + private static final String DESTROYED_MEAN_SUFFIX = "destroyed.mean"; + private static final String DESTROYED_SD_SUFFIX = "destroyed.sd"; + private static final String SEVERE_MEAN_SUFFIX = "severe.mean"; + private static final String SEVERE_SD_SUFFIX = "severe.sd"; + private static final String MODERATE_MEAN_SUFFIX = "moderate.mean"; + private static final String MODERATE_SD_SUFFIX = "moderate.sd"; + private static final String SLIGHT_MEAN_SUFFIX = "slight.mean"; + private static final String SLIGHT_SD_SUFFIX = "slight.sd"; + + private static final String BLOCK_KEY = "collapse.create-road-blockages"; + + private static final String FLOOR_HEIGHT_KEY = "collapse.floor-height"; + private static final String WALL_COLLAPSE_EXTENT_MIN_KEY = "collapse.wall-extent.min"; + private static final String WALL_COLLAPSE_EXTENT_MAX_KEY = "collapse.wall-extent.max"; + + /* Aftershock Requirment 2013 */ + private static final String RANDOM_AFTERSHOCK = "collapse.aftershock.random"; + + /* Aftershock Requirment 2013 */ + // enum IntensityType { + // ON_DAMAGE, ON_DISTANCE; + // public String toString() { + // if (this.equals(ON_DAMAGE)) + // return "damage"; + // else + // return "distance"; + // }; + // + // } + enum CollapsePolicy { + MERGE_BLOCKADES, DONT_MERGE_BLOCKADES; + public String toString() { + if (this.equals(MERGE_BLOCKADES)) + return "merge"; + else + return "dont_merge"; + }; + } + + CollapsePolicy policy = CollapsePolicy.DONT_MERGE_BLOCKADES; + + private static final int MAX_COLLAPSE = 100; + + private static final double REPAIR_COST_FACTOR = 0.000001; // Converts + // square mm to + // square m. + + public static final String NAME = "Collapse Simulator v1.1"; + + private static final List<EntityID> EMPTY_ID_LIST = new ArrayList<EntityID>( + 0); + + private NumberGenerator<Double> destroyed; + private NumberGenerator<Double> severe; + private NumberGenerator<Double> moderate; + private NumberGenerator<Double> slight; + + private boolean block; + + private double floorHeight; + private NumberGenerator<Double> extent; + + private Map<StandardEntityConstants.BuildingCode, CollapseStats> stats; + + private CollapseSimulatorGUI gui; + private Collection<Building> buildingCache; + private Collection<Road> roadCache; + + public CollapseSimulator() { + } + + /** + * This method instantiate a CollapseWorldModel instance. + */ + @Override + protected StandardWorldModel createWorldModel() { + return new CollapseWorldModel();/* Aftershocks Requirement:2013 */ + } + + @Override + public JComponent getGUIComponent() { + if (gui == null) { + gui = new CollapseSimulatorGUI(); + } + return gui; + } + + @Override + public String getGUIComponentName() { + return "Collapse simulator"; + } + + @Override + public String getName() { + return NAME; + } + + @Override + protected void postConnect() { + super.postConnect(); + stats = new EnumMap<StandardEntityConstants.BuildingCode, CollapseStats>( + StandardEntityConstants.BuildingCode.class); + for (StandardEntityConstants.BuildingCode code : StandardEntityConstants.BuildingCode + .values()) { + stats.put(code, new CollapseStats(code, config)); + } + slight = new GaussianGenerator(config.getFloatValue(CONFIG_PREFIX + + SLIGHT_MEAN_SUFFIX), config.getFloatValue(CONFIG_PREFIX + + SLIGHT_SD_SUFFIX), config.getRandom()); + moderate = new GaussianGenerator(config.getFloatValue(CONFIG_PREFIX + + MODERATE_MEAN_SUFFIX), config.getFloatValue(CONFIG_PREFIX + + MODERATE_SD_SUFFIX), config.getRandom()); + severe = new GaussianGenerator(config.getFloatValue(CONFIG_PREFIX + + SEVERE_MEAN_SUFFIX), config.getFloatValue(CONFIG_PREFIX + + SEVERE_SD_SUFFIX), config.getRandom()); + destroyed = new GaussianGenerator(config.getFloatValue(CONFIG_PREFIX + + DESTROYED_MEAN_SUFFIX), config.getFloatValue(CONFIG_PREFIX + + DESTROYED_SD_SUFFIX), config.getRandom()); + block = config.getBooleanValue(BLOCK_KEY); + floorHeight = config.getFloatValue(FLOOR_HEIGHT_KEY) * 1000; + extent = new ContinuousUniformGenerator( + config.getFloatValue(WALL_COLLAPSE_EXTENT_MIN_KEY), + config.getFloatValue(WALL_COLLAPSE_EXTENT_MAX_KEY), + config.getRandom()); + buildingCache = new HashSet<Building>(); + roadCache = new HashSet<Road>(); + for (StandardEntity next : model) { + if (next instanceof Building) { + buildingCache.add((Building) next); + } + if (next instanceof Road) { + roadCache.add((Road) next); + } + } + model.addWorldModelListener(new WorldModelListener<StandardEntity>() { + @Override + public void entityAdded(WorldModel<? extends StandardEntity> model, + StandardEntity e) { + if (e instanceof Building) { + buildingCache.add((Building) e); + } + if (e instanceof Road) { + roadCache.add((Road) e); + } + } + + @Override + public void entityRemoved( + WorldModel<? extends StandardEntity> model, StandardEntity e) { + if (e instanceof Building) { + buildingCache.remove((Building) e); + } + if (e instanceof Road) { + roadCache.remove((Road) e); + } + } + }); + } + + @Override + protected void processCommands(KSCommands c, ChangeSet changes) { + long start = System.currentTimeMillis(); + int time = c.getTime(); + Logger.info("Timestep " + time); + if (gui != null) { + gui.timestep(time); + } + Collection<Building> collapsed = doCollapse(changes, time); + Map<Road, Collection<java.awt.geom.Area>> newBlock = doBlock(collapsed, + time); + // Create blockade objects + Map<Road, Collection<Blockade>> blockades = createBlockadeObjects(newBlock); + for (Map.Entry<Road, Collection<Blockade>> entry : blockades.entrySet()) { + Road r = entry.getKey(); + List<EntityID> existing = r.getBlockades(); + List<EntityID> ids = new ArrayList<EntityID>(); + if (existing != null) { + ids.addAll(existing); + } + for (Blockade b : entry.getValue()) { + ids.add(b.getID()); + } + r.setBlockades(ids); + changes.addAll(entry.getValue()); + changes.addChange(r, r.getBlockadesProperty()); + } + // If any roads have undefined blockades then set the blockades property + // to the empty list + for (Road next : roadCache) { + if (!next.isBlockadesDefined()) { + next.setBlockades(EMPTY_ID_LIST); + changes.addChange(next, next.getBlockadesProperty()); + } + } + long end = System.currentTimeMillis(); + Logger.info("Timestep " + time + " took " + (end - start) + " ms"); + } + + private Collection<Building> doCollapse(ChangeSet changes, int time) { + Collection<Building> result = new HashSet<Building>(); + if (gui != null) { + gui.startCollapse(buildingCache.size()); + } + if (time == 1) { + result.addAll(doEarthquakeCollapse(changes)); + } + /* Aftershocks Requirement:2013 */ + if (time != 1 && model().aftershockHappens(time)) { + result.addAll(doEarthquakeCollapse(changes)); + } + if (gui != null) { + gui.endCollapse(); + } + if (gui != null) { + gui.startFire(buildingCache.size()); + } + // result.addAll(doFireCollapse(changes)); + if (gui != null) { + gui.endFire(); + } + return result; + } + + static long t1; + static long t2; + static long t3; + + private Map<Road, Collection<java.awt.geom.Area>> doBlock( + Collection<Building> collapsed, int time) { + Map<Road, Collection<java.awt.geom.Area>> result = new LazyMap<Road, Collection<java.awt.geom.Area>>() { + @Override + public Collection<java.awt.geom.Area> createValue() { + return new ArrayList<java.awt.geom.Area>(); + } + }; + if (!block) { + return result; + } + if (gui != null) { + gui.startBlock(collapsed.size()); + } + for (Building b : collapsed) { + createBlockages(b, result, time); + if (gui != null) { + gui.bumpBlock(); + } + } + if (gui != null) { + gui.endBlock(); + } + return result; + } + + private Collection<Building> doEarthquakeCollapse(ChangeSet changes) { + Map<StandardEntityConstants.BuildingCode, Map<CollapseDegree, Integer>> count = new EnumMap<StandardEntityConstants.BuildingCode, Map<CollapseDegree, Integer>>( + StandardEntityConstants.BuildingCode.class); + Map<StandardEntityConstants.BuildingCode, Integer> total = new EnumMap<StandardEntityConstants.BuildingCode, Integer>( + StandardEntityConstants.BuildingCode.class); + + for (StandardEntityConstants.BuildingCode code : StandardEntityConstants.BuildingCode + .values()) { + Map<CollapseDegree, Integer> next = new EnumMap<CollapseDegree, Integer>( + CollapseDegree.class); + for (CollapseDegree cd : CollapseDegree.values()) { + next.put(cd, 0); + } + count.put(code, next); + total.put(code, 0); + } + + Logger.debug("Collapsing buildings"); + Collection<Building> result = new HashSet<Building>(); + for (Building b : buildingCache) { + StandardEntityConstants.BuildingCode code = b.getBuildingCodeEnum(); + int damage = code == null ? 0 : stats.get(code).damage(); + damage = Maths.restrictRange(damage, 0, MAX_COLLAPSE); + int lastDamage = b.getBrokenness(); + /* + * Aftershock Requirement 2013: this ignores the new damage of it is + * less than the previous + */ + if (damage < lastDamage) { + damage = lastDamage; + } + /* Aftershock Requirement 2013 */ + b.setBrokenness(damage); + changes.addChange(b, b.getBrokennessProperty()); + + CollapseDegree degree = CollapseDegree.get(damage); + count.get(code).put(degree, count.get(code).get(degree) + 1); + total.put(code, total.get(code) + 1); + + if (damage > 0) { + result.add(b); + } + if (gui != null) { + gui.bumpCollapse(); + } + } + Logger.info("Finished collapsing buildings: "); + for (StandardEntityConstants.BuildingCode code : StandardEntityConstants.BuildingCode + .values()) { + Logger.info("Building code " + code + ": " + total.get(code) + + " buildings"); + Map<CollapseDegree, Integer> data = count.get(code); + for (Map.Entry<CollapseDegree, Integer> entry : data.entrySet()) { + Logger.info(" " + entry.getValue() + " " + + entry.getKey().toString().toLowerCase()); + } + } + return result; + } + + private Collection<Building> doFireCollapse(ChangeSet changes) { + Logger.debug("Checking fire damage"); + Collection<Building> result = new HashSet<Building>(); + for (Building b : buildingCache) { + /* + * Aftershock Requirement @ 2103: it is used in doBlock to check + * whether apply the aftershock intensity or not + */ + model().getCSBuiding(b).setHasFireDamage(false); + + if (!b.isFierynessDefined()) { + if (gui != null) { + gui.bumpFire(); + } + continue; + } + int minDamage = 0; + switch (b.getFierynessEnum()) { + case HEATING: + minDamage = slight.nextValue().intValue(); + break; + case BURNING: + minDamage = moderate.nextValue().intValue(); + break; + case INFERNO: + minDamage = severe.nextValue().intValue(); + break; + case BURNT_OUT: + minDamage = destroyed.nextValue().intValue(); + break; + default: + break; + } + /* Aftershock Requirement @ 2103 */ + if (minDamage != 0) { + // indicates that the building has fire damage + model().getCSBuiding(b).setHasFireDamage(true); + } + minDamage = Maths.restrictRange(minDamage, 0, MAX_COLLAPSE); + int damage = b.isBrokennessDefined() ? b.getBrokenness() : 0; + if (damage < minDamage) { + Logger.info(b + " damaged by fire. New brokenness: " + + minDamage); + b.setBrokenness(minDamage); + changes.addChange(b, b.getBrokennessProperty()); + result.add(b); + } + if (gui != null) { + gui.bumpFire(); + } + } + Logger.debug("Finished checking fire damage"); + return result; + } + + private Map<Road, Collection<Blockade>> createBlockadeObjects( + Map<Road, Collection<java.awt.geom.Area>> blocks) { + Map<Road, Collection<Blockade>> result = new LazyMap<Road, Collection<Blockade>>() { + @Override + public Collection<Blockade> createValue() { + return new ArrayList<Blockade>(); + } + }; + int count = 0; + for (Collection<java.awt.geom.Area> c : blocks.values()) { + count += c.size(); + } + try { + if (count != 0) { + List<EntityID> newIDs = requestNewEntityIDs(count); + Iterator<EntityID> it = newIDs.iterator(); + Logger.debug("Creating new blockade objects"); + for (Map.Entry<Road, Collection<java.awt.geom.Area>> entry : blocks + .entrySet()) { + Road r = entry.getKey(); + for (java.awt.geom.Area area : entry.getValue()) { + EntityID id = it.next(); + Blockade blockade = makeBlockade(id, area, r.getID()); + if (blockade != null) { + result.get(r).add(blockade); + } + } + } + } + } catch (InterruptedException e) { + Logger.error("Interrupted while requesting IDs"); + } + return result; + } + + private void createBlockages(Building b, + Map<Road, Collection<java.awt.geom.Area>> roadBlockages, int time) { + long t = System.currentTimeMillis(); + Logger.debug("Creating blockages for " + b); + /* + * Aftershock's Requirement@2103: this checks the ratio of the + * building's floors that is possible to collapsed + */ + double remainingfloors = model().getCSBuiding(b) + .getRemainingToCollapse(floorHeight); + double currentExtent = extent.nextValue(); + double damage = b.getBrokenness(); + /* + * Aftershock Requirement 2013: applies the intensity of the aftershock. + * This only applies if there is an aftershock and time is >1 and the + * building doesn't have any fire damage. In case of any fire damage the + * building creates the max possible collapse. + */ + if (time != 1 && model().aftershockHappens(time) + && !model().getCSBuiding(b).hasFireDamage()) { + float intensity = model().aftershockIntensity(time); + damage = (int) ((float) damage * Math.min(1, intensity)); + } + /* Aftershock's Requirement: 2013 */ + double d = remainingfloors * (damage / (double) MAX_COLLAPSE) + * currentExtent; + t1 += System.currentTimeMillis() - t; + t = System.currentTimeMillis(); + /* Aftershock's Requirement: 2013 */ + model().getCSBuiding(b).increaseCollapseRatio( + (float) (d / model().getCSBuiding(b).getTotalCollapse( + floorHeight))); + // Place some blockages on surrounding roads + List<java.awt.geom.Area> wallAreas = new ArrayList<java.awt.geom.Area>(); + // Project each wall out and build a list of wall areas + for (Edge edge : b.getEdges()) { + projectWall(edge, wallAreas, d); + } + java.awt.geom.Area fullArea = new java.awt.geom.Area(); + for (java.awt.geom.Area wallArea : wallAreas) { + fullArea.add(wallArea); + } + + /* + * new ShapeDebugFrame().show("Collapsed building", new + * ShapeDebugFrame.AWTShapeInfo(b.getShape(), "Original building area", + * Color.RED, true), new ShapeDebugFrame.AWTShapeInfo(fullArea, + * "Expanded building area (d = " + d + ")", Color.BLACK, false) ); + */ + // Find existing blockade areas + java.awt.geom.Area existing = new java.awt.geom.Area(); + if (policy.equals(CollapsePolicy.MERGE_BLOCKADES)) { + for (StandardEntity e : model + .getEntitiesOfType(StandardEntityURN.BLOCKADE)) { + Blockade blockade = (Blockade) e; + existing.add(blockadeToArea(blockade)); + } + } + t2 += System.currentTimeMillis() - t; + // Intersect wall areas with roads + t = System.currentTimeMillis(); + Map<Road, Collection<java.awt.geom.Area>> blockadesForRoads = createRoadBlockades( + fullArea, existing); + t3 += System.currentTimeMillis() - t; + // Add to roadBlockages + for (Map.Entry<Road, Collection<java.awt.geom.Area>> entry : blockadesForRoads + .entrySet()) { + Road r = entry.getKey(); + Collection<java.awt.geom.Area> c = entry.getValue(); + roadBlockages.get(r).addAll(c); + } + } + + private void projectWall(Edge edge, + Collection<java.awt.geom.Area> areaList, double d) { + Line2D wallLine = new Line2D(edge.getStartX(), edge.getStartY(), + edge.getEndX() - edge.getStartX(), edge.getEndY() + - edge.getStartY()); + Vector2D wallDirection = wallLine.getDirection(); + Vector2D offset = wallDirection.getNormal().normalised().scale(-d); + Path2D path = new Path2D.Double(); + + Point2D right = wallLine.getOrigin(); + Point2D left = wallLine.getEndPoint(); + + Point2D first = left.plus(offset.scale(-1)); + Point2D second = right.plus(offset.scale(-1)); + Point2D third = right.plus(offset); + Point2D fourth = left.plus(offset); + + path.moveTo(first.getX(), first.getY()); + path.lineTo(second.getX(), second.getY()); + path.lineTo(third.getX(), third.getY()); + path.lineTo(fourth.getX(), fourth.getY()); + path.closePath(); + + java.awt.geom.Area wallArea = new java.awt.geom.Area(path); + areaList.add(wallArea); + // Also add circles at each corner + double radius = offset.getLength(); + Ellipse2D ellipse1 = new Ellipse2D.Double(right.getX() - radius, + right.getY() - radius, radius * 2, radius * 2); + Ellipse2D ellipse2 = new Ellipse2D.Double(left.getX() - radius, + left.getY() - radius, radius * 2, radius * 2); + areaList.add(new java.awt.geom.Area(ellipse1)); + areaList.add(new java.awt.geom.Area(ellipse2)); + // Logger.info("Edge from " + wallLine + " expanded to " + first + ", " + // + second + ", " + third + ", " + fourth); + // debug.show("Collapsed building", + // new ShapeDebugFrame.AWTShapeInfo(buildingArea, + // "Original building area", Color.RED, true), + // new ShapeDebugFrame.Line2DShapeInfo(wallLine, "Wall edge", + // Color.WHITE, true, true), + // new ShapeDebugFrame.AWTShapeInfo(wallArea, "Wall area (d = " + d + + // ")", Color.GREEN, false), + // new ShapeDebugFrame.AWTShapeInfo(ellipse1, "Ellipse 1", Color.BLUE, + // false), + // new ShapeDebugFrame.AWTShapeInfo(ellipse2, "Ellipse 2", Color.ORANGE, + // false) + // ); + } + + private Map<Road, Collection<java.awt.geom.Area>> createRoadBlockades( + java.awt.geom.Area buildingArea, java.awt.geom.Area existing) { + Map<Road, Collection<java.awt.geom.Area>> result = new HashMap<Road, Collection<java.awt.geom.Area>>(); + java.awt.Rectangle rectangle = buildingArea.getBounds(); + Collection<StandardEntity> roads = model.getObjectsInRectangle( + (int) rectangle.getMinX(), (int) rectangle.getMinY(), + (int) rectangle.getMaxX(), (int) rectangle.getMaxY()); + for (StandardEntity e : roads) { + if (!(e instanceof Road)) { + continue; + } + Road r = (Road) e; + java.awt.geom.Area roadArea = areaToGeomArea(r); + java.awt.geom.Area intersection = new java.awt.geom.Area(roadArea); + intersection.intersect(buildingArea); + intersection.subtract(existing); + if (intersection.isEmpty()) { + continue; + } + existing.add(intersection); + List<java.awt.geom.Area> blockadeAreas = fix(intersection); + result.put(r, blockadeAreas); + // debug.show("Road blockage", + // new ShapeDebugFrame.AWTShapeInfo(buildingArea, "Building area", + // Color.BLACK, false), + // new ShapeDebugFrame.AWTShapeInfo(roadArea, "Road area", + // Color.BLUE, false), + // new ShapeDebugFrame.AWTShapeInfo(intersection, "Intersection", + // Color.GREEN, true) + // ); + } + return result; + } + + private java.awt.geom.Area areaToGeomArea( + rescuecore2.standard.entities.Area area) { + Path2D result = new Path2D.Double(); + Iterator<Edge> it = area.getEdges().iterator(); + Edge e = it.next(); + result.moveTo(e.getStartX(), e.getStartY()); + result.lineTo(e.getEndX(), e.getEndY()); + while (it.hasNext()) { + e = it.next(); + result.lineTo(e.getEndX(), e.getEndY()); + } + return new java.awt.geom.Area(result); + } + + private List<java.awt.geom.Area> fix(java.awt.geom.Area area) { + List<java.awt.geom.Area> result = new ArrayList<java.awt.geom.Area>(); + if (area.isSingular()) { + result.add(area); + return result; + } + PathIterator it = area.getPathIterator(null); + Path2D current = null; + // CHECKSTYLE:OFF:MagicNumber + double[] d = new double[6]; + while (!it.isDone()) { + switch (it.currentSegment(d)) { + case PathIterator.SEG_MOVETO: + if (current != null) { + result.add(new java.awt.geom.Area(current)); + } + current = new Path2D.Double(); + current.moveTo(d[0], d[1]); + break; + case PathIterator.SEG_LINETO: + current.lineTo(d[0], d[1]); + break; + case PathIterator.SEG_QUADTO: + current.quadTo(d[0], d[1], d[2], d[3]); + break; + case PathIterator.SEG_CUBICTO: + current.curveTo(d[0], d[1], d[2], d[3], d[4], d[5]); + break; + case PathIterator.SEG_CLOSE: + current.closePath(); + break; + default: + throw new RuntimeException( + "Unexpected result from PathIterator.currentSegment: " + + it.currentSegment(d)); + } + it.next(); + } + // CHECKSTYLE:ON:MagicNumber + if (current != null) { + result.add(new java.awt.geom.Area(current)); + } + return result; + } + + private Blockade makeBlockade(EntityID id, java.awt.geom.Area area, + EntityID roadID) { + if (area.isEmpty()) { + return null; + } + Blockade result = new Blockade(id); + int[] apexes = getApexes(area); + List<Point2D> points = GeometryTools2D.vertexArrayToPoints(apexes); + if (points.size() < 2) { + return null; + } + int cost = (int) (GeometryTools2D.computeArea(points) * REPAIR_COST_FACTOR); + if (cost == 0) { + return null; + } + Point2D centroid = GeometryTools2D.computeCentroid(points); + result.setApexes(apexes); + result.setPosition(roadID); + result.setX((int) centroid.getX()); + result.setY((int) centroid.getY()); + result.setRepairCost((int) cost); + return result; + } + + private int[] getApexes(java.awt.geom.Area area) { + // Logger.debug("getApexes"); + List<Integer> apexes = new ArrayList<Integer>(); + // CHECKSTYLE:OFF:MagicNumber + PathIterator it = area.getPathIterator(null, 100); + double[] d = new double[6]; + int moveX = 0; + int moveY = 0; + int lastX = 0; + int lastY = 0; + boolean finished = false; + while (!finished && !it.isDone()) { + int x = 0; + int y = 0; + switch (it.currentSegment(d)) { + case PathIterator.SEG_MOVETO: + x = (int) d[0]; + y = (int) d[1]; + moveX = x; + moveY = y; + // Logger.debug("Move to " + x + ", " + y); + break; + case PathIterator.SEG_LINETO: + x = (int) d[0]; + y = (int) d[1]; + // Logger.debug("Line to " + x + ", " + y); + if (x == moveX && y == moveY) { + finished = true; + } + break; + case PathIterator.SEG_QUADTO: + x = (int) d[2]; + y = (int) d[3]; + // Logger.debug("Quad to " + x + ", " + y); + if (x == moveX && y == moveY) { + finished = true; + } + break; + case PathIterator.SEG_CUBICTO: + x = (int) d[4]; + y = (int) d[5]; + // Logger.debug("Cubic to " + x + ", " + y); + if (x == moveX && y == moveY) { + finished = true; + } + break; + case PathIterator.SEG_CLOSE: + // Logger.debug("Close"); + finished = true; + break; + default: + throw new RuntimeException( + "Unexpected result from PathIterator.currentSegment: " + + it.currentSegment(d)); + } + // Logger.debug(x + ", " + y); + if (!finished && (x != lastX || y != lastY)) { + apexes.add(x); + apexes.add(y); + } + lastX = x; + lastY = y; + it.next(); + } + // CHECKSTYLE:ON:MagicNumber + int[] result = new int[apexes.size()]; + int i = 0; + for (Integer next : apexes) { + result[i++] = next; + } + return result; + } + + private java.awt.geom.Area blockadeToArea(Blockade b) { + Path2D result = new Path2D.Double(); + int[] apexes = b.getApexes(); + result.moveTo(apexes[0], apexes[1]); + for (int i = 2; i < apexes.length; i += 2) { + result.lineTo(apexes[i], apexes[i + 1]); + } + result.closePath(); + return new java.awt.geom.Area(result); + } + + private class CollapseStats { + private double pDestroyed; + private double pSevere; + private double pModerate; + private double pSlight; + + CollapseStats(StandardEntityConstants.BuildingCode code, Config config) { + String s = CONFIG_PREFIX + code.toString().toLowerCase(); + pDestroyed = config.getFloatValue(s + DESTROYED_SUFFIX); + pSevere = pDestroyed + config.getFloatValue(s + SEVERE_SUFFIX); + pModerate = pSevere + config.getFloatValue(s + MODERATE_SUFFIX); + pSlight = pModerate + config.getFloatValue(s + SLIGHT_SUFFIX); + } + + int damage() { + double d = random.nextDouble(); + if (d < pDestroyed) { + return destroyed.nextValue().intValue(); + } + if (d < pSevere) { + return severe.nextValue().intValue(); + } + if (d < pModerate) { + return moderate.nextValue().intValue(); + } + if (d < pSlight) { + return slight.nextValue().intValue(); + } + return 0; + } + } + + private enum CollapseDegree { + NONE(0), SLIGHT(25), MODERATE(50), SEVERE(75), DESTROYED(100); + + private int max; + + private CollapseDegree(int max) { + this.max = max; + } + + public static CollapseDegree get(int d) { + for (CollapseDegree next : values()) { + if (d <= next.max) { + return next; + } + } + throw new IllegalArgumentException( + "Don't know what to do with a damage value of " + d); + } + } + + // /* Aftershocks Requirement:2013 */ + // /** + // * Handles KAAftershocksInfo and calls the parent processMessage. + // */ + // @Override + // protected void processMessage(Message msg) { + // + // } + + /* Aftershocks Requirement:2013 */ + public CollapseWorldModel model() { + return (CollapseWorldModel) model; + } + + @Override + protected void processMessage(Message msg) { + if (msg instanceof KSAfterShocksInfo) { + model().updateAftershocks((KSAfterShocksInfo) msg); + } else { + super.processMessage(msg); + } + } + + public static void main(String[] args) { + } +} diff --git a/modules/collapse/src/collapse/CollapseSimulatorGUI.java b/modules/collapse/src/collapse/CollapseSimulatorGUI.java new file mode 100755 index 0000000000000000000000000000000000000000..4afb0a8e3c93880f90175599b62b00965e8517a8 --- /dev/null +++ b/modules/collapse/src/collapse/CollapseSimulatorGUI.java @@ -0,0 +1,197 @@ +package collapse; + +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import java.awt.GridLayout; + +/** + GUI for the collapse simulator. +*/ +public class CollapseSimulatorGUI extends JPanel { + private JLabel timeLabel; + private JLabel statusLabel; + private JProgressBar collapseProgress; + private JProgressBar fireProgress; + private JProgressBar blockadeProgress; + + private int collapse; + private int fire; + private int block; + + /** + Construct a collapse simulator GUI. + */ + public CollapseSimulatorGUI() { + super(new GridLayout(0, 2)); + + timeLabel = new JLabel("Not started"); + statusLabel = new JLabel("Not started"); + collapseProgress = new JProgressBar(0, 1); + fireProgress = new JProgressBar(0, 1); + blockadeProgress = new JProgressBar(0, 1); + + collapseProgress.setStringPainted(true); + fireProgress.setStringPainted(true); + blockadeProgress.setStringPainted(true); + + add(new JLabel("Timestep")); + add(timeLabel); + add(new JLabel("Status")); + add(statusLabel); + add(new JLabel("Collapsing buildings")); + add(collapseProgress); + add(new JLabel("Fire damage")); + add(fireProgress); + add(new JLabel("Creating blockades")); + add(blockadeProgress); + } + + /** + Notify the gui that a new timestep has started. + @param time The timestep. + */ + void timestep(final int time) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + timeLabel.setText(String.valueOf(time)); + collapseProgress.setValue(0); + fireProgress.setValue(0); + blockadeProgress.setValue(0); + collapse = 0; + fire = 0; + block = 0; + } + }); + } + + /** + Notify the gui that collapse computation has begun. + @param buildingCount The number of buildings to process. + */ + void startCollapse(final int buildingCount) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Collapsing buildings"); + collapseProgress.setMaximum(buildingCount); + collapseProgress.setValue(0); + collapse = 0; + } + }); + } + + /** + Notify the gui that a building collapse has been processed. + */ + void bumpCollapse() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + collapseProgress.setValue(++collapse); + } + }); + } + + /** + Notify the gui that building collapse computation is complete. + */ + void endCollapse() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + collapseProgress.setValue(collapseProgress.getMaximum()); + } + }); + } + + /** + Notify the gui that fire collapse computation has begun. + @param buildingCount The number of buildings to process. + */ + void startFire(final int buildingCount) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Fire damage"); + fireProgress.setMaximum(buildingCount); + fireProgress.setValue(0); + fire = 0; + } + }); + } + + /** + Notify the gui that a fire collapse has been processed. + */ + void bumpFire() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fireProgress.setValue(++fire); + } + }); + } + + /** + Notify the gui that fire collapse computation is complete. + */ + void endFire() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + fireProgress.setValue(fireProgress.getMaximum()); + } + }); + } + + /** + Notify the gui that blockade generation has begun. + @param buildingCount The number of buildings to process. + */ + void startBlock(final int buildingCount) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Computing blockades"); + if (buildingCount == 0) { + blockadeProgress.setMaximum(1); + blockadeProgress.setValue(1); + } + else { + blockadeProgress.setMaximum(buildingCount); + blockadeProgress.setValue(0); + block = 0; + } + } + }); + } + + /** + Notify the gui that blockade generation for a building has been processed. + */ + void bumpBlock() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + blockadeProgress.setValue(++block); + } + }); + } + + /** + Notify the gui that blockade generation is complete. + */ + void endBlock() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + blockadeProgress.setValue(blockadeProgress.getMaximum()); + statusLabel.setText("Done"); + } + }); + } + +} \ No newline at end of file diff --git a/modules/collapse/src/collapse/CollapseWorldModel.java b/modules/collapse/src/collapse/CollapseWorldModel.java new file mode 100644 index 0000000000000000000000000000000000000000..6a12b23fc27d1fd78554c70294dbdaf40a84a6fd --- /dev/null +++ b/modules/collapse/src/collapse/CollapseWorldModel.java @@ -0,0 +1,92 @@ +package collapse; + + +import java.util.HashMap; + +import rescuecore2.messages.control.KSAfterShocksInfo; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +/** + * + * Collapse simulator's world model contains aftershocks' information that other + * simulators don't need. + * + * @author Salim + * + */ +public class CollapseWorldModel extends StandardWorldModel { + private HashMap<Integer, Float> aftershocks; + private HashMap<Building, CSBuilding> collapseBuildings; + + public CollapseWorldModel() { + this.aftershocks = new HashMap<Integer, Float>(); + collapseBuildings = new HashMap<Building, CSBuilding>(); + } + + @Override + public void merge(ChangeSet changes) { + super.merge(changes); + } + + /** + * Changes the list in the world model with the new input aftershock list + * + * @param msg + * instance of KSAfterShocksInfo + */ + public void updateAftershocks(KSAfterShocksInfo msg) { + aftershocks = msg.getAftershocks(); + } + + public boolean aftershockHappens(int time) { + return aftershocks.get(time) != null; + } + + public float aftershockIntensity(int time) { + return aftershocks.get(time); + } + + public HashMap<Building, CSBuilding> getCollapseBuildings() { + if (collapseBuildings.size() == 0) + createCollapseBuildings(); + return collapseBuildings; + } + + /** + * Creates Collapse Simulator Buildings using the Standard Buildings + */ + private void createCollapseBuildings() { + for (StandardEntity entity : this) { + if(entity instanceof Building) + collapseBuildings.put((Building) entity, new CSBuilding((Building) entity)); + } + + } + + /** + * Returns a specific CSBuilding by its EntityID + * + * @param id + * is an EntityID + * @return the corresponding CSBuilding to id + */ + public CSBuilding getCSBuiding(EntityID id) { + return getCollapseBuildings().get((Building) getEntity(id)); + } + + /** + * Returns a specific CSBuilding by its Building + * + * @param building + * is an Building + * @return the corresponding CSBuilding to building + */ + public CSBuilding getCSBuiding(Building building) { + return getCollapseBuildings().get(building); + } +} diff --git a/modules/gis2/src/gis2/GISServer.java b/modules/gis2/src/gis2/GISServer.java new file mode 100644 index 0000000000000000000000000000000000000000..a23cd479ffea96d2cfbc26bcfbc72aa7402a9e2b --- /dev/null +++ b/modules/gis2/src/gis2/GISServer.java @@ -0,0 +1,139 @@ +package gis2; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import org.apache.log4j.Logger; + +import rescuecore2.Constants; +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.connection.TCPConnection; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.GKConnectOK; +import rescuecore2.messages.control.KGConnect; +import rescuecore2.messages.control.Shutdown; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * This class is used for starting a remote GIS server. + */ +public final class GISServer { + private static final long WAIT_TIME = 1000; + + private ServerSocket server; + private WorldModel<? extends Entity> world; + private volatile boolean running; + + private static final Logger LOG = Logger.getLogger(GISServer.class); + + private GISServer(int port, WorldModel<? extends Entity> world) throws IOException { + server = new ServerSocket(port); + this.world = world; + running = true; + } + + /** + * Start the GIS server. + * + * @param args Command line arguments: <-c config file> + */ + public static void main(String[] args) { + Config config = new Config(); + try { + CommandLineOptions.processArgs(args, config); + int port = config.getIntValue(Constants.GIS_PORT_NUMBER_KEY, Constants.DEFAULT_GIS_PORT_NUMBER); + processJarFiles(config); + GMLWorldModelCreator creator = new GMLWorldModelCreator(); + new GISServer(port, creator.buildWorldModel(config)).run(); + LOG.info("GIS server listening on port " + port); + } catch (Exception e) { + LOG.fatal("Error starting GIS server", e); + } + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.process(); + } + + /** + * Run the GIS server. + */ + public void run() { + while (running) { + try { + Socket socket = server.accept(); + new ServerThread(socket).start(); + } catch (IOException e) { + LOG.error("Error accepting connection", e); + running = false; + } + } + } + + private class ServerThread extends Thread implements ConnectionListener { + private Socket socket; + private boolean dead; + + public ServerThread(Socket socket) { + this.socket = socket; + dead = false; + } + + @Override + public void run() { + TCPConnection c = null; + try { + c = new TCPConnection(socket); + } catch (IOException e) { + LOG.error("Error starting TCPConnection", e); + return; + } + c.startup(); + c.addConnectionListener(this); + synchronized (this) { + while (!dead) { + try { + this.wait(WAIT_TIME); + } catch (InterruptedException e) { + dead = true; + } + } + } + c.shutdown(); + } + + @Override + public void messageReceived(Connection c, Message msg) { + if (msg instanceof KGConnect) { + // Send a GKConnectOK + try { + c.sendMessage(new GKConnectOK(world.getAllEntities())); + } catch (ConnectionException e) { + LOG.fatal("Error sending message", e); + die(); + } + } + if (msg instanceof Shutdown) { + die(); + } + } + + private void die() { + synchronized (this) { + dead = true; + notifyAll(); + } + running = false; + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/GMLWorldModelCreator.java b/modules/gis2/src/gis2/GMLWorldModelCreator.java new file mode 100755 index 0000000000000000000000000000000000000000..d3e5b38e2583b49c23559dd39f9b0566bc3f05c0 --- /dev/null +++ b/modules/gis2/src/gis2/GMLWorldModelCreator.java @@ -0,0 +1,273 @@ +package gis2; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import kernel.KernelException; +import kernel.WorldModelCreator; +import maps.CoordinateConversion; +import maps.MapException; +import maps.MapReader; +import maps.ScaleConversion; +import maps.gml.GMLBuilding; +import maps.gml.GMLCoordinates; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLMap; +import maps.gml.GMLRoad; +import maps.gml.GMLShape; +import org.apache.log4j.Logger; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import rescuecore2.config.Config; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.scenario.exceptions.ScenarioException; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Edge; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +/** + * A WorldModelCreator that reads a GML file and scenario descriptor. + */ +public class GMLWorldModelCreator implements WorldModelCreator { + + private static final String MAP_DIRECTORY_KEY = "gis.map.dir"; + private static final String MAP_FILE_KEY = "gis.map.file"; + private static final String DEFAULT_MAP_FILE = "map.gml"; + private static final String SCENARIO_FILE_KEY = "gis.map.scenario"; + private static final String DEFAULT_SCENARIO_FILE = "scenario.xml"; + private static final String MAX_FLOOR = "gis.map.max-floor"; + private static final String FLOOR_PLACEMENT_TYPE = "gis.map.floor-placement.random"; + private static final String RANDOM_FLOOR_RATE = "gis.map.floor-placement.random.floor-rate."; + private static final String BUILDING_CODE_PLACEMENT_TYPE = "gis.map.building-code-placement.random"; + private static final String RANDOM_BUILDING_CODE_RATE = "gis.map.building-code-placement.random.code-rate."; + private static final String MAX_BUILDING_CODE = "gis.map.max-building-code"; + + private static final double SQ_MM_TO_SQ_M = 0.000001; + + private GisScenario scenario; + private static final Logger LOG = Logger + .getLogger(GMLWorldModelCreator.class); + + // private ShapeDebugFrame debug; + + private int nextID; + + @Override + public String toString() { + return "GML world model creator"; + } + + + @Override + public WorldModel<? extends Entity> buildWorldModel(Config config) + throws KernelException { + try { + StandardWorldModel result = new StandardWorldModel(); + File dir = new File(config.getValue(MAP_DIRECTORY_KEY)); + File mapFile = new File(dir, + config.getValue(MAP_FILE_KEY, DEFAULT_MAP_FILE)); + File scenarioFile = new File(dir, + config.getValue(SCENARIO_FILE_KEY, DEFAULT_SCENARIO_FILE)); + readMapData(mapFile, result, config); + readScenarioAndApply(scenarioFile, result, config); + for (Entity e : result) { + nextID = Math.max(nextID, e.getID().getValue()); + } + ++nextID; + result.index(); + return result; + } catch (MapException e) { + throw new KernelException("Couldn't read GML file", e); + } catch (DocumentException e) { + throw new KernelException("Couldn't read scenario file", e); + } catch (ScenarioException e) { + throw new KernelException("Invalid scenario file", e); + } + } + + + @Override + public EntityID generateID() { + return new EntityID(nextID++); + } + + + private void readMapData(File mapFile, StandardWorldModel result, + Config config) throws MapException { + int maxFloor = config.getIntValue(MAX_FLOOR, 3); + boolean randomfloorPlacement = config.getBooleanValue(FLOOR_PLACEMENT_TYPE, + false); + int[] floorRates = null; + int[] floorRatesCumulative = null; + if (randomfloorPlacement) { + floorRates = new int[maxFloor + 1]; + floorRatesCumulative = new int[maxFloor + 1]; + for (int i = 1; i <= maxFloor; i++) { + floorRates[i] = config.getIntValue(RANDOM_FLOOR_RATE + i); + floorRatesCumulative[i] = floorRatesCumulative[i - 1] + floorRates[i]; + } + } + + int maxBuildingCode = config.getIntValue(MAX_BUILDING_CODE, 2); + boolean randomBuildingCodePlacement = config + .getBooleanValue(BUILDING_CODE_PLACEMENT_TYPE, false); + int[] buildingCodeRates = null; + int[] buildingCodesCumulative = null; + if (randomBuildingCodePlacement) { + buildingCodeRates = new int[maxBuildingCode + 1]; + buildingCodesCumulative = new int[maxBuildingCode + 1]; + for (int i = 0; i <= maxBuildingCode; i++) { + buildingCodeRates[i] = config + .getIntValue(RANDOM_BUILDING_CODE_RATE + i); + buildingCodesCumulative[i] = (i > 0 ? buildingCodesCumulative[i - 1] + : 0) + buildingCodeRates[i]; + } + } + + GMLMap map = (GMLMap) MapReader.readMap(mapFile); + CoordinateConversion conversion = getCoordinateConversion(map); + LOG.debug("Creating entities"); + LOG.debug(map.getBuildings().size() + " buildings"); + LOG.debug(map.getRoads().size() + " roads"); + + for (GMLBuilding next : map.getBuildings()) { + // Create a new Building entity + EntityID id = new EntityID(next.getID()); + Building b = new Building(id); + List<Point2D> vertices = convertShapeToPoints(next, conversion); + double area = GeometryTools2D.computeArea(vertices) * SQ_MM_TO_SQ_M; + Point2D centroid = GeometryTools2D.computeCentroid(vertices); + + // Building properties + int floors = Math.min(maxFloor, next.getFloors()); + if (randomfloorPlacement) { + int rnd = config.getRandom().nextInt(floorRatesCumulative[maxFloor]) + + 1; + for (int i = 1; i <= maxFloor; i++) { + if (rnd <= floorRatesCumulative[i]) { + floors = i; + break; + } + } + } + + int code = Math.min(maxBuildingCode, next.getCode()); + if (randomBuildingCodePlacement) { + int rnd = config.getRandom() + .nextInt(buildingCodesCumulative[maxBuildingCode]) + 1; + for (int i = 0; i <= maxBuildingCode; i++) { + if (rnd <= buildingCodesCumulative[i]) { + code = i; + break; + } + } + } + + b.setFloors(floors); + b.setFieryness(0); + b.setBrokenness(0); + b.setBuildingCode(code); + b.setBuildingAttributes(0); + b.setGroundArea((int) Math.abs(area)); + b.setTotalArea(((int) Math.abs(area)) * b.getFloors()); + b.setImportance(next.getImportance()); + b.setCapacity(0); + // Area properties + b.setEdges(createEdges(next, conversion)); + b.setX((int) centroid.getX()); + b.setY((int) centroid.getY()); + result.addEntity(b); + } + for (GMLRoad next : map.getRoads()) { + // Create a new Road entity + EntityID id = new EntityID(next.getID()); + Road r = new Road(id); + List<Point2D> vertices = convertShapeToPoints(next, conversion); + Point2D centroid = GeometryTools2D.computeCentroid(vertices); + + // Road properties: None + // Area properties + r.setX((int) centroid.getX()); + r.setY((int) centroid.getY()); + r.setEdges(createEdges(next, conversion)); + result.addEntity(r); + } + } + + + private void readScenarioAndApply(File scenarioFile, + StandardWorldModel result, Config config) + throws DocumentException, ScenarioException { + if (scenarioFile.exists()) { + readScenario(scenarioFile, config); + LOG.debug("Applying scenario"); + scenario.apply(result, config); + } + } + + + private void readScenario(File scenarioFile, Config config) + throws DocumentException, ScenarioException { + if (scenarioFile.exists()) { + SAXReader reader = new SAXReader(); + LOG.debug("Reading scenario"); + Document doc = reader.read(scenarioFile); + scenario = new GisScenario(doc, config); + } + } + + + private List<Edge> createEdges(GMLShape s, CoordinateConversion conversion) { + List<Edge> result = new ArrayList<Edge>(); + for (GMLDirectedEdge edge : s.getEdges()) { + GMLCoordinates start = edge.getStartCoordinates(); + GMLCoordinates end = edge.getEndCoordinates(); + Integer neighbourID = s.getNeighbour(edge); + EntityID id = neighbourID == null ? null : new EntityID(neighbourID); + double sx = conversion.convertX(start.getX()); + double sy = conversion.convertY(start.getY()); + double ex = conversion.convertX(end.getX()); + double ey = conversion.convertY(end.getY()); + result.add(new Edge((int) sx, (int) sy, (int) ex, (int) ey, id)); + } + return result; + } + + + private List<Point2D> convertShapeToPoints(GMLShape shape, + CoordinateConversion conversion) { + List<Point2D> points = new ArrayList<Point2D>(); + for (GMLCoordinates next : shape.getCoordinates()) { + points.add(new Point2D(conversion.convertX(next.getX()), + conversion.convertY(next.getY()))); + } + return points; + } + + + private CoordinateConversion getCoordinateConversion(GMLMap map) { + return new ScaleConversion(map.getMinX(), map.getMinY(), 1000, 1000); + } + + + public GisScenario getScenario(Config config) throws DocumentException { + + if (scenario == null) { + File dir = new File(config.getValue(MAP_DIRECTORY_KEY)); + File scenarioFile = new File(dir, + config.getValue(SCENARIO_FILE_KEY, DEFAULT_SCENARIO_FILE)); + try { + readScenario(scenarioFile, config); + } catch (ScenarioException e) { + e.printStackTrace(); + } + } + return scenario; + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/GisScenario.java b/modules/gis2/src/gis2/GisScenario.java new file mode 100755 index 0000000000000000000000000000000000000000..282c0bb4373fdda59a2f3042ac221c65993e927b --- /dev/null +++ b/modules/gis2/src/gis2/GisScenario.java @@ -0,0 +1,919 @@ +package gis2; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import maps.gml.GMLRefuge; + +import org.apache.log4j.Logger; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Namespace; +import org.dom4j.QName; + +import rescuecore2.config.Config; +import rescuecore2.scenario.compatibilities.CollapseSimCompatibaleScenarioV1_1; +import rescuecore2.scenario.exceptions.ScenarioException; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.FireStation; +import rescuecore2.standard.entities.GasStation; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Hydrant; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.PoliceOffice; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * This class knows how to read scenario files and apply them to + * StandardWorldModels. + */ +public class GisScenario implements rescuecore2.scenario.Scenario, CollapseSimCompatibaleScenarioV1_1 { + private static final String SCENARIO_NAMESPACE_URI = "urn:roborescue:map:scenario"; + private static final Namespace SCENARIO_NAMESPACE = DocumentHelper.createNamespace("scenario", + SCENARIO_NAMESPACE_URI); + + private static final int DEFAULT_HP = 10000; + private static final int DEFAULT_STAMINA = 10000; + private static final String WATER_QUANTITY_KEY = "fire.tank.maximum"; + + private static final QName LOCATION_QNAME = DocumentHelper.createQName("location", SCENARIO_NAMESPACE); + private static final QName TIME_QNAME = DocumentHelper.createQName("time", SCENARIO_NAMESPACE); + private static final QName INTENSITY_QNAME = DocumentHelper.createQName("intensity", SCENARIO_NAMESPACE); + private static final QName SCENARIO_QNAME = DocumentHelper.createQName("scenario", SCENARIO_NAMESPACE); + private static final QName REFUGE_QNAME = DocumentHelper.createQName("refuge", SCENARIO_NAMESPACE); + private static final QName GAS_STATION_QNAME = DocumentHelper.createQName("gasstation", SCENARIO_NAMESPACE); + private static final QName HYDRANT_QNAME = DocumentHelper.createQName("hydrant", SCENARIO_NAMESPACE); + private static final QName CIV_QNAME = DocumentHelper.createQName("civilian", SCENARIO_NAMESPACE); + private static final QName FB_QNAME = DocumentHelper.createQName("firebrigade", SCENARIO_NAMESPACE); + private static final QName AT_QNAME = DocumentHelper.createQName("ambulanceteam", SCENARIO_NAMESPACE); + private static final QName PF_QNAME = DocumentHelper.createQName("policeforce", SCENARIO_NAMESPACE); + private static final QName FS_QNAME = DocumentHelper.createQName("firestation", SCENARIO_NAMESPACE); + private static final QName AC_QNAME = DocumentHelper.createQName("ambulancecentre", SCENARIO_NAMESPACE); + private static final QName PO_QNAME = DocumentHelper.createQName("policeoffice", SCENARIO_NAMESPACE); + private static final QName FIRE_QNAME = DocumentHelper.createQName("fire", SCENARIO_NAMESPACE); + /* Aftershock requirement:2013 */ + private static final QName AFTERSHOCK_QNAME = DocumentHelper.createQName("aftershock", SCENARIO_NAMESPACE); + /* Refuge bed capacity:2020 */ + private static final QName BEDCAPACITY_QNAME = DocumentHelper.createQName("bedCapacity", SCENARIO_NAMESPACE); + /* Refuge refill capacity:2020 */ + private static final QName REFILLCAPACITY_QNAME = DocumentHelper.createQName("refillCapacity", SCENARIO_NAMESPACE); + + private Set<Integer> refuges; + private Set<Integer> hydrants; + private Set<Integer> gasStations; + private Set<Integer> fires; + /* Aftershock requirement:2013 */ + private HashMap<Integer, Float> aftershocks; + private Collection<Integer> civLocations; + private Collection<Integer> fbLocations; + private Collection<Integer> atLocations; + private Collection<Integer> pfLocations; + private Collection<Integer> fsLocations; + private Collection<Integer> acLocations; + private Collection<Integer> poLocations; + + /* Refuge Capacity requirements: 2020 */ + private HashMap<Integer, Integer> refugeBedCapacity; + private HashMap<Integer, Integer> refugeRefillCapacity; + private Map<Integer, GMLRefuge> gmlRefuges; + + private static final Logger LOG = Logger.getLogger(GisScenario.class); + + /** + * Create an empty scenario. + */ + public GisScenario() { + refuges = new HashSet<Integer>(); + hydrants = new HashSet<Integer>(); + gasStations = new HashSet<Integer>(); + fires = new HashSet<Integer>(); + civLocations = new ArrayList<Integer>(); + fbLocations = new ArrayList<Integer>(); + pfLocations = new ArrayList<Integer>(); + atLocations = new ArrayList<Integer>(); + fsLocations = new ArrayList<Integer>(); + poLocations = new ArrayList<Integer>(); + acLocations = new ArrayList<Integer>(); + /* Aftershock requirement:2013 */ + aftershocks = new HashMap<Integer, Float>(); + /* Refuge Capacity requirements: 2020 */ + refugeBedCapacity = new HashMap<Integer, Integer>(); + refugeRefillCapacity = new HashMap<Integer, Integer>(); + gmlRefuges = new HashMap<Integer, GMLRefuge>(); + } + + /** + * Create a scenario from an XML document. + * + * @param doc The document to read. + * @throws ScenarioException If the scenario is invalid. + */ + public GisScenario(Document doc, Config config) throws ScenarioException { + this(); + read(doc, config); + } + + /** + * Read scenario data from an XML document. + * + * @param doc The document to read. + * @throws ScenarioException If the scenario is invalid. + */ + public void read(Document doc, Config config) throws ScenarioException { + hydrants.clear(); + gasStations.clear(); + refuges.clear(); + fires.clear(); + civLocations.clear(); + fbLocations.clear(); + pfLocations.clear(); + atLocations.clear(); + fsLocations.clear(); + poLocations.clear(); + acLocations.clear(); + refugeBedCapacity.clear(); + refugeRefillCapacity.clear(); + + Element root = doc.getRootElement(); + if (!root.getQName().equals(SCENARIO_QNAME)) { + throw new ScenarioException( + "Scenario document has wrong root element: expecting " + SCENARIO_QNAME + "; not " + root.getQName()); + } + for (Object next : root.elements(REFUGE_QNAME)) { + Element e = (Element) next; + refuges.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + /* capacity requirements:2020 */ + int default_bedCapacity = config.getIntValue("gis.map.refuge.default-bedCapacity", 1000); + int default_refillCapacity = config.getIntValue("gis.map.refuge.default-refillCapacity", 1000); + int bedCapacity = e.attributeValue(BEDCAPACITY_QNAME) != null + ? Integer.parseInt(e.attributeValue(BEDCAPACITY_QNAME)) + : default_bedCapacity; + int refillCapacity = e.attributeValue(REFILLCAPACITY_QNAME) != null + ? Integer.parseInt(e.attributeValue(REFILLCAPACITY_QNAME)) + : default_refillCapacity; + refugeBedCapacity.put(Integer.parseInt(e.attributeValue(LOCATION_QNAME)), bedCapacity); + refugeRefillCapacity.put(Integer.parseInt(e.attributeValue(LOCATION_QNAME)), refillCapacity); + } + for (Object next : root.elements(HYDRANT_QNAME)) { + Element e = (Element) next; + hydrants.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(GAS_STATION_QNAME)) { + Element e = (Element) next; + gasStations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(CIV_QNAME)) { + Element e = (Element) next; + civLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(FB_QNAME)) { + Element e = (Element) next; + fbLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(PF_QNAME)) { + Element e = (Element) next; + pfLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(AT_QNAME)) { + Element e = (Element) next; + atLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(FS_QNAME)) { + Element e = (Element) next; + fsLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(PO_QNAME)) { + Element e = (Element) next; + poLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(AC_QNAME)) { + Element e = (Element) next; + acLocations.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + for (Object next : root.elements(FIRE_QNAME)) { + Element e = (Element) next; + fires.add(Integer.parseInt(e.attributeValue(LOCATION_QNAME))); + } + /* + * Aftershock requirement:2013 + */ + for (Object next : root.elements(AFTERSHOCK_QNAME)) { + Element e = (Element) next; + aftershocks.put(Integer.parseInt(e.attributeValue(TIME_QNAME)), + Float.parseFloat(e.attributeValue(INTENSITY_QNAME))); + } + } + + /** + * Write scenario data to an XML document. + * + * @param doc The document to write to. + */ + public void write(Document doc) { + Element root = DocumentHelper.createElement(SCENARIO_QNAME); + doc.setRootElement(root); + for (int next : refuges) { + Element el = root.addElement(REFUGE_QNAME); + el.addAttribute(LOCATION_QNAME, String.valueOf(next)); + el.addAttribute(BEDCAPACITY_QNAME, String.valueOf(refugeBedCapacity.get(next))); + // el.addAttribute(REFILLCAPACITY_QNAME, + // String.valueOf(refugeRefillCapacity.get(next))); + } + for (int next : fires) { + root.addElement(FIRE_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : hydrants) { + root.addElement(HYDRANT_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : gasStations) { + root.addElement(GAS_STATION_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : civLocations) { + root.addElement(CIV_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : fbLocations) { + root.addElement(FB_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : fsLocations) { + root.addElement(FS_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : pfLocations) { + root.addElement(PF_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : poLocations) { + root.addElement(PO_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : atLocations) { + root.addElement(AT_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + for (int next : acLocations) { + root.addElement(AC_QNAME).addAttribute(LOCATION_QNAME, String.valueOf(next)); + } + root.addNamespace("scenario", SCENARIO_NAMESPACE_URI); + } + + /** + * Apply this scenario to a world model. + * + * @param model The world model to alter. + * @param config The configuration. + * @throws ScenarioException if this scenario is invalid. + */ + public void apply(StandardWorldModel model, Config config) throws ScenarioException { + LOG.debug("Creating " + refuges.size() + " refuges"); + for (int next : refuges) { + LOG.debug("Converting building " + next + " to a refuge"); + Building b = (Building) model.getEntity(new EntityID(next)); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + Refuge r = new Refuge(b); + if (refugeBedCapacity.containsKey(next)) + r.setBedCapacity(refugeBedCapacity.get(next)); + else + throw new ScenarioException("RefugeBedCapacity dose not contains " + next); + if (refugeRefillCapacity.containsKey(next)) + r.setRefillCapacity(refugeRefillCapacity.get(next)); + else + throw new ScenarioException("refugeRefillCapacity dose not contain " + next); + + r.setOccupiedBeds(0); + r.setCapacity(0);// todo + r.setWaitingListSize(0); + model.removeEntity(b); + model.addEntity(r); + LOG.debug("Converted " + b + " into " + r); + } + for (int next : gasStations) { + LOG.debug("Converting building " + next + " to a gas station"); + Building b = (Building) model.getEntity(new EntityID(next)); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + GasStation r = new GasStation(b); + r.setImportance(5); + model.removeEntity(b); + model.addEntity(r); + LOG.debug("Converted " + b + " into " + r); + } + for (int next : hydrants) { + LOG.debug("Converting Road " + next + " to a hydrant"); + Area area = (Area) model.getEntity(new EntityID(next)); + if (area == null || !(area instanceof Road)) { + throw new ScenarioException("Road " + next + " does not exist"); + } + Hydrant h = new Hydrant((Road) area); + model.removeEntity(area); + model.addEntity(h); + LOG.debug("Converted " + area + " into " + h); + } + LOG.debug("Igniting " + fires.size() + " fires"); + for (int next : fires) { + LOG.debug("Igniting " + next); + Building b = (Building) model.getEntity(new EntityID(next)); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + b.setIgnition(true); + } + int lastID = 0; + for (StandardEntity next : model) { + lastID = Math.max(lastID, next.getID().getValue()); + } + LOG.debug("Creating " + fbLocations.size() + " fire brigades"); + + for (int next : fbLocations) { + EntityID id = new EntityID(next); + lastID = getNextId(model, config, lastID); + FireBrigade f = new FireBrigade(new EntityID(lastID)); + setupAgent(f, id, model, config); + } + LOG.debug("Creating " + pfLocations.size() + " police forces"); + for (int next : pfLocations) { + EntityID id = new EntityID(next); + lastID = getNextId(model, config, lastID); + PoliceForce p = new PoliceForce(new EntityID(lastID)); + setupAgent(p, id, model, config); + } + LOG.debug("Creating " + atLocations.size() + " ambulance teams"); + for (int next : atLocations) { + EntityID id = new EntityID(next); + lastID = getNextId(model, config, lastID); + AmbulanceTeam a = new AmbulanceTeam(new EntityID(lastID)); + setupAgent(a, id, model, config); + } + LOG.debug("Creating " + fsLocations.size() + " fire stations"); + for (int next : fsLocations) { + EntityID id = new EntityID(next); + LOG.debug("Coverting building " + next + " to a fire station"); + Building b = (Building) model.getEntity(id); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + FireStation f = new FireStation(b); + model.removeEntity(b); + model.addEntity(f); + LOG.debug("Converted " + b + " into " + f); + } + LOG.debug("Creating " + poLocations.size() + " police offices"); + for (int next : poLocations) { + EntityID id = new EntityID(next); + LOG.debug("Coverting building " + next + " to a police office"); + Building b = (Building) model.getEntity(id); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + PoliceOffice p = new PoliceOffice(b); + model.removeEntity(b); + model.addEntity(p); + LOG.debug("Converted " + b + " into " + p); + } + LOG.debug("Creating " + acLocations.size() + " ambulance centres"); + for (int next : acLocations) { + EntityID id = new EntityID(next); + LOG.debug("Coverting building " + next + " to an ambulance centre"); + Building b = (Building) model.getEntity(id); + if (b == null) { + throw new ScenarioException("Building " + next + " does not exist"); + } + AmbulanceCentre a = new AmbulanceCentre(b); + model.removeEntity(b); + model.addEntity(a); + LOG.debug("Converted " + b + " into " + a); + } + LOG.debug("Creating " + civLocations.size() + " civilians"); + for (int next : civLocations) { + EntityID id = new EntityID(next); + lastID = getNextId(model, config, lastID); + Civilian c = new Civilian(new EntityID(lastID)); + setupAgent(c, id, model, config); + } + } + + private int getNextId(StandardWorldModel model, Config config, int lastId) { + boolean humanRandomId = config.getBooleanValue("senario.human.random-id", true); + if (humanRandomId) { + int newId; + do { + newId = config.getRandom().nextInt(Integer.MAX_VALUE); + } while (model.getEntity(new EntityID(newId)) != null); + return newId; + } else { + return lastId + 1; + } + } + + /** + * Get the set of fire locations. + * + * @return The set of fire locations. + */ + public Set<Integer> getFires() { + return Collections.unmodifiableSet(fires); + } + + /** + * Get the set of refuge locations. + * + * @return The set of refuge locations. + */ + public Set<Integer> getRefuges() { + return Collections.unmodifiableSet(refuges); + } + + /** + * Get a refuge by ID. + * + * @param id The ID to look up. + * @return The refuge with that ID or null if the ID is not found. + */ + public GMLRefuge getRefuge(int id) { + return gmlRefuges.get(id); + } + + /** + * Get the set of GMLRefuges. + * + * @return The set of GMLRefuges. + */ + public Map<Integer, GMLRefuge> getGMLRefuges() { + return gmlRefuges; + } + + /** + * Get the HashMap of refuge bed capacity. + * + * @return The HashMap of refuge bed capacity. + */ + public HashMap<Integer, Integer> getRefugeBedCapacity() { + return refugeBedCapacity; + } + + /** + * Get the HashMap of refuge refill capacity. + * + * @return The HashMap of refuge refill capacity. + */ + public HashMap<Integer, Integer> getRefugeRefillCapacity() { + return refugeRefillCapacity; + } + + /** + * Get the set of GasStations locations. + * + * @return The set of GasStations locations. + */ + public Set<Integer> getGasStations() { + return Collections.unmodifiableSet(gasStations); + } + + /** + * Get the set of hydrant locations. + * + * @return The set of hydrant locations. + */ + public Set<Integer> getHydrants() { + return Collections.unmodifiableSet(hydrants); + } + + /** + * Get the list of civilian locations. + * + * @return The list of civilian locations. + */ + public Collection<Integer> getCivilians() { + return Collections.unmodifiableCollection(civLocations); + } + + /** + * Get the list of fire brigade locations. + * + * @return The list of fire brigade locations. + */ + public Collection<Integer> getFireBrigades() { + return Collections.unmodifiableCollection(fbLocations); + } + + /** + * Get the list of fire station locations. + * + * @return The list of fire station locations. + */ + public Collection<Integer> getFireStations() { + return Collections.unmodifiableCollection(fsLocations); + } + + /** + * Get the list of police force locations. + * + * @return The list of police force locations. + */ + public Collection<Integer> getPoliceForces() { + return Collections.unmodifiableCollection(pfLocations); + } + + /** + * Get the list of police office locations. + * + * @return The list of police office locations. + */ + public Collection<Integer> getPoliceOffices() { + return Collections.unmodifiableCollection(poLocations); + } + + /** + * Get the list of ambulance team locations. + * + * @return The list of ambulance team locations. + */ + public Collection<Integer> getAmbulanceTeams() { + return Collections.unmodifiableCollection(atLocations); + } + + /** + * Get the list of ambulance centre locations. + * + * @return The list of ambulance centre locations. + */ + public Collection<Integer> getAmbulanceCentres() { + return Collections.unmodifiableCollection(acLocations); + } + + /** + * Set the set of fire locations. + * + * @param newLocations The new set of locations. + */ + public void setFires(Set<Integer> newLocations) { + fires.clear(); + fires.addAll(newLocations); + } + + /** + * Set the set of refuge locations. + * + * @param newLocations The new set of locations. + */ + public void setRefuges(Set<Integer> newLocations) { + refuges.clear(); + refuges.addAll(newLocations); + } + + /** + * Set the set of gas station locations. + * + * @param newLocations The new set of locations. + */ + public void setGasStations(Set<Integer> newLocations) { + gasStations.clear(); + gasStations.addAll(newLocations); + } + + /** + * Set the set of hydrant locations. + * + * @param newLocations The new set of locations. + */ + public void setHydrants(Set<Integer> newLocations) { + hydrants.clear(); + hydrants.addAll(newLocations); + } + + /** + * Set the list of civilian locations. + * + * @param newLocations The new list of locations. + */ + public void setCivilians(Collection<Integer> newLocations) { + civLocations.clear(); + civLocations.addAll(newLocations); + } + + /** + * Set the list of fire brigade locations. + * + * @param newLocations The new list of locations. + */ + public void setFireBrigades(Collection<Integer> newLocations) { + fbLocations.clear(); + fbLocations.addAll(newLocations); + } + + /** + * Set the list of fire station locations. + * + * @param newLocations The new list of locations. + */ + public void setFireStations(Collection<Integer> newLocations) { + fsLocations.clear(); + fsLocations.addAll(newLocations); + } + + /** + * Set the list of police force locations. + * + * @param newLocations The new list of locations. + */ + public void setPoliceForces(Collection<Integer> newLocations) { + pfLocations.clear(); + pfLocations.addAll(newLocations); + } + + /** + * Set the list of police office locations. + * + * @param newLocations The new list of locations. + */ + public void setPoliceOffices(Collection<Integer> newLocations) { + poLocations.clear(); + poLocations.addAll(newLocations); + } + + /** + * Set the list of ambulance team locations. + * + * @param newLocations The new list of locations. + */ + public void setAmbulanceTeams(Collection<Integer> newLocations) { + atLocations.clear(); + atLocations.addAll(newLocations); + } + + /** + * Set the list of ambulance centre locations. + * + * @param newLocations The new list of locations. + */ + public void setAmbulanceCentres(Collection<Integer> newLocations) { + acLocations.clear(); + acLocations.addAll(newLocations); + } + + /** + * Add a fire. + * + * @param location The new fire location. + */ + public void addFire(int location) { + fires.add(location); + } + + /** + * Remove a fire. + * + * @param location The fire location to remove. + */ + public void removeFire(int location) { + fires.remove(location); + } + + /** + * Add a refuge. + * + * @param location The new refuge location. + */ + public void addRefuge(int location) { + refuges.add(location); + } + + public void addRefuge(int location, int bedCapacity, int refillCapacity) { + refuges.add(location); + refugeBedCapacity.put(location, bedCapacity); + refugeRefillCapacity.put(location, refillCapacity); + } + + public void addRefuge(int location, int bedCapacity) { + refuges.add(location); + refugeBedCapacity.put(location, bedCapacity); + } + + public void addGMLRefuge(GMLRefuge r) { + gmlRefuges.put(r.getID(), r); + } + + /** + * Remove a refuge. + * + * @param location The refuge location to remove. + */ + public void removeRefuge(int location) { + refuges.remove(location); + if (refugeRefillCapacity.containsKey(location)) { + refugeRefillCapacity.remove(location); + } + if (refugeBedCapacity.containsKey(location)) { + refugeBedCapacity.remove(location); + } + gmlRefuges.remove(location); + } + + /** + * Add a hydrant. + * + * @param location The new hydrant location. + */ + public void addHydrant(int location) { + hydrants.add(location); + } + + /** + * Remove a hydrant. + * + * @param location The hydrant location to remove. + */ + public void removeHydrant(int location) { + hydrants.remove(location); + } + + /** + * Remove a GasStation. + * + * @param location The GasStation location to remove. + */ + public void removeGasStation(int location) { + gasStations.remove(location); + } + + /** + * Add a GasStation. + * + * @param location The new GasStation location. + */ + public void addGasStation(int location) { + gasStations.add(location); + } + + /** + * Add a civilian. + * + * @param location The new civilian location. + */ + public void addCivilian(int location) { + civLocations.add(location); + } + + /** + * Remove a civilian. + * + * @param location The civilian location to remove. + */ + public void removeCivilian(int location) { + civLocations.remove(location); + } + + /** + * Add a fire brigade. + * + * @param location The new fire brigade location. + */ + public void addFireBrigade(int location) { + fbLocations.add(location); + } + + /** + * Remove a fire brigade. + * + * @param location The fire brigade location to remove. + */ + public void removeFireBrigade(int location) { + fbLocations.remove(location); + } + + /** + * Add a fire station. + * + * @param location The new fire station location. + */ + public void addFireStation(int location) { + fsLocations.add(location); + } + + /** + * Remove a fire station. + * + * @param location The fire station location to remove. + */ + public void removeFireStation(int location) { + fsLocations.remove(location); + } + + /** + * Add a police force. + * + * @param location The new police force location. + */ + public void addPoliceForce(int location) { + pfLocations.add(location); + } + + /** + * Remove a police force. + * + * @param location The police force location to remove. + */ + public void removePoliceForce(int location) { + pfLocations.remove(location); + } + + /** + * Add a police office. + * + * @param location The new police office location. + */ + public void addPoliceOffice(int location) { + poLocations.add(location); + } + + /** + * Remove a police office. + * + * @param location The police office location to remove. + */ + public void removePoliceOffice(int location) { + poLocations.remove(location); + } + + /** + * Add an ambulance team. + * + * @param location The new ambulance team location. + */ + public void addAmbulanceTeam(int location) { + atLocations.add(location); + } + + /** + * Remove an ambulance team. + * + * @param location The ambulance team location to remove. + */ + public void removeAmbulanceTeam(int location) { + atLocations.remove(location); + } + + /** + * Add an ambulance centre. + * + * @param location The new ambulance centre location. + */ + public void addAmbulanceCentre(int location) { + acLocations.add(location); + } + + /** + * Remove an ambulance centre. + * + * @param location The ambulance centre location to remove. + */ + public void removeAmbulanceCentre(int location) { + acLocations.remove(location); + } + + private void setupAgent(Human h, EntityID position, StandardWorldModel model, Config config) + throws ScenarioException { + Entity areaEntity = model.getEntity(position); + if (areaEntity == null) { + throw new ScenarioException("Area " + position + " does not exist"); + } + if (!(areaEntity instanceof Area)) { + throw new ScenarioException("Entity " + position + " is not an area: " + areaEntity); + } + Area area = (Area) areaEntity; + h.setX(area.getX()); + h.setY(area.getY()); + h.setPosition(position); + h.setStamina(DEFAULT_STAMINA); + h.setHP(DEFAULT_HP); + h.setDamage(0); + h.setBuriedness(0); + h.setDirection(0); + h.setTravelDistance(0); + h.setPositionHistory(new int[0]); + if (h instanceof FireBrigade) { + ((FireBrigade) h).setWater(config.getIntValue(WATER_QUANTITY_KEY)); + } + model.addEntity(h); + LOG.debug("Created " + h); + } + + @Override + public HashMap<Integer, Float> getAftershocks() { + return aftershocks; + } +} diff --git a/modules/gis2/src/gis2/ScenarioException.java b/modules/gis2/src/gis2/ScenarioException.java new file mode 100644 index 0000000000000000000000000000000000000000..1c42a80702fe66d24864428c5ba8868a9d8ecd8b --- /dev/null +++ b/modules/gis2/src/gis2/ScenarioException.java @@ -0,0 +1,41 @@ +package gis2; + +/** + * Exception class for problems with scenarios. + */ +public class ScenarioException extends Exception { + /** + * Construct a scenario exception with no information. + */ + public ScenarioException() { + super(); + } + + /** + * Construct a scenario exception with an error message. + * + * @param msg The error message. + */ + public ScenarioException(String msg) { + super(msg); + } + + /** + * Construct a scenario exception that was caused by another exception. + * + * @param cause The cause of this exception. + */ + public ScenarioException(Throwable cause) { + super(cause); + } + + /** + * Construct a scenario exception with an error message and an underlying cause. + * + * @param msg The error message. + * @param cause The cause of this exception. + */ + public ScenarioException(String msg, Throwable cause) { + super(msg, cause); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/AbstractFunction.java b/modules/gis2/src/gis2/scenario/AbstractFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..05c3c7b7e40d0a5a368cb6fec47cb4dc821e2ac5 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/AbstractFunction.java @@ -0,0 +1,18 @@ +package gis2.scenario; + +/** + * Abstract base class for scenario editing functions. + */ +public abstract class AbstractFunction implements Function { + /** The editor instance. */ + protected ScenarioEditor editor; + + /** + * Construct an AbstractFunction. + * + * @param editor The editor instance. + */ + protected AbstractFunction(ScenarioEditor editor) { + this.editor = editor; + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/AbstractTool.java b/modules/gis2/src/gis2/scenario/AbstractTool.java new file mode 100644 index 0000000000000000000000000000000000000000..510cca0e1ae2bb14ce61b8dde8b69cb27c0a2b25 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/AbstractTool.java @@ -0,0 +1,18 @@ +package gis2.scenario; + +/** + * Abstract base class for scenario editing tools. + */ +public abstract class AbstractTool implements Tool { + /** The scenario editor instance. */ + protected ScenarioEditor editor; + + /** + * Construct an AbstractTool. + * + * @param editor The scenario editor instance. + */ + protected AbstractTool(ScenarioEditor editor) { + this.editor = editor; + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/AgentOverlay.java b/modules/gis2/src/gis2/scenario/AgentOverlay.java new file mode 100644 index 0000000000000000000000000000000000000000..7013ac951fe7ba60f358c8acd7fb9c0517b91805 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/AgentOverlay.java @@ -0,0 +1,134 @@ +package gis2.scenario; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.util.Map; + +import maps.gml.GMLShape; +import maps.gml.view.Overlay; + +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.misc.gui.ScreenTransform; + +/** + * Overlay for viewing agents in a scenario. + */ +public class AgentOverlay implements Overlay { + private static final int SIZE = 11; + private static final Color CIVILIAN_COLOUR = Color.GREEN; + 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 int OFFSET = 7; + private ScenarioEditor editor; + + /** + * Construct an AgentOverlay. + * + * @param editor The scenario editor. + */ + public AgentOverlay(ScenarioEditor editor) { + this.editor = editor; + } + + @Override + public void render(Graphics2D g, ScreenTransform transform) { + // Count agents in each location + g.setFont(new Font(g.getFont().getName(), Font.BOLD, g.getFont().getSize())); + Map<Integer, Integer> civs = new LazyMap<Integer, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + Map<Integer, Integer> fbs = new LazyMap<Integer, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + Map<Integer, Integer> pfs = new LazyMap<Integer, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + Map<Integer, Integer> ats = new LazyMap<Integer, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + for (int next : editor.getScenario().getCivilians()) { + civs.put(next, civs.get(next) + 1); + } + for (int next : editor.getScenario().getFireBrigades()) { + fbs.put(next, fbs.get(next) + 1); + } + for (int next : editor.getScenario().getPoliceForces()) { + pfs.put(next, pfs.get(next) + 1); + } + for (int next : editor.getScenario().getAmbulanceTeams()) { + ats.put(next, ats.get(next) + 1); + } + // Now draw them + for (Map.Entry<Integer, Integer> next : civs.entrySet()) { + GMLShape shape = editor.getMap().getShape(next.getKey()); + int count = next.getValue(); + // int x = transform.xToScreen(shape.getCentreX()); + // int y = transform.yToScreen(shape.getCentreY()) + CIV_OFFSET; + int x = transform.xToScreen(shape.getCentreX()) + OFFSET; + int y = transform.yToScreen(shape.getCentreY()); + // g.drawString(count + " civs", x, y); + paint(g, x, y, CIVILIAN_COLOUR); + g.drawString(count + "", x, y); + + } + for (Map.Entry<Integer, Integer> next : fbs.entrySet()) { + GMLShape shape = editor.getMap().getShape(next.getKey()); + int count = next.getValue(); + // int x = transform.xToScreen(shape.getCentreX()); + // int y = transform.yToScreen(shape.getCentreY()) + FB_OFFSET; + int x = transform.xToScreen(shape.getCentreX()); + int y = transform.yToScreen(shape.getCentreY()) - OFFSET; + // g.drawString(count + " fbs", x, y); + paint(g, x, y, FIRE_BRIGADE_COLOUR); + g.drawString(count + "", x, y); + } + for (Map.Entry<Integer, Integer> next : pfs.entrySet()) { + GMLShape shape = editor.getMap().getShape(next.getKey()); + int count = next.getValue(); + // int x = transform.xToScreen(shape.getCentreX()); + // int y = transform.yToScreen(shape.getCentreY()) + PF_OFFSET; + int x = transform.xToScreen(shape.getCentreX()); + int y = transform.yToScreen(shape.getCentreY()) + OFFSET; + // g.drawString(count + " pfs", x, y); + paint(g, x, y, POLICE_FORCE_COLOUR); + g.drawString(count + "", x, y); + } + for (Map.Entry<Integer, Integer> next : ats.entrySet()) { + GMLShape shape = editor.getMap().getShape(next.getKey()); + int count = next.getValue(); + // int x = transform.xToScreen(shape.getCentreX()); + // int y = transform.yToScreen(shape.getCentreY()) + AT_OFFSET; + int x = transform.xToScreen(shape.getCentreX()) - OFFSET; + int y = transform.yToScreen(shape.getCentreY()); + // g.drawString(count + " ats", x, y); + paint(g, x, y, AMBULANCE_TEAM_COLOUR); + g.drawString(count + "", x, y); + + } + } + + public void paint(Graphics2D g, int x, int y, Color color) { + Shape shape = new Ellipse2D.Double(x - SIZE / 4, y - SIZE, SIZE, SIZE); + g.setColor(color); + g.fill(shape); + g.draw(shape); + g.setColor(Color.black); + } + +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/CancelledByUserException.java b/modules/gis2/src/gis2/scenario/CancelledByUserException.java new file mode 100644 index 0000000000000000000000000000000000000000..54a7708d36377edfd9db07353973c6a834e738eb --- /dev/null +++ b/modules/gis2/src/gis2/scenario/CancelledByUserException.java @@ -0,0 +1,12 @@ +package gis2.scenario; + +/** + * Exception for indicating the the user has cancelled an operation. + */ +public class CancelledByUserException extends Exception { + /** + * Constructor. + */ + public CancelledByUserException() { + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ClearAgentsFunction.java b/modules/gis2/src/gis2/scenario/ClearAgentsFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..d6d340eb622d357ee123bb84f8777aafce9615c4 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ClearAgentsFunction.java @@ -0,0 +1,37 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.util.HashSet; + +/** + * Function for removing all agents. + */ +public class ClearAgentsFunction extends AbstractFunction { + /** + * Construct a clear agents function. + * + * @param editor The editor instance. + */ + public ClearAgentsFunction(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove agents"; + } + + @Override + public void execute() { + GisScenario s = editor.getScenario(); + s.setFireBrigades(new HashSet<Integer>()); + s.setFireStations(new HashSet<Integer>()); + s.setPoliceForces(new HashSet<Integer>()); + s.setPoliceOffices(new HashSet<Integer>()); + s.setAmbulanceTeams(new HashSet<Integer>()); + s.setAmbulanceCentres(new HashSet<Integer>()); + editor.setChanged(); + editor.updateOverlays(); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ClearAllFunction.java b/modules/gis2/src/gis2/scenario/ClearAllFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..c4f4dd6e3ad0492440c3efb81a980a10558b82b8 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ClearAllFunction.java @@ -0,0 +1,42 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.util.HashSet; + +/** + * Function for removing all agents, fires, civilians and refuges. + */ +public class ClearAllFunction extends AbstractFunction { + /** + * Construct a clear all function. + * + * @param editor The editor instance. + */ + public ClearAllFunction(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove all"; + } + + @Override + public void execute() { + GisScenario s = editor.getScenario(); + s.setFireBrigades(new HashSet<Integer>()); + s.setFireStations(new HashSet<Integer>()); + s.setPoliceForces(new HashSet<Integer>()); + s.setPoliceOffices(new HashSet<Integer>()); + s.setAmbulanceTeams(new HashSet<Integer>()); + s.setAmbulanceCentres(new HashSet<Integer>()); + s.setCivilians(new HashSet<Integer>()); + s.setFires(new HashSet<Integer>()); + s.setRefuges(new HashSet<Integer>()); + s.setGasStations(new HashSet<Integer>()); + s.setHydrants(new HashSet<Integer>()); + editor.setChanged(); + editor.updateOverlays(); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ClearFiresFunction.java b/modules/gis2/src/gis2/scenario/ClearFiresFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..cb3439b10212bfa06b0e80f2d4ca56f5277ab9ab --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ClearFiresFunction.java @@ -0,0 +1,32 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.util.HashSet; + +/** + * Function for removing all fires. + */ +public class ClearFiresFunction extends AbstractFunction { + /** + * Construct a clear fires function. + * + * @param editor The editor instance. + */ + public ClearFiresFunction(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove fires"; + } + + @Override + public void execute() { + GisScenario s = editor.getScenario(); + s.setFires(new HashSet<Integer>()); + editor.setChanged(); + editor.updateOverlays(); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/Function.java b/modules/gis2/src/gis2/scenario/Function.java new file mode 100644 index 0000000000000000000000000000000000000000..56376cda6d4933928bec9d0e31a132eb3c9fb6af --- /dev/null +++ b/modules/gis2/src/gis2/scenario/Function.java @@ -0,0 +1,18 @@ +package gis2.scenario; + +/** + * Interface for a scenario editing function. + */ +public interface Function { + /** + * Get the name of this function. + * + * @return The name of the function. + */ + String getName(); + + /** + * Execute this function. + */ + void execute(); +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ModifiedFlowLayout.java b/modules/gis2/src/gis2/scenario/ModifiedFlowLayout.java new file mode 100644 index 0000000000000000000000000000000000000000..a254a86edf4c4c6537dae0d3d262e54a2eb52195 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ModifiedFlowLayout.java @@ -0,0 +1,113 @@ +package gis2.scenario; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +/** + * A modified version of FlowLayout that allows containers using this Layout to + * behave in a reasonable manner when placed inside a JScrollPane + * + * @author Babu Kalakrishnan Modifications by greearb and jzd + */ + +public class ModifiedFlowLayout extends FlowLayout { + public ModifiedFlowLayout() { + super(FlowLayout.LEFT); + } + + public ModifiedFlowLayout(int align) { + super(align); + } + + public ModifiedFlowLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + public Dimension minimumLayoutSize(Container target) { + // Size of largest component, so we can resize it in + // either direction with something like a split-pane. + return computeMinSize(target); + } + + public Dimension preferredLayoutSize(Container target) { + return computeSize(target); + } + + private Dimension computeSize(Container target) { + synchronized (target.getTreeLock()) { + int hgap = getHgap(); + int vgap = getVgap(); + int w = target.getWidth(); + + // Let this behave like a regular FlowLayout (single row) + // if the container hasn't been assigned any size yet + if (w == 0) { + w = Integer.MAX_VALUE; + } + + Insets insets = target.getInsets(); + if (insets == null) { + insets = new Insets(0, 0, 0, 0); + } + int reqdWidth = 0; + + int maxwidth = w - (insets.left + insets.right + hgap * 2); + int n = target.getComponentCount(); + int x = 0; + int y = insets.top + vgap; // FlowLayout starts by adding vgap, so + // do that here too. + int rowHeight = 0; + + for (int i = 0; i < n; i++) { + Component c = target.getComponent(i); + if (c.isVisible()) { + Dimension d = c.getPreferredSize(); + if ((x == 0) || ((x + d.width) <= maxwidth)) { + // fits in current row. + if (x > 0) { + x += hgap; + } + x += d.width; + rowHeight = Math.max(rowHeight, d.height); + } else { + // Start of new row + x = d.width; + y += vgap + rowHeight; + rowHeight = d.height; + } + reqdWidth = Math.max(reqdWidth, x); + } + } + y += rowHeight; + y += insets.bottom; + return new Dimension(reqdWidth + insets.left + insets.right, y); + } + } + + private Dimension computeMinSize(Container target) { + synchronized (target.getTreeLock()) { + int minx = Integer.MAX_VALUE; + int miny = Integer.MIN_VALUE; + boolean found_one = false; + int n = target.getComponentCount(); + + for (int i = 0; i < n; i++) { + Component c = target.getComponent(i); + if (c.isVisible()) { + found_one = true; + Dimension d = c.getPreferredSize(); + minx = Math.min(minx, d.width); + miny = Math.min(miny, d.height); + } + } + if (found_one) { + return new Dimension(minx, miny); + } + return new Dimension(0, 0); + } + } + +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceAgentsFunction.java b/modules/gis2/src/gis2/scenario/PlaceAgentsFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..9c8eb4ef21d767379fa1c3c98be70c84f012b1d8 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceAgentsFunction.java @@ -0,0 +1,114 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import maps.gml.GMLShape; + +/** + * Function for placing agents. + */ +public class PlaceAgentsFunction extends AbstractFunction { + private static final int TYPE_FIRE = 0; + private static final int TYPE_POLICE = 1; + private static final int TYPE_AMBULANCE = 2; + private static final int TYPE_CIVILIAN = 3; + + private Random random; + + /** + * Construct a place agents function. + * + * @param editor The editor instance. + */ + public PlaceAgentsFunction(ScenarioEditor editor) { + super(editor); + random = new Random(); + } + + @Override + public String getName() { + return "Place agents"; + } + + @Override + public void execute() { + JPanel panel = new JPanel(new GridLayout(3, 2)); + JTextField numberField = new JTextField("1"); + JComboBox<String> typeCombo = new JComboBox<String>(new String[] { "Fire", "Police", "Ambulance", "Civilian" }); + + JCheckBox buildingBox = new JCheckBox("In buildings?", false); + JCheckBox roadBox = new JCheckBox("In Roads?", true); + JPanel jp = new JPanel(); + jp.add(buildingBox); + jp.add(roadBox); + + panel.add(new JLabel("Type")); + panel.add(typeCombo); + panel.add(new JLabel("Number")); + panel.add(numberField); + panel.add(jp); + List<Integer> ids = new ArrayList<Integer>(); + int type = -1; + List<GMLShape> all = new ArrayList<GMLShape>(); + if (JOptionPane.showConfirmDialog(null, panel, "Add agents", + JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + try { + int number = Integer.parseInt(numberField.getText()); + type = typeCombo.getSelectedIndex(); + if (roadBox.isSelected()) + all.addAll(editor.getMap().getRoads()); + if (buildingBox.isSelected()) + all.addAll(editor.getMap().getBuildings()); + if (all.size() == 0) { + JOptionPane.showMessageDialog(null, "No Area to Place... Please choose In Road or Building...", "Error", + JOptionPane.ERROR_MESSAGE); + return; + } + for (int i = 0; i < number; ++i) { + ids.add(all.get(random.nextInt(all.size())).getID()); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + GisScenario s = editor.getScenario(); + switch (type) { + case TYPE_FIRE: + for (int id : ids) { + s.addFireBrigade(id); + } + break; + case TYPE_POLICE: + for (int id : ids) { + s.addPoliceForce(id); + } + break; + case TYPE_AMBULANCE: + for (int id : ids) { + s.addAmbulanceTeam(id); + } + break; + case TYPE_CIVILIAN: + for (int id : ids) { + s.addCivilian(id); + } + break; + default: + throw new IllegalArgumentException("Unexpected type: " + type); + } + editor.setChanged(); + editor.updateOverlays(); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceAmbulanceCentreTool.java b/modules/gis2/src/gis2/scenario/PlaceAmbulanceCentreTool.java new file mode 100644 index 0000000000000000000000000000000000000000..e51f62d80d6a3a0d8a2e162ed1cef745da1e356b --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceAmbulanceCentreTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing ambulance centres. + */ +public class PlaceAmbulanceCentreTool extends ShapeTool { + /** + * Construct a PlaceAmbulanceCentreTool. + * + * @param editor The editor instance. + */ + public PlaceAmbulanceCentreTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place ambulance centre"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addAmbulanceCentre(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddAmbulanceCentreEdit(shape.getID())); + } + + private class AddAmbulanceCentreEdit extends AbstractUndoableEdit { + private int id; + + public AddAmbulanceCentreEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeAmbulanceCentre(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addAmbulanceCentre(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceAmbulanceTeamTool.java b/modules/gis2/src/gis2/scenario/PlaceAmbulanceTeamTool.java new file mode 100644 index 0000000000000000000000000000000000000000..32119990202b8118589ef49af42ec61a07dcfa54 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceAmbulanceTeamTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for placing ambulance teams. + */ +public class PlaceAmbulanceTeamTool extends ShapeTool { + /** + * Construct a PlaceAmbulanceTeamTool. + * + * @param editor The editor instance. + */ + public PlaceAmbulanceTeamTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place ambulance team"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addAmbulanceTeam(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddAmbulanceTeamEdit(shape.getID())); + } + + private class AddAmbulanceTeamEdit extends AbstractUndoableEdit { + private int id; + + public AddAmbulanceTeamEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeAmbulanceTeam(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addAmbulanceTeam(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceCivilianTool.java b/modules/gis2/src/gis2/scenario/PlaceCivilianTool.java new file mode 100644 index 0000000000000000000000000000000000000000..662e59be8bcc7210c5b88bc1f35b7043b2da05cf --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceCivilianTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for placing civilians. + */ +public class PlaceCivilianTool extends ShapeTool { + /** + * Construct a PlaceCivilianTool. + * + * @param editor The editor instance. + */ + public PlaceCivilianTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place civilian"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addCivilian(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddCivilianEdit(shape.getID())); + } + + private class AddCivilianEdit extends AbstractUndoableEdit { + private int id; + + public AddCivilianEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeCivilian(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addCivilian(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceFireBrigadeTool.java b/modules/gis2/src/gis2/scenario/PlaceFireBrigadeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..78f2e8523d44d59c000e3c9f0a4afb6d18d68c45 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceFireBrigadeTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for placing fire brigades. + */ +public class PlaceFireBrigadeTool extends ShapeTool { + /** + * Construct a PlaceFireBrigadeTool. + * + * @param editor The editor instance. + */ + public PlaceFireBrigadeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place fire brigade"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addFireBrigade(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddFireBrigadeEdit(shape.getID())); + } + + private class AddFireBrigadeEdit extends AbstractUndoableEdit { + private int id; + + public AddFireBrigadeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeFireBrigade(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addFireBrigade(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceFireStationTool.java b/modules/gis2/src/gis2/scenario/PlaceFireStationTool.java new file mode 100644 index 0000000000000000000000000000000000000000..b4e25be0c7969f890e42a426bddb9b983dcabc23 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceFireStationTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing fire stations. + */ +public class PlaceFireStationTool extends ShapeTool { + /** + * Construct a PlaceFireStationTool. + * + * @param editor The editor instance. + */ + public PlaceFireStationTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place fire station"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addFireStation(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddFireStationEdit(shape.getID())); + } + + private class AddFireStationEdit extends AbstractUndoableEdit { + private int id; + + public AddFireStationEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeFireStation(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addFireStation(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceFireTool.java b/modules/gis2/src/gis2/scenario/PlaceFireTool.java new file mode 100644 index 0000000000000000000000000000000000000000..00cc15ba6432369ba16e25379da260c9c2cfc316 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceFireTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing fires. + */ +public class PlaceFireTool extends ShapeTool { + /** + * Construct a PlaceFireTool. + * + * @param editor The editor instance. + */ + public PlaceFireTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place fire"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addFire(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddFireEdit(shape.getID())); + } + + private class AddFireEdit extends AbstractUndoableEdit { + private int id; + + public AddFireEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeFire(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addFire(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceGasStationTool.java b/modules/gis2/src/gis2/scenario/PlaceGasStationTool.java new file mode 100755 index 0000000000000000000000000000000000000000..f9be232b3ea0010f2feb0b587d3be20cd43abc57 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceGasStationTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing gasStation. + */ +public class PlaceGasStationTool extends ShapeTool { + /** + * Construct a PlaceGasStationTool. + * + * @param editor The editor instance. + */ + public PlaceGasStationTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place gas station"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addGasStation(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddGasStationEdit(shape.getID())); + } + + private class AddGasStationEdit extends AbstractUndoableEdit { + private int id; + + public AddGasStationEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeGasStation(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addGasStation(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceHydrantTool.java b/modules/gis2/src/gis2/scenario/PlaceHydrantTool.java new file mode 100644 index 0000000000000000000000000000000000000000..eb909bd45ec1beb00654ca2ee05283e47f503e53 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceHydrantTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLRoad; +import maps.gml.GMLShape; + +/** + * Tool for placing refuges. + */ +public class PlaceHydrantTool extends ShapeTool { + /** + * Construct a PlaceHydrantTool. + * + * @param editor The editor instance. + */ + public PlaceHydrantTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place hydrant"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLRoad; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addHydrant(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddHydrantEdit(shape.getID())); + } + + private class AddHydrantEdit extends AbstractUndoableEdit { + private int id; + + public AddHydrantEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeHydrant(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addHydrant(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlacePoliceForceTool.java b/modules/gis2/src/gis2/scenario/PlacePoliceForceTool.java new file mode 100644 index 0000000000000000000000000000000000000000..706d9ce92ea9b169be412c6570ad4cad88c035c9 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlacePoliceForceTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for placing police forces. + */ +public class PlacePoliceForceTool extends ShapeTool { + /** + * Construct a PlacePoliceForceTool. + * + * @param editor The editor instance. + */ + public PlacePoliceForceTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place police force"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addPoliceForce(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddPoliceForceEdit(shape.getID())); + } + + private class AddPoliceForceEdit extends AbstractUndoableEdit { + private int id; + + public AddPoliceForceEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removePoliceForce(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addPoliceForce(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlacePoliceOfficeTool.java b/modules/gis2/src/gis2/scenario/PlacePoliceOfficeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..49776004fc064124238a3da97a16fb34103ab1cc --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlacePoliceOfficeTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing police offices. + */ +public class PlacePoliceOfficeTool extends ShapeTool { + /** + * Construct a PlacePoliceOfficeTool. + * + * @param editor The editor instance. + */ + public PlacePoliceOfficeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place police office"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().addPoliceOffice(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddPoliceOfficeEdit(shape.getID())); + } + + private class AddPoliceOfficeEdit extends AbstractUndoableEdit { + private int id; + + public AddPoliceOfficeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removePoliceOffice(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addPoliceOffice(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/PlaceRefugeTool.java b/modules/gis2/src/gis2/scenario/PlaceRefugeTool.java new file mode 100755 index 0000000000000000000000000000000000000000..5282a0c4f842feb63b41aceaf9fab92f1b048090 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/PlaceRefugeTool.java @@ -0,0 +1,77 @@ +package gis2.scenario; + +import java.awt.GridLayout; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for placing refuges. + */ +public class PlaceRefugeTool extends ShapeTool { + /** + * Construct a PlaceRefugeTool. + * + * @param editor The editor instance. + */ + public PlaceRefugeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Place refuge"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + JPanel panel = new JPanel(new GridLayout(3, 2)); + JTextField bedNumberField = new JTextField("100"); + panel.add(new JLabel("Insert a bed capacity for the refuge")); + panel.add(bedNumberField); + + if (JOptionPane.showConfirmDialog(null, panel, "Refuge Capacity", + JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + int bedCapacity = Integer.parseInt(bedNumberField.getText()); + int refillCapacity = 1000;// Integer.parseInt(refillNumberField.getText()); + + editor.getScenario().addRefuge(shape.getID(), bedCapacity, refillCapacity); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new AddRefugeEdit(shape.getID())); + } + } + + private class AddRefugeEdit extends AbstractUndoableEdit { + private int id; + + public AddRefugeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().removeRefuge(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().addRefuge(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RandomHydrantPlacementFunction.java b/modules/gis2/src/gis2/scenario/RandomHydrantPlacementFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..10df614557c92d4e9a4e904be66dd7235ff9fa9c --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RandomHydrantPlacementFunction.java @@ -0,0 +1,73 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import maps.gml.GMLMap; +import maps.gml.GMLShape; + +/** + * Function for placing agents. + */ +public class RandomHydrantPlacementFunction extends AbstractFunction { + private Random random; + + /** + * Construct a place agents function. + * + * @param editor The editor instance. + */ + public RandomHydrantPlacementFunction(ScenarioEditor editor) { + super(editor); + random = new Random(); + } + + @Override + public String getName() { + return "Random Hydrant Placement"; + } + + @Override + public void execute() { + JPanel panel = new JPanel(new GridLayout(3, 2)); + JTextField numberField = new JTextField("1"); + GMLMap map = editor.getMap(); + double height = (map.getMaxX() - map.getMinX()); + double width = (map.getMaxY() - map.getMinY()); + int suggestedCount = (int) (height * width / 30000); + panel.add(new JLabel("Number: suggested number:" + suggestedCount)); + panel.add(numberField); + HashSet<Integer> selectedIds = new HashSet<>(); + List<GMLShape> all = new ArrayList<GMLShape>(editor.getMap().getRoads()); + if (JOptionPane.showConfirmDialog(null, panel, "Add agents", + JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + GisScenario s = editor.getScenario(); + try { + int number = Integer.parseInt(numberField.getText()); + for (int i = 0; i < number; ++i) { + int id = all.get(random.nextInt(all.size())).getID(); + if (selectedIds.contains(id)) + i--; + else { + s.addHydrant(id); + selectedIds.add(id); + } + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + editor.setChanged(); + editor.updateOverlays(); + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RandomScenarioGenerator.java b/modules/gis2/src/gis2/scenario/RandomScenarioGenerator.java new file mode 100755 index 0000000000000000000000000000000000000000..95a73511fd8ac0d07fb29c1ab0fd73d562a736cf --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RandomScenarioGenerator.java @@ -0,0 +1,354 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +import maps.MapException; +import maps.MapReader; +import maps.gml.GMLBuilding; +import maps.gml.GMLMap; +import maps.gml.GMLShape; + +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; + +/** + * A class for generating random scenarios. + */ +public class RandomScenarioGenerator { + private static final int DEFAULT_MIN_CIVS = 50; + private static final int DEFAULT_MAX_CIVS = 200; + private static final int DEFAULT_MIN_PLATOONS = 0; + private static final int DEFAULT_MAX_PLATOONS = 30; + private static final int DEFAULT_MIN_CENTRES = 0; + private static final int DEFAULT_MAX_CENTRES = 5; + private static final int DEFAULT_MIN_REFUGES = 0; + private static final int DEFAULT_MAX_REFUGES = 5; + private static final int DEFAULT_MIN_FIRES = 1; + private static final int DEFAULT_MAX_FIRES = 10; + + private int minCivs; + private int maxCivs; + private int minFBs; + private int maxFBs; + private int minFSs; + private int maxFSs; + private int minPOs; + private int maxPOs; + private int minPFs; + private int maxPFs; + private int minATs; + private int maxATs; + private int minACs; + private int maxACs; + private int minFires; + private int maxFires; + private int minRefuges; + private int maxRefuges; + + /** + * Construct a RandomScenarioGenerator with default parameters. + */ + public RandomScenarioGenerator() { + minCivs = DEFAULT_MIN_CIVS; + maxCivs = DEFAULT_MAX_CIVS; + minFBs = DEFAULT_MIN_PLATOONS; + maxFBs = DEFAULT_MAX_PLATOONS; + minPFs = DEFAULT_MIN_PLATOONS; + maxPFs = DEFAULT_MAX_PLATOONS; + minATs = DEFAULT_MIN_PLATOONS; + maxATs = DEFAULT_MAX_PLATOONS; + minFSs = DEFAULT_MIN_CENTRES; + maxFSs = DEFAULT_MAX_CENTRES; + minPOs = DEFAULT_MIN_CENTRES; + maxPOs = DEFAULT_MAX_CENTRES; + minACs = DEFAULT_MIN_CENTRES; + maxACs = DEFAULT_MAX_CENTRES; + minFires = DEFAULT_MIN_FIRES; + maxFires = DEFAULT_MAX_FIRES; + minRefuges = DEFAULT_MIN_REFUGES; + maxRefuges = DEFAULT_MAX_REFUGES; + } + + /** + * Entry point. + * + * @param args Command line arguments: <map directory> [-civ min max] [-fb min + * max] [-fs min max] [-pf min max] [-po min max] [-at min max] [-ac + * min max] [-refuge min max] [-fire min max]. + */ + public static void main(String[] args) { + + if (args.length < 1) { + printUsage(); + return; + } + + String dirName = args[0]; + RandomScenarioGenerator generator = new RandomScenarioGenerator(); + for (int i = 1; i < args.length; ++i) { + if ("-civ".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setCivilians(min, max); + } else if ("-fb".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setFireBrigades(min, max); + } else if ("-fs".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setFireStations(min, max); + } else if ("-pf".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setPoliceForces(min, max); + } else if ("-po".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setPoliceOffices(min, max); + } else if ("-at".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setAmbulanceTeams(min, max); + } else if ("-ac".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setAmbulanceCentres(min, max); + } else if ("-refuge".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setRefuges(min, max); + } else if ("-fire".equals(args[i])) { + int min = Integer.parseInt(args[i + 1]); + int max = Integer.parseInt(args[i + 2]); + i += 2; + generator.setFires(min, max); + } + } + try { + File dir = new File(dirName); + GMLMap map = (GMLMap) MapReader.readMap(new File(dir, "map.gml")); + GisScenario s = generator.makeRandomScenario(map, new Random()); + Document doc = DocumentHelper.createDocument(); + s.write(doc); + XMLWriter writer = new XMLWriter(new FileOutputStream(new File(dir, "scenario.xml")), + OutputFormat.createPrettyPrint()); + writer.write(doc); + writer.flush(); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } catch (MapException e) { + e.printStackTrace(); + } + } + + private static void printUsage() { + System.out.println("Usage: Launch Random Scenario Generator [map] [options]"); + System.out.println(); + System.out.println("[map] Map directory to generate random scenario"); + System.out.println(); + System.out.println("[options]"); + System.out.println("-civ\tmin max\tSet the minimum and maximum number of civilians"); + System.out.println("-fb\tmin max\tSet the minimum and maximum number of fire brigades"); + System.out.println("-fs\tmin max\tSet the minimum and maximum number of fire stations"); + System.out.println("-pf\tmin max\tSet the minimum and maximum number of police forces"); + System.out.println("-po\tmin max\tSet the minimum and maximum number of police offices"); + System.out.println("-at\tmin max\tSet the minimum and maximum number of ambulance teams"); + System.out.println("-ac\tmin max\tSet the minimum and maximum number of ambulance centers"); + System.out.println("-refuge\tmin max\tSet the minimum and maximum number of refuges"); + System.out.println("-fire\tmin max\tSet the minimum and maximum number of fires"); + } + + /** + * Set the minimum and maximum number of civilians. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setCivilians(int min, int max) { + minCivs = min; + maxCivs = max; + } + + /** + * Set the minimum and maximum number of fire brigades. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setFireBrigades(int min, int max) { + minFBs = min; + maxFBs = max; + } + + /** + * Set the minimum and maximum number of fire stations. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setFireStations(int min, int max) { + minFSs = min; + maxFSs = max; + } + + /** + * Set the minimum and maximum number of police forces. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setPoliceForces(int min, int max) { + minPFs = min; + maxPFs = max; + } + + /** + * Set the minimum and maximum number of police offices. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setPoliceOffices(int min, int max) { + minPOs = min; + maxPOs = max; + } + + /** + * Set the minimum and maximum number of ambulance teams. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setAmbulanceTeams(int min, int max) { + minATs = min; + maxATs = max; + } + + /** + * Set the minimum and maximum number of ambulance centres. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setAmbulanceCentres(int min, int max) { + minACs = min; + maxACs = max; + } + + /** + * Set the minimum and maximum number of refuges. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setRefuges(int min, int max) { + minRefuges = min; + maxRefuges = max; + } + + /** + * Set the minimum and maximum number of fires. + * + * @param min The new minimum. + * @param max The new maximum. + */ + public void setFires(int min, int max) { + minFires = min; + maxFires = max; + } + + /** + * Generate a random scenario. + * + * @param map The map to generate a scenario for. + * @param random A source of randomness. + * @return A new Scenario. + */ + public GisScenario makeRandomScenario(GMLMap map, Random random) { + GisScenario result = new GisScenario(); + int civ = random.nextInt(maxCivs - minCivs + 1) + minCivs; + int fb = random.nextInt(maxFBs - minFBs + 1) + minFBs; + int fs = random.nextInt(maxFSs - minFSs + 1) + minFSs; + int pf = random.nextInt(maxPFs - minPFs + 1) + minPFs; + int po = random.nextInt(maxPOs - minPOs + 1) + minPOs; + int at = random.nextInt(maxATs - minATs + 1) + minATs; + int ac = random.nextInt(maxACs - minACs + 1) + minACs; + int fire = random.nextInt(maxFires - minFires + 1) + minFires; + int refuge = random.nextInt(maxRefuges - minRefuges + 1) + minRefuges; + List<GMLBuilding> buildings = new ArrayList<GMLBuilding>(map.getBuildings()); + Collections.shuffle(buildings, random); + Iterator<GMLBuilding> it = buildings.iterator(); + placeRefuges(it, result, refuge); + placeCentres(it, result, fs, po, ac); + placeFires(it, result, fire); + placeAgents(map, result, random, fb, pf, at, civ); + return result; + } + + private void placeRefuges(Iterator<GMLBuilding> it, GisScenario result, int num) { + for (int i = 0; i < num; ++i) { + result.addRefuge(it.next().getID(), 1000, 1000); + } + } + + private void placeCentres(Iterator<GMLBuilding> it, GisScenario result, int fire, int police, int ambulance) { + for (int i = 0; i < fire; ++i) { + result.addFireStation(it.next().getID()); + } + for (int i = 0; i < police; ++i) { + result.addPoliceOffice(it.next().getID()); + } + for (int i = 0; i < ambulance; ++i) { + result.addAmbulanceCentre(it.next().getID()); + } + } + + private void placeFires(Iterator<GMLBuilding> it, GisScenario result, int num) { + for (int i = 0; i < num; ++i) { + result.addFire(it.next().getID()); + } + } + + private void placeAgents(GMLMap map, GisScenario result, Random random, int fire, int police, int ambulance, + int civ) { + List<GMLShape> all = new ArrayList<GMLShape>(map.getAllShapes()); + List<GMLBuilding> buildings = new ArrayList<GMLBuilding>(map.getBuildings()); + for (int i = 0; i < fire; ++i) { + int id = all.get(random.nextInt(all.size())).getID(); + result.addFireBrigade(id); + } + for (int i = 0; i < police; ++i) { + int id = all.get(random.nextInt(all.size())).getID(); + result.addPoliceForce(id); + } + for (int i = 0; i < ambulance; ++i) { + int id = all.get(random.nextInt(all.size())).getID(); + result.addAmbulanceTeam(id); + } + for (int i = 0; i < civ; ++i) { + int id = buildings.get(random.nextInt(buildings.size())).getID(); + result.addCivilian(id); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RandomiseFunction.java b/modules/gis2/src/gis2/scenario/RandomiseFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..7fb1dcb9a327e8005d2436c438a333f6727b3cd5 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RandomiseFunction.java @@ -0,0 +1,40 @@ +package gis2.scenario; + +import gis2.GisScenario; + +import java.util.Random; + +/** + * Function for randomizing a scenario. + */ +public class RandomiseFunction extends AbstractFunction { + private Random random; + + /** + * Construct a randomizer function. + * + * @param editor The editor instance. + */ + public RandomiseFunction(ScenarioEditor editor) { + super(editor); + random = new Random(); + } + + @Override + public String getName() { + return "Randomise"; + } + + @Override + public void execute() { + RandomScenarioGenerator generator = new RandomScenarioGenerator(); + GisScenario s = generator.makeRandomScenario(editor.getMap(), random); + try { + editor.setScenario(editor.getMap(), s); + editor.setChanged(); + editor.updateOverlays(); + } catch (CancelledByUserException e) { + // Ignore + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveAmbulanceCentreTool.java b/modules/gis2/src/gis2/scenario/RemoveAmbulanceCentreTool.java new file mode 100644 index 0000000000000000000000000000000000000000..3f905e62b18bd6927259177725bdb4fa9b2ef775 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveAmbulanceCentreTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing ambulance centres. + */ +public class RemoveAmbulanceCentreTool extends ShapeTool { + /** + * Construct a RemoveAmbulanceCentreTool. + * + * @param editor The editor instance. + */ + public RemoveAmbulanceCentreTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove ambulance centre"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeAmbulanceCentre(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveAmbulanceCentreEdit(shape.getID())); + } + + private class RemoveAmbulanceCentreEdit extends AbstractUndoableEdit { + private int id; + + public RemoveAmbulanceCentreEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addAmbulanceCentre(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeAmbulanceCentre(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveAmbulanceTeamTool.java b/modules/gis2/src/gis2/scenario/RemoveAmbulanceTeamTool.java new file mode 100644 index 0000000000000000000000000000000000000000..3094b41e84c952230a4aeeb03291633e069b47d6 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveAmbulanceTeamTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for removing ambulance teams. + */ +public class RemoveAmbulanceTeamTool extends ShapeTool { + /** + * Construct a RemoveAmbulanceTeamTool. + * + * @param editor The editor instance. + */ + public RemoveAmbulanceTeamTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove ambulance team"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeAmbulanceTeam(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveAmbulanceTeamEdit(shape.getID())); + } + + private class RemoveAmbulanceTeamEdit extends AbstractUndoableEdit { + private int id; + + public RemoveAmbulanceTeamEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addAmbulanceTeam(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeAmbulanceTeam(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveCivilianTool.java b/modules/gis2/src/gis2/scenario/RemoveCivilianTool.java new file mode 100644 index 0000000000000000000000000000000000000000..5d87dd740b8d7b2004dacf8d57353b6325bda9d2 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveCivilianTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for removing civilians. + */ +public class RemoveCivilianTool extends ShapeTool { + /** + * Construct a RemoveCivilianTool. + * + * @param editor The editor instance. + */ + public RemoveCivilianTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove civilian"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeCivilian(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveCivilianEdit(shape.getID())); + } + + private class RemoveCivilianEdit extends AbstractUndoableEdit { + private int id; + + public RemoveCivilianEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addCivilian(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeCivilian(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveFireBrigadeTool.java b/modules/gis2/src/gis2/scenario/RemoveFireBrigadeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..26d513aa0ee4fb98d986b7649ae7839fe50782a1 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveFireBrigadeTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for removing fire brigades. + */ +public class RemoveFireBrigadeTool extends ShapeTool { + /** + * Construct a RemoveFireBrigadeTool. + * + * @param editor The editor instance. + */ + public RemoveFireBrigadeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove fire brigade"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeFireBrigade(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveFireBrigadeEdit(shape.getID())); + } + + private class RemoveFireBrigadeEdit extends AbstractUndoableEdit { + private int id; + + public RemoveFireBrigadeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addFireBrigade(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeFireBrigade(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveFireStationTool.java b/modules/gis2/src/gis2/scenario/RemoveFireStationTool.java new file mode 100644 index 0000000000000000000000000000000000000000..433dfaf6e45edc7bfa9ffca2bcb9201a0f1fcf56 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveFireStationTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing fire stations. + */ +public class RemoveFireStationTool extends ShapeTool { + /** + * Construct a RemoveFireStationTool. + * + * @param editor The editor instance. + */ + public RemoveFireStationTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove fire station"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeFireStation(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveFireStationEdit(shape.getID())); + } + + private class RemoveFireStationEdit extends AbstractUndoableEdit { + private int id; + + public RemoveFireStationEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addFireStation(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeFireStation(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveFireTool.java b/modules/gis2/src/gis2/scenario/RemoveFireTool.java new file mode 100644 index 0000000000000000000000000000000000000000..b6115a568436a89205abff3e9a9d4a8db5d2b725 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveFireTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing fires. + */ +public class RemoveFireTool extends ShapeTool { + /** + * Construct a RemoveFireTool. + * + * @param editor The editor instance. + */ + public RemoveFireTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove fire"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeFire(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveFireEdit(shape.getID())); + } + + private class RemoveFireEdit extends AbstractUndoableEdit { + private int id; + + public RemoveFireEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addFire(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeFire(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveGasStationTool.java b/modules/gis2/src/gis2/scenario/RemoveGasStationTool.java new file mode 100644 index 0000000000000000000000000000000000000000..b4160f73861db849e47b6a07ac61112eb0a8d96e --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveGasStationTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing refuges. + */ +public class RemoveGasStationTool extends ShapeTool { + /** + * Construct a RemoveGasStationTool. + * + * @param editor The editor instance. + */ + public RemoveGasStationTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove gas station"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeGasStation(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveGasStationEdit(shape.getID())); + } + + private class RemoveGasStationEdit extends AbstractUndoableEdit { + private int id; + + public RemoveGasStationEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addGasStation(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeGasStation(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveHydrantTool.java b/modules/gis2/src/gis2/scenario/RemoveHydrantTool.java new file mode 100644 index 0000000000000000000000000000000000000000..d4ddc256c0abf714173ae02d7acf0ba5528de947 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveHydrantTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLRoad; +import maps.gml.GMLShape; + +/** + * Tool for removing refuges. + */ +public class RemoveHydrantTool extends ShapeTool { + /** + * Construct a RemoveHydrantTool. + * + * @param editor The editor instance. + */ + public RemoveHydrantTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove hydrant"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLRoad; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeHydrant(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveHydrantEdit(shape.getID())); + } + + private class RemoveHydrantEdit extends AbstractUndoableEdit { + private int id; + + public RemoveHydrantEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addHydrant(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeHydrant(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemovePoliceForceTool.java b/modules/gis2/src/gis2/scenario/RemovePoliceForceTool.java new file mode 100644 index 0000000000000000000000000000000000000000..a872a495b6a79db93da3d814fe9113bbeede34fc --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemovePoliceForceTool.java @@ -0,0 +1,59 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLShape; + +/** + * Tool for removing police forces. + */ +public class RemovePoliceForceTool extends ShapeTool { + /** + * Construct a RemovePoliceForceTool. + * + * @param editor The editor instance. + */ + public RemovePoliceForceTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove police force"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return true; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removePoliceForce(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemovePoliceForceEdit(shape.getID())); + } + + private class RemovePoliceForceEdit extends AbstractUndoableEdit { + private int id; + + public RemovePoliceForceEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addPoliceForce(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removePoliceForce(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemovePoliceOfficeTool.java b/modules/gis2/src/gis2/scenario/RemovePoliceOfficeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..d24cd360ed943bf2cd5285bde803cc932d49f447 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemovePoliceOfficeTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing police offices. + */ +public class RemovePoliceOfficeTool extends ShapeTool { + /** + * Construct a RemovePoliceOfficeTool. + * + * @param editor The editor instance. + */ + public RemovePoliceOfficeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove police office"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removePoliceOffice(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemovePoliceOfficeEdit(shape.getID())); + } + + private class RemovePoliceOfficeEdit extends AbstractUndoableEdit { + private int id; + + public RemovePoliceOfficeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addPoliceOffice(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removePoliceOffice(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/RemoveRefugeTool.java b/modules/gis2/src/gis2/scenario/RemoveRefugeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..04de06d89fa9d5eb76d09872ce63fabb54cc008a --- /dev/null +++ b/modules/gis2/src/gis2/scenario/RemoveRefugeTool.java @@ -0,0 +1,60 @@ +package gis2.scenario; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLShape; + +/** + * Tool for removing refuges. + */ +public class RemoveRefugeTool extends ShapeTool { + /** + * Construct a RemoveRefugeTool. + * + * @param editor The editor instance. + */ + public RemoveRefugeTool(ScenarioEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Remove refuge"; + } + + @Override + protected boolean shouldHighlight(GMLShape shape) { + return shape instanceof GMLBuilding; + } + + @Override + protected void processClick(GMLShape shape) { + editor.getScenario().removeRefuge(shape.getID()); + editor.setChanged(); + editor.updateOverlays(); + editor.addEdit(new RemoveRefugeEdit(shape.getID())); + } + + private class RemoveRefugeEdit extends AbstractUndoableEdit { + private int id; + + public RemoveRefugeEdit(int id) { + this.id = id; + } + + @Override + public void undo() { + super.undo(); + editor.getScenario().addRefuge(id); + editor.updateOverlays(); + } + + @Override + public void redo() { + super.redo(); + editor.getScenario().removeRefuge(id); + editor.updateOverlays(); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ScenarioEditor.java b/modules/gis2/src/gis2/scenario/ScenarioEditor.java new file mode 100755 index 0000000000000000000000000000000000000000..5ae933fc2f6627104dbb2092e6ee513ecbffaf61 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ScenarioEditor.java @@ -0,0 +1,852 @@ +package gis2.scenario; + +import gis2.GisScenario; +import gis2.ScenarioException; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ButtonGroup; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; +import javax.swing.filechooser.FileFilter; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.UndoManager; +import javax.swing.undo.UndoableEdit; +import maps.MapException; +import maps.MapReader; +import maps.gml.GMLMap; +import maps.gml.GMLRefuge; +import maps.gml.view.DecoratorOverlay; +import maps.gml.view.FilledShapeDecorator; +import maps.gml.view.GMLMapViewer; +import maps.gml.view.GMLObjectInspector; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; +import rescuecore2.config.Config; + +/** + * A component for editing scenarios. + */ +public class ScenarioEditor extends JPanel { + + private static final int VIEWER_PREFERRED_SIZE = 500; + private static final int INSPECTOR_PREFERRED_WIDTH = 300; + private static final int INSPECTOR_PREFERRED_HEIGHT = 500; + + private static final Color FIRE_COLOUR = new Color(255, 0, 0, 128); + private static final Color FIRE_STATION_COLOUR = new Color(255, 255, 0); + private static final Color POLICE_OFFICE_COLOUR = new Color(0, 0, 255); + private static final Color AMBULANCE_CENTRE_COLOUR = new Color(255, 255, 255); + private static final Color REFUGE_COLOUR = new Color(0, 128, 0); + private static final Color HYDRANT_COLOUR = new Color(128, 128, 0); + private static final Color GAS_STATION_COLOUR = new Color(255, 128, 0); + + private GMLMap map; + private GMLMapViewer viewer; + private GMLObjectInspector inspector; + private DecoratorOverlay fireOverlay; + private DecoratorOverlay centreOverlay; + private transient AgentOverlay agentOverlay; + private GisScenario scenario; + private Tool currentTool; + private JLabel statusLabel; + + private boolean changed; + + private UndoManager undoManager; + private transient Action undoAction; + private transient Action redoAction; + + private File baseDir; + private File saveFile; + + private FilledShapeDecorator fireDecorator = new FilledShapeDecorator( + FIRE_COLOUR, null, null); + private FilledShapeDecorator fireStationDecorator = new FilledShapeDecorator( + FIRE_STATION_COLOUR, null, null); + private FilledShapeDecorator policeOfficeDecorator = new FilledShapeDecorator( + POLICE_OFFICE_COLOUR, null, null); + private FilledShapeDecorator ambulanceCentreDecorator = new FilledShapeDecorator( + AMBULANCE_CENTRE_COLOUR, null, null); + private FilledShapeDecorator refugeDecorator = new FilledShapeDecorator( + REFUGE_COLOUR, null, null); + private FilledShapeDecorator gasStationDecorator = new FilledShapeDecorator( + GAS_STATION_COLOUR, null, null); + private FilledShapeDecorator hydrantDecorator = new FilledShapeDecorator(null, + HYDRANT_COLOUR, null); + + /** + * Construct a new ScenarioEditor. + * + * @param menuBar + * The menu bar to add menus to. + */ + public ScenarioEditor(JMenuBar menuBar) { + this(menuBar, null, null); + } + + + /** + * Construct a new ScenarioEditor. + * + * @param menuBar + * The menu bar to add menus to. + * @param map + * The GMLMap to view. + * @param scenario + * The scenario to edit. + */ + public ScenarioEditor(JMenuBar menuBar, GMLMap map, GisScenario scenario) { + super(new BorderLayout()); + this.map = map; + this.scenario = scenario; + viewer = new GMLMapViewer(map); + viewer.setPaintNodes(false); + statusLabel = new JLabel("Status"); + fireOverlay = new DecoratorOverlay(); + centreOverlay = new DecoratorOverlay(); + agentOverlay = new AgentOverlay(this); + viewer.addOverlay(fireOverlay); + viewer.addOverlay(centreOverlay); + viewer.addOverlay(agentOverlay); + inspector = new GMLObjectInspector(map); + undoManager = new UndoManager(); + viewer.setPreferredSize( + new Dimension(VIEWER_PREFERRED_SIZE, VIEWER_PREFERRED_SIZE)); + inspector.setPreferredSize( + new Dimension(INSPECTOR_PREFERRED_WIDTH, INSPECTOR_PREFERRED_HEIGHT)); + viewer.setBackground(Color.GRAY); + viewer.getPanZoomListener().setPanOnRightMouse(); + changed = false; + JToolBar fileToolbar = new JToolBar("File"); + JToolBar editToolbar = new JToolBar("Edit"); + JToolBar toolsToolbar = new JToolBar("Tools"); + toolsToolbar.setLayout(new ModifiedFlowLayout()); + JToolBar functionsToolbar = new JToolBar("Functions"); + JMenu fileMenu = new JMenu("File", false); + JMenu editMenu = new JMenu("Edit", false); + JMenu toolsMenu = new JMenu("Tools", false); + JMenu functionsMenu = new JMenu("Functions", false); + + createFileActions(fileMenu, fileToolbar); + createEditActions(editMenu, editToolbar); + createToolActions(toolsMenu, toolsToolbar); + createFunctionActions(functionsMenu, functionsToolbar); + + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewer, + inspector); + add(split, BorderLayout.CENTER); + JPanel toolbars = new JPanel(new ModifiedFlowLayout()); + toolbars.add(fileToolbar); + toolbars.add(editToolbar); + toolbars.add(functionsToolbar); + toolbars.add(toolsToolbar); + add(toolbars, BorderLayout.NORTH); + add(statusLabel, BorderLayout.SOUTH); + menuBar.add(fileMenu); + menuBar.add(editMenu); + menuBar.add(toolsMenu); + menuBar.add(functionsMenu); + + baseDir = new File(System.getProperty("user.dir")); + saveFile = null; + } + + + /** + * Entry point. + * + * @param args + * Command line arguments. + */ + public static void main(String[] args) { + final JFrame frame = new JFrame("Scenario Editor"); + JMenuBar menuBar = new JMenuBar(); + final ScenarioEditor editor = new ScenarioEditor(menuBar); + if (args.length > 0 && args[0].length() > 0) { + try { + editor.load(args[0]); + } catch (CancelledByUserException e) { + return; + } catch (MapException e) { + e.printStackTrace(); + } catch (ScenarioException e) { + e.printStackTrace(); + } catch (rescuecore2.scenario.exceptions.ScenarioException e) { + e.printStackTrace(); + } + } + + frame.setJMenuBar(menuBar); + frame.setContentPane(editor); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + frame.pack(); + frame.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + try { + editor.close(); + frame.setVisible(false); + frame.dispose(); + System.exit(0); + } catch (CancelledByUserException ex) { + frame.setVisible(true); + } + } + }); + frame.setVisible(true); + } + + + /** + * Load a map and scenario by showing a file chooser dialog. + * + * @throws CancelledByUserException + * If the user cancels + * the change due to + * unsaved changes. + * @throws MapException + * If there is a + * problem reading the + * map. + * @throws ScenarioException + * If there is a + * problem reading the + * scenario. + * @throws rescuecore2.scenario.exceptions.ScenarioException + */ + public void load() throws CancelledByUserException, MapException, + ScenarioException, rescuecore2.scenario.exceptions.ScenarioException { + JFileChooser chooser = new JFileChooser(baseDir); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setFileFilter(new FileFilter() { + + @Override + public boolean accept(File f) { + return f.isDirectory(); + } + + + @Override + public String getDescription() { + return "Directories"; + } + }); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + load(chooser.getSelectedFile()); + } + } + + + /** + * Load a map and scenario from a directory. + * + * @param filename + * The name of the file to read. + * + * @throws CancelledByUserException + * If the user cancels + * the change due to + * unsaved changes. + * @throws MapException + * If there is a + * problem reading the + * map. + * @throws ScenarioException + * If there is a + * problem reading the + * scenario. + * @throws rescuecore2.scenario.exceptions.ScenarioException + */ + public void load(String filename) + throws CancelledByUserException, MapException, ScenarioException, + rescuecore2.scenario.exceptions.ScenarioException { + load(new File(filename)); + } + + + /** + * Load a map and scenario from a directory. + * + * @param dir + * The directory to read. + * + * @throws CancelledByUserException + * If the user cancels + * the change due to + * unsaved changes. + * @throws MapException + * If there is a + * problem reading the + * map. + * @throws ScenarioException + * If there is a + * problem reading the + * scenario. + * @throws rescuecore2.scenario.exceptions.ScenarioException + */ + public void load(File dir) throws CancelledByUserException, MapException, + ScenarioException, rescuecore2.scenario.exceptions.ScenarioException { + + try (FileReader r = new FileReader(new File(dir, "scenario.xml"))) { + GMLMap newMap = (GMLMap) MapReader.readMap(new File(dir, "map.gml")); + + SAXReader saxReader = new SAXReader(); + Document doc = saxReader.read(r); + GisScenario newScenario = new GisScenario(doc, new Config()); + setScenario(newMap, newScenario); + baseDir = dir; + saveFile = new File(dir, "scenario.xml"); + } catch (IOException | DocumentException e) { + throw new ScenarioException(e); + } + } + + + /** + * Set the map and scenario. + * + * @param newMap + * The new map. + * @param newScenario + * The new scenario. + * + * @throws CancelledByUserException + * If the user cancels the change due to + * unsaved changes. + */ + public void setScenario(GMLMap newMap, GisScenario newScenario) + throws CancelledByUserException { + checkForChanges(); + if (!checkScenario(newMap, newScenario)) { + JOptionPane.showMessageDialog(null, + "The scenario file contained errors."); + return; + } + map = newMap; + scenario = newScenario; + changed = false; + viewer.setMap(map); + inspector.setMap(map); + updateOverlays(); + } + + + public void updateGMLRefuges() { + for (int next : scenario.getRefuges()) { + GMLRefuge refuge = new GMLRefuge(next, map.getBuilding(next).getEdges()); + refuge.setBedCapacity(scenario.getRefugeBedCapacity().get(next)); + refuge.setRefillCapacity(scenario.getRefugeRefillCapacity().get(next)); + scenario.addGMLRefuge(refuge); + } + } + + + /** + * Get the map. + * + * @return The map. + */ + public GMLMap getMap() { + return map; + } + + + /** + * Get the scenario. + * + * @return The scenario. + */ + public GisScenario getScenario() { + return scenario; + } + + + /** + * Save the scenario. + * + * @throws ScenarioException + * If there is a problem saving the scenario. + */ + public void save() throws ScenarioException { + if (saveFile == null) { + saveAs(); + } + if (saveFile != null) { + Document doc = DocumentHelper.createDocument(); + scenario.write(doc); + try { + if (!saveFile.exists()) { + File parent = saveFile.getParentFile(); + if ((!parent.exists()) && (!saveFile.getParentFile().mkdirs())) { + throw new ScenarioException( + "Couldn't create file " + saveFile.getPath()); + } + if (!saveFile.createNewFile()) { + throw new ScenarioException( + "Couldn't create file " + saveFile.getPath()); + } + } + XMLWriter writer = new XMLWriter(new FileOutputStream(saveFile), + OutputFormat.createPrettyPrint()); + writer.write(doc); + writer.flush(); + writer.close(); + } catch (IOException e) { + throw new ScenarioException(e); + } + baseDir = saveFile.getParentFile(); + changed = false; + } + } + + + /** + * Save the scenario. + * + * @throws ScenarioException + * If there is a problem saving the scenario. + */ + public void saveAs() throws ScenarioException { + JFileChooser chooser = new JFileChooser(baseDir); + if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { + saveFile = chooser.getSelectedFile(); + save(); + } + } + + + /** + * Close the editor. + * + * @throws CancelledByUserException + * If the user cancels the close due to unsaved + * changes." + */ + public void close() throws CancelledByUserException { + checkForChanges(); + } + + + /** + * Get the map viewer. + * + * @return The map viewer. + */ + public GMLMapViewer getViewer() { + return viewer; + } + + + /** + * Get the object inspector. + * + * @return The object inspector. + */ + public GMLObjectInspector getInspector() { + return inspector; + } + + + /** + * Register a change to the map. + */ + public void setChanged() { + changed = true; + } + + + /** + * Register an undoable edit. + * + * @param edit + * The edit to add. + */ + public void addEdit(UndoableEdit edit) { + undoManager.addEdit(edit); + undoAction.setEnabled(undoManager.canUndo()); + redoAction.setEnabled(undoManager.canRedo()); + } + + + /** + * Update the overlay views. + */ + public void updateOverlays() { + updateGMLRefuges(); + updateFireOverlay(); + updateCentreOverlay(); + updateAgentOverlay(); + updateStatusLabel(); + viewer.repaint(); + } + + + private void checkForChanges() throws CancelledByUserException { + if (changed) { + switch (JOptionPane.showConfirmDialog(null, + "The current scenario has changes. Do you want to save them?")) { + case JOptionPane.YES_OPTION: + try { + save(); + } catch (ScenarioException e) { + JOptionPane.showMessageDialog(null, e); + throw new CancelledByUserException(); + } + break; + + case JOptionPane.NO_OPTION: + changed = false; + return; + + case JOptionPane.CANCEL_OPTION: + throw new CancelledByUserException(); + + default: + throw new RuntimeException( + "JOptionPane.showConfirmDialog returned something weird"); + } + } + } + + + private void createFileActions(JMenu menu, JToolBar toolbar) { + Action newAction = new AbstractAction("New") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + checkForChanges(); + setScenario(map, new GisScenario()); + } catch (CancelledByUserException ex) { + } + } + }; + Action loadAction = new AbstractAction("Load") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + checkForChanges(); + load(); + } catch (CancelledByUserException ex) { + } catch (MapException | ScenarioException ex) { + JOptionPane.showMessageDialog(null, ex); + } catch (rescuecore2.scenario.exceptions.ScenarioException ex) { + ex.printStackTrace(); + } + } + }; + Action saveAction = new AbstractAction("Save") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + save(); + } catch (ScenarioException ex) { + JOptionPane.showMessageDialog(null, ex); + } + } + }; + Action saveAsAction = new AbstractAction("Save as") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + saveAs(); + } catch (ScenarioException ex) { + JOptionPane.showMessageDialog(null, ex); + } + } + }; + toolbar.add(newAction); + toolbar.add(loadAction); + toolbar.add(saveAction); + toolbar.add(saveAsAction); + menu.add(newAction); + menu.add(loadAction); + menu.add(saveAction); + menu.add(saveAsAction); + } + + + private void createEditActions(JMenu menu, JToolBar toolbar) { + undoAction = new AbstractAction("Undo") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + undoManager.undo(); + } catch (CannotUndoException ex) { + JOptionPane.showMessageDialog(null, ex); + } + setEnabled(undoManager.canUndo()); + redoAction.setEnabled(undoManager.canRedo()); + } + }; + redoAction = new AbstractAction("Redo") { + + @Override + public void actionPerformed(ActionEvent e) { + try { + undoManager.redo(); + } catch (CannotUndoException ex) { + JOptionPane.showMessageDialog(null, ex); + } + setEnabled(undoManager.canRedo()); + undoAction.setEnabled(undoManager.canUndo()); + } + }; + undoAction.setEnabled(false); + redoAction.setEnabled(false); + toolbar.add(undoAction); + toolbar.add(redoAction); + menu.add(undoAction); + menu.add(redoAction); + } + + + private void createToolActions(JMenu menu, JToolBar toolbar) { + ButtonGroup toolbarGroup = new ButtonGroup(); + ButtonGroup menuGroup = new ButtonGroup(); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new PlaceFireTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new RemoveFireTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new PlaceRefugeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new RemoveRefugeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new PlaceGasStationTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveGasStationTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlaceHydrantTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new RemoveHydrantTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlaceCivilianTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveCivilianTool(this), menu, toolbar, menuGroup, + toolbarGroup); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new PlaceFireBrigadeTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveFireBrigadeTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlacePoliceForceTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemovePoliceForceTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlaceAmbulanceTeamTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveAmbulanceTeamTool(this), menu, toolbar, menuGroup, + toolbarGroup); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new PlaceFireStationTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveFireStationTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlacePoliceOfficeTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemovePoliceOfficeTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new PlaceAmbulanceCentreTool(this), menu, toolbar, menuGroup, + toolbarGroup); + addTool(new RemoveAmbulanceCentreTool(this), menu, toolbar, menuGroup, + toolbarGroup); + } + + + private void createFunctionActions(JMenu menu, JToolBar toolbar) { + addFunction(new RandomiseFunction(this), menu, toolbar); + addFunction(new ClearFiresFunction(this), menu, toolbar); + addFunction(new ClearAgentsFunction(this), menu, toolbar); + addFunction(new ClearAllFunction(this), menu, toolbar); + addFunction(new PlaceAgentsFunction(this), menu, toolbar); + addFunction(new RandomHydrantPlacementFunction(this), menu, toolbar); + } + + + private void addTool(final Tool t, JMenu menu, JToolBar toolbar, + ButtonGroup menuGroup, ButtonGroup toolbarGroup) { + final JToggleButton toggle = new JToggleButton(); + final JCheckBoxMenuItem check = new JCheckBoxMenuItem(); + Action action = new AbstractAction(t.getName()) { + + @Override + public void actionPerformed(ActionEvent e) { + if (currentTool != null) { + currentTool.deactivate(); + } + currentTool = t; + toggle.setSelected(true); + check.setSelected(true); + currentTool.activate(); + } + }; + toggle.setAction(action); + check.setAction(action); + menu.add(check); + toolbar.add(toggle); + menuGroup.add(check); + toolbarGroup.add(toggle); + } + + + private void addFunction(final Function f, JMenu menu, JToolBar toolbar) { + Action action = new AbstractAction(f.getName()) { + + @Override + public void actionPerformed(ActionEvent e) { + f.execute(); + } + }; + toolbar.add(action); + menu.add(action); + } + + + private boolean checkScenario(GMLMap newMap, GisScenario newScenario) { + boolean valid = true; + for (int id : newScenario.getFires()) { + if (newMap.getBuilding(id) == null) { + valid = false; + } + } + for (int id : newScenario.getRefuges()) { + if (newMap.getBuilding(id) == null) { + valid = false; + } + } + for (int id : newScenario.getHydrants()) { + if (newMap.getRoad(id) == null) { + valid = false; + } + } + for (int id : newScenario.getFireStations()) { + if (newMap.getBuilding(id) == null) { + valid = false; + } + } + for (int id : newScenario.getAmbulanceCentres()) { + if (newMap.getBuilding(id) == null) { + valid = false; + } + } + for (int id : newScenario.getPoliceOffices()) { + if (newMap.getBuilding(id) == null) { + valid = false; + } + } + + for (int id : newScenario.getCivilians()) { + if (newMap.getShape(id) == null) { + valid = false; + } + } + for (int id : newScenario.getFireBrigades()) { + if (newMap.getShape(id) == null) { + valid = false; + } + } + for (int id : newScenario.getAmbulanceTeams()) { + if (newMap.getShape(id) == null) { + valid = false; + } + } + for (int id : newScenario.getPoliceForces()) { + if (newMap.getShape(id) == null) { + valid = false; + } + } + return valid; + } + + + private void updateStatusLabel() { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + statusLabel.setText(scenario.getFires().size() + " fires, " + + scenario.getRefuges().size() + " refuges, " + + scenario.getHydrants().size() + " hydrants, " + + scenario.getGasStations().size() + " gas stations, " + + scenario.getCivilians().size() + " civilians, " + + scenario.getFireBrigades().size() + " fb, " + + scenario.getFireStations().size() + " fs, " + + scenario.getPoliceForces().size() + " pf, " + + scenario.getPoliceOffices().size() + " po, " + + scenario.getAmbulanceTeams().size() + " at, " + + scenario.getAmbulanceCentres().size() + " ac"); + } + }); + } + + + private void updateFireOverlay() { + fireOverlay.clearAllBuildingDecorators(); + for (int next : scenario.getFires()) { + fireOverlay.setBuildingDecorator(fireDecorator, map.getBuilding(next)); + } + } + + + private void updateCentreOverlay() { + centreOverlay.clearAllBuildingDecorators(); + centreOverlay.clearAllRoadDecorators(); + for (int next : scenario.getFireStations()) { + centreOverlay.setBuildingDecorator(fireStationDecorator, + map.getBuilding(next)); + } + for (int next : scenario.getPoliceOffices()) { + centreOverlay.setBuildingDecorator(policeOfficeDecorator, + map.getBuilding(next)); + } + for (int next : scenario.getAmbulanceCentres()) { + centreOverlay.setBuildingDecorator(ambulanceCentreDecorator, + map.getBuilding(next)); + } + for (int next : scenario.getRefuges()) { + + centreOverlay.setBuildingDecorator(refugeDecorator, + scenario.getRefuge(next)); + } + for (int next : scenario.getGasStations()) { + centreOverlay.setBuildingDecorator(gasStationDecorator, + map.getBuilding(next)); + } + for (int next : scenario.getHydrants()) { + centreOverlay.setRoadDecorator(hydrantDecorator, map.getRoad(next)); + } + } + + + private void updateAgentOverlay() { + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/ShapeTool.java b/modules/gis2/src/gis2/scenario/ShapeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..22a108a7222e975124072dada4e5d34e42d3171b --- /dev/null +++ b/modules/gis2/src/gis2/scenario/ShapeTool.java @@ -0,0 +1,145 @@ +package gis2.scenario; + +import java.awt.Color; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +import maps.gml.GMLBuilding; +import maps.gml.GMLCoordinates; +import maps.gml.GMLRoad; +import maps.gml.GMLShape; +import maps.gml.GMLSpace; +import maps.gml.view.FilledShapeDecorator; + +/** + * Abstract base class for tools that operate on GML shapes. + */ +public abstract class ShapeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = new Color(0, 0, 255, 128); + + private Listener listener; + private FilledShapeDecorator highlight; + + private GMLShape highlightShape; + + /** + * Construct a ShapeTool. + * + * @param editor The editor instance. + */ + public ShapeTool(ScenarioEditor editor) { + super(editor); + listener = new Listener(); + highlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR); + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + highlightShape = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllBuildingDecorators(); + editor.getViewer().clearAllRoadDecorators(); + editor.getViewer().clearAllSpaceDecorators(); + editor.getViewer().repaint(); + } + + /** + * Handle a click on a shape. + * + * @param shape The shape that was clicked. + */ + protected abstract void processClick(GMLShape shape); + + /** + * Find out if a shape should be highlighted or not. Only highlighted shapes can + * be clicked. + * + * @param shape The shape to check. + * @return True if the shape should be highlighted, false otherwise. + */ + protected abstract boolean shouldHighlight(GMLShape shape); + + private void highlight(GMLShape newShape) { + if (!shouldHighlight(newShape)) { + return; + } + if (highlightShape == newShape) { + return; + } + if (highlightShape != null) { + if (highlightShape instanceof GMLBuilding) { + editor.getViewer().clearBuildingDecorator((GMLBuilding) highlightShape); + } + if (highlightShape instanceof GMLRoad) { + editor.getViewer().clearRoadDecorator((GMLRoad) highlightShape); + } + if (highlightShape instanceof GMLSpace) { + editor.getViewer().clearSpaceDecorator((GMLSpace) highlightShape); + } + } + highlightShape = newShape; + if (highlightShape != null) { + if (highlightShape instanceof GMLBuilding) { + editor.getViewer().setBuildingDecorator(highlight, (GMLBuilding) highlightShape); + } + if (highlightShape instanceof GMLRoad) { + editor.getViewer().setRoadDecorator(highlight, (GMLRoad) highlightShape); + } + if (highlightShape instanceof GMLSpace) { + editor.getViewer().setSpaceDecorator(highlight, (GMLSpace) highlightShape); + } + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (highlightShape != null && e.getButton() == MouseEvent.BUTTON1) { + processClick(highlightShape); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + highlight(editor.getMap().findShapeUnder(c.getX(), c.getY())); + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/gis2/src/gis2/scenario/Tool.java b/modules/gis2/src/gis2/scenario/Tool.java new file mode 100644 index 0000000000000000000000000000000000000000..b0264d7ee0f434fb144d0829377a3bac124aab03 --- /dev/null +++ b/modules/gis2/src/gis2/scenario/Tool.java @@ -0,0 +1,23 @@ +package gis2.scenario; + +/** + * Interface for a scenario editing tool. + */ +public interface Tool { + /** + * Get the name of this tool. + * + * @return The name of the tool. + */ + String getName(); + + /** + * Activate this tool. + */ + void activate(); + + /** + * Deactivate this tool. + */ + void deactivate(); +} \ No newline at end of file diff --git a/modules/human/src/human/ControlledAgentGUI.java b/modules/human/src/human/ControlledAgentGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..e2f52dd710cc7a14bf3b00b555a0b44f2923694d --- /dev/null +++ b/modules/human/src/human/ControlledAgentGUI.java @@ -0,0 +1,350 @@ +package human; + +import rescuecore2.Constants; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.connection.ConnectionException; +import rescuecore2.components.Agent; +import rescuecore2.components.ComponentLauncher; +import rescuecore2.components.TCPComponentLauncher; +import rescuecore2.components.ComponentConnectionException; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; +import rescuecore2.view.RenderedObject; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.view.StandardWorldModelViewer; +import rescuecore2.standard.components.StandardViewer; + +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.BorderFactory; +import javax.swing.AbstractListModel; + +import java.util.List; +import java.util.ArrayList; + +import java.io.IOException; + +/** + GUI for controlled agents. + */ +public class ControlledAgentGUI extends JPanel { + private static final int VIEW_SIZE = 500; + + private List<ControlledFireBrigade> fbs; + private ListListModel fbListModel; + private JList fbList; + private List<ControlledPoliceForce> pfs; + private ListListModel pfListModel; + private JList pfList; + private List<ControlledAmbulanceTeam> ats; + private ListListModel atListModel; + private JList atList; + + /** + Construct a ControlledAgentGUI. + @param view The view of the world. + */ + public ControlledAgentGUI(StandardWorldModelViewer view) { + super(new BorderLayout()); + fbs = new ArrayList<ControlledFireBrigade>(); + fbListModel = new ListListModel(fbs); + fbList = new JList(fbListModel); + pfs = new ArrayList<ControlledPoliceForce>(); + pfListModel = new ListListModel(pfs); + pfList = new JList(pfListModel); + ats = new ArrayList<ControlledAmbulanceTeam>(); + atListModel = new ListListModel(ats); + atList = new JList(atListModel); + // CHECKSTYLE:OFF:MagicNumber + JPanel agents = new JPanel(new GridLayout(3, 1)); + // CHECKSTYLE:ON:MagicNumber + JScrollPane scroll = new JScrollPane(fbList); + scroll.setBorder(BorderFactory.createTitledBorder("Fire brigades")); + agents.add(scroll); + scroll = new JScrollPane(pfList); + scroll.setBorder(BorderFactory.createTitledBorder("Police forces")); + agents.add(scroll); + scroll = new JScrollPane(atList); + scroll.setBorder(BorderFactory.createTitledBorder("Ambulance teams")); + agents.add(scroll); + add(agents, BorderLayout.WEST); + add(view, BorderLayout.CENTER); + view.addViewListener(new ViewListener() { + @Override + public void objectsClicked(ViewComponent view, List<RenderedObject> objects) { + handleClick(objects); + } + @Override + public void objectsRollover(ViewComponent view, List<RenderedObject> objects) { + } + }); + } + + /** + Entry point. + @param args Command-line arguments. + */ + public static void main(String[] args) { + Config config = new Config(); + try { + CommandLineOptions.processArgs(args, config); + } + catch (ConfigException e) { + Logger.error("Configuration error", e); + System.exit(-1); + } + catch (IOException e) { + Logger.error("Configuration error", e); + System.exit(-1); + } + StandardWorldModelViewer view = new StandardWorldModelViewer(); + view.setPreferredSize(new Dimension(VIEW_SIZE, VIEW_SIZE)); + ControlledAgentGUI gui = new ControlledAgentGUI(view); + JFrame frame = new JFrame("Controlled agents"); + frame.add(gui); + frame.pack(); + frame.setVisible(true); + + // Connect a viewer and agents + int port = config.getIntValue(Constants.KERNEL_PORT_NUMBER_KEY, Constants.DEFAULT_KERNEL_PORT_NUMBER); + String host = config.getValue(Constants.KERNEL_HOST_NAME_KEY, Constants.DEFAULT_KERNEL_HOST_NAME); + ComponentLauncher launcher = new TCPComponentLauncher(host, port, config); + ControlViewer viewer = new ControlViewer(view, gui); + try { + launcher.connect(viewer); + } + catch (InterruptedException e) { + Logger.error("Interrupted", e); + System.exit(-1); + } + catch (ConnectionException e) { + Logger.error("Viewer connection failed", e); + System.exit(-1); + } + catch (ComponentConnectionException e) { + Logger.error("Viewer connection failed", e); + System.exit(-1); + } + gui.launchAgents(launcher); + } + + private void launchAgents(ComponentLauncher launcher) { + // Connect as many fire brigades, police forces and ambulance teams as possible, but do it in a new thread. + new AgentConnector(launcher).start(); + } + + private void refreshLists() { + fbListModel.refresh(); + pfListModel.refresh(); + atListModel.refresh(); + } + + private void handleClick(List<RenderedObject> clicked) { + handleFBClick(clicked); + handlePFClick(clicked); + handleATClick(clicked); + } + + private void handleFBClick(List<RenderedObject> clicked) { + for (RenderedObject next : clicked) { + if (next.getObject() instanceof Building) { + Building b = (Building)next.getObject(); + for (ControlledFireBrigade agent : getSelectedFireBrigades()) { + agent.setTarget(b); + } + break; + } + } + fbListModel.refresh(); + } + + private void handlePFClick(List<RenderedObject> clicked) { + for (RenderedObject next : clicked) { + if (next.getObject() instanceof Road) { + Road r = (Road)next.getObject(); + for (ControlledPoliceForce agent : getSelectedPoliceForces()) { + agent.setTarget(r); + } + break; + } + } + pfListModel.refresh(); + } + + private void handleATClick(List<RenderedObject> clicked) { + for (RenderedObject next : clicked) { + if (next.getObject() instanceof Human) { + Human h = (Human)next.getObject(); + for (ControlledAmbulanceTeam agent : getSelectedAmbulanceTeams()) { + agent.setTarget(h); + } + break; + } + } + atListModel.refresh(); + } + + private List<ControlledFireBrigade> getSelectedFireBrigades() { + int[] selected = fbList.getSelectedIndices(); + List<ControlledFireBrigade> agents = new ArrayList<ControlledFireBrigade>(selected.length); + for (int next : selected) { + agents.add(fbs.get(next)); + } + return agents; + } + + private List<ControlledPoliceForce> getSelectedPoliceForces() { + int[] selected = pfList.getSelectedIndices(); + List<ControlledPoliceForce> agents = new ArrayList<ControlledPoliceForce>(selected.length); + for (int next : selected) { + agents.add(pfs.get(next)); + } + return agents; + } + + private List<ControlledAmbulanceTeam> getSelectedAmbulanceTeams() { + int[] selected = atList.getSelectedIndices(); + List<ControlledAmbulanceTeam> agents = new ArrayList<ControlledAmbulanceTeam>(selected.length); + for (int next : selected) { + agents.add(ats.get(next)); + } + return agents; + } + + private static class ControlViewer extends StandardViewer { + private StandardWorldModelViewer view; + private ControlledAgentGUI gui; + + public ControlViewer(StandardWorldModelViewer view, ControlledAgentGUI gui) { + this.view = view; + this.gui = gui; + } + + @Override + protected void postConnect() { + view.view(model); + } + + @Override + protected void handleTimestep(KVTimestep t) { + super.handleTimestep(t); + view.repaint(); + gui.refreshLists(); + } + } + + private static class ListListModel extends AbstractListModel { + private List<?> data; + + public ListListModel(List<?> data) { + this.data = data; + } + + @Override + public int getSize() { + return data.size(); + } + + @Override + public Object getElementAt(int index) { + return data.get(index); + } + + public void refresh() { + fireContentsChanged(this, 0, data.size()); + } + } + + private class AgentConnector extends Thread { + private ComponentLauncher launcher; + + public AgentConnector(ComponentLauncher launcher) { + this.launcher = launcher; + } + + @Override + public void run() { + connectAgents(new FireBrigadeAgentType(), fbs, fbListModel); + connectAgents(new PoliceForceAgentType(), pfs, pfListModel); + connectAgents(new AmbulanceTeamAgentType(), ats, atListModel); + } + + private <T extends Agent> void connectAgents(AgentType<T> type, List<? super T> list, ListListModel model) { + int count = 0; + while (true) { + ++count; + T agent = type.createAgent(); + try { + launcher.connect(agent); + list.add(agent); + } + catch (ComponentConnectionException e) { + break; + } + catch (InterruptedException e) { + break; + } + catch (ConnectionException e) { + break; + } + } + model.refresh(); + } + } + + private interface AgentType<T extends Agent> { + /** + Create an Agent of the right type. + @return A new Agent implementation. + */ + T createAgent(); + } + + private static class FireBrigadeAgentType implements AgentType<ControlledFireBrigade> { + @Override + public ControlledFireBrigade createAgent() { + return new ControlledFireBrigade(); + } + + @Override + public String toString() { + return "fire brigade"; + } + } + + private static class PoliceForceAgentType implements AgentType<ControlledPoliceForce> { + @Override + public ControlledPoliceForce createAgent() { + return new ControlledPoliceForce(); + } + + @Override + public String toString() { + return "police force"; + } + } + + private static class AmbulanceTeamAgentType implements AgentType<ControlledAmbulanceTeam> { + @Override + public ControlledAmbulanceTeam createAgent() { + return new ControlledAmbulanceTeam(); + } + + @Override + public String toString() { + return "ambulance team"; + } + } +} diff --git a/modules/human/src/human/ControlledAmbulanceTeam.java b/modules/human/src/human/ControlledAmbulanceTeam.java new file mode 100644 index 0000000000000000000000000000000000000000..8059c915ed8c13f3950eec3791627b499adf5add --- /dev/null +++ b/modules/human/src/human/ControlledAmbulanceTeam.java @@ -0,0 +1,135 @@ +package human; + +import static rescuecore2.misc.Handy.objectsToIDs; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.components.StandardAgent; + +import sample.SampleSearch; + +import java.util.Collection; +import java.util.List; +import java.util.EnumSet; + +/** + A basic ambulance team agent that will try to rescue a given target. Once the target is unburied this agent will attempt to load it and transport it to a refuge. If there is no target then this agent does nothing. + */ +public class ControlledAmbulanceTeam extends StandardAgent<AmbulanceTeam> { + private SampleSearch search; + private Human target; + + /** + Set the target of this ambulance team. + @param target The new target. + */ + public void setTarget(Human target) { + this.target = target; + } + + @Override + protected void think(int time, ChangeSet changed, Collection<Command> heard) { + if (target == null) { + Logger.info("Nothing to do."); + return; + } + else { + // Is the target on board? + if (target.getPosition().equals(getID())) { + // Yes + // Are we at a refuge? + if (location() instanceof Refuge) { + sendUnload(time); + return; + } + else { + List<EntityID> path = search.breadthFirstSearch(me().getPosition(), objectsToIDs(model.getEntitiesOfType(StandardEntityURN.REFUGE))); + if (path != null) { + sendMove(time, path); + return; + } + else { + Logger.info("Couldn't plan a path to refuge."); + return; + } + } + } + else { + if (target.getPosition().equals(me().getPosition())) { + // We're at the same location + if (target.getBuriedness() != 0) { + sendRescue(time, target.getID()); + return; + } + else { + // Unburied: try to load + sendLoad(time, target.getID()); + return; + } + } + else { + // Plan a path + List<EntityID> path = search.breadthFirstSearch(me().getPosition(), target.getID()); + if (path != null) { + sendMove(time, path); + return; + } + else { + Logger.info("Couldn't plan a path to target."); + } + } + } + } + } + + @Override + protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() { + return EnumSet.of(StandardEntityURN.AMBULANCE_TEAM); + } + + /** + Get the location of the entity controlled by this agent. + @return The location of the entity controlled by this agent. + */ + protected StandardEntity location() { + AmbulanceTeam me = me(); + return me.getPosition(model); + } + + @Override + protected void postConnect() { + super.postConnect(); + search = new SampleSearch(model); + } + + @Override + public String toString() { + if (me() == null) { + return "Human controlled ambulance team"; + } + StringBuilder result = new StringBuilder(); + result.append("Human controlled ambulance team "); + result.append(getID()); + result.append(" "); + if (target == null) { + result.append("(no target)"); + } + else { + result.append("target: human "); + result.append(target.getID()); + if (target.getPosition().equals(getID())) { + result.append(" (loaded)"); + } + } + return result.toString(); + } +} + diff --git a/modules/human/src/human/ControlledFireBrigade.java b/modules/human/src/human/ControlledFireBrigade.java new file mode 100644 index 0000000000000000000000000000000000000000..c8c17fefd0cbe5d264869f8302665f19b2c5d764 --- /dev/null +++ b/modules/human/src/human/ControlledFireBrigade.java @@ -0,0 +1,114 @@ +package human; + +import static rescuecore2.misc.Handy.objectsToIDs; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.components.StandardAgent; + +import sample.SampleSearch; + +import java.util.List; +import java.util.Collection; +import java.util.EnumSet; + +/** + A basic fire brigade agent that will try to extinguish a given target. If the target is a refuge then the fire brigade will attempt to enter the building to replenish water. If there is no target then this agent does nothing. + */ +public class ControlledFireBrigade extends StandardAgent<FireBrigade> { + private static final int MAX_WATER = 15000; + private static final int EXTINGUISH_DISTANCE = 30000; + private static final int EXTINGUISH_POWER = 1000; + + private SampleSearch search; + private Building target; + + /** + Set the target of this fire brigade. + @param target The new target. + */ + public void setTarget(Building target) { + this.target = target; + } + + @Override + protected void think(int time, ChangeSet changed, Collection<Command> heard) { + if (target == null) { + Logger.info("Nothing to do"); + return; + } + if (target instanceof Refuge) { + // Just go there + List<EntityID> path = search.breadthFirstSearch(me().getPosition(), target.getID()); + if (path != null) { + sendMove(time, path); + return; + } + else { + Logger.info("Couldn't plan a path to refuge."); + } + } + // Are we close enough to extinguish? + int distance = model.getDistance(me(), target); + if (distance < EXTINGUISH_DISTANCE) { + sendExtinguish(time, target.getID(), EXTINGUISH_POWER); + return; + } + // Otherwise plan a path + if (!target.equals(location())) { + List<EntityID> path = planPathToFire(); + if (path != null) { + sendMove(time, path); + return; + } + else { + Logger.info("Couldn't plan a path to target."); + } + } + } + + private List<EntityID> planPathToFire() { + // Try to get to anything within EXTINGUISH_DISTANCE of the target + Collection<StandardEntity> targets = model.getObjectsInRange(target, EXTINGUISH_DISTANCE); + if (targets.isEmpty()) { + return null; + } + return search.breadthFirstSearch(me().getPosition(), objectsToIDs(targets)); + } + + @Override + protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() { + return EnumSet.of(StandardEntityURN.FIRE_BRIGADE); + } + + /** + Get the location of the entity controlled by this agent. + @return The location of the entity controlled by this agent. + */ + protected StandardEntity location() { + FireBrigade me = me(); + return me.getPosition(model); + } + + @Override + protected void postConnect() { + super.postConnect(); + search = new SampleSearch(model); + } + + @Override + public String toString() { + if (me() == null) { + return "Human controlled fire brigade"; + } + return "Human controlled fire brigade " + me().getID() + " (" + me().getWater() + " water)" + (target == null ? " (no target)" : " target: building " + target.getID()); + } +} \ No newline at end of file diff --git a/modules/human/src/human/ControlledPoliceForce.java b/modules/human/src/human/ControlledPoliceForce.java new file mode 100644 index 0000000000000000000000000000000000000000..72a1f2a9f8479cf73c1f862ff6dd608336f6dc2b --- /dev/null +++ b/modules/human/src/human/ControlledPoliceForce.java @@ -0,0 +1,125 @@ +package human; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.Command; +import rescuecore2.misc.Pair; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.components.StandardAgent; + +import sample.SampleSearch; + +import java.util.Collection; +import java.util.List; +import java.util.EnumSet; + +/** + A basic police force agent that will try to clear a given target. Fully-blocked roads encountered along the way are also cleared. If there is no target then this agent does nothing. +*/ +public class ControlledPoliceForce extends StandardAgent<PoliceForce> { + private SampleSearch search; + private Road target; + + /** + Set the target for this police force. + @param target The new target. + */ + public void setTarget(Road target) { + this.target = target; + } + + @Override + protected void think(int time, ChangeSet changed, Collection<Command> heard) { + if (location() instanceof Road) { + Road r = (Road)location(); + EntityID nearest = getNearestBlockade(); + if (nearest != null) { + sendClear(time, nearest); + return; + } + } + if (target == null) { + Logger.info("Nothing to do."); + return; + } + List<EntityID> path = search.breadthFirstSearch(me().getPosition(), target.getID()); + if (path != null) { + sendMove(time, path); + return; + } + else { + Logger.info("Couldn't plan a path to target."); + } + } + + @Override + protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() { + return EnumSet.of(StandardEntityURN.POLICE_FORCE); + } + + /** + Get the location of the entity controlled by this agent. + @return The location of the entity controlled by this agent. + */ + protected StandardEntity location() { + PoliceForce me = me(); + return me.getPosition(model); + } + + @Override + protected void postConnect() { + super.postConnect(); + search = new SampleSearch(model); + } + + @Override + public String toString() { + if (me() == null) { + return "Human controlled police force"; + } + return "Human controlled police force " + getID() + (target == null ? " (no target)" : " target: road " + target.getID() + " with " + (target.isBlockadesDefined() ? " unknown" : String.valueOf(target.getBlockades().size())) + " blockades"); + } + + /** + Get the blockade that is nearest this agent. + @return The EntityID of the nearest blockade, or null if there are no blockades in the agents current location. + */ + public EntityID getNearestBlockade() { + return getNearestBlockade((Area)location(), me().getX(), me().getY()); + } + + /** + Get the blockade that is nearest a point. + @param area The area to check. + @param x The X coordinate to look up. + @param y The X coordinate to look up. + @return The EntityID of the nearest blockade, or null if there are no blockades in this area. + */ + public EntityID getNearestBlockade(Area area, int x, int y) { + double bestDistance = 0; + EntityID best = null; + if (area.isBlockadesDefined()) { + for (EntityID blockadeID : area.getBlockades()) { + StandardEntity entity = model.getEntity(blockadeID); + Pair<Integer, Integer> location = entity.getLocation(model); + if (location == null) { + continue; + } + double dx = location.first() - x; + double dy = location.second() - y; + double distance = Math.hypot(dx, dy); + if (best == null || distance < bestDistance) { + bestDistance = distance; + best = entity.getID(); + } + } + } + return best; + } +} diff --git a/modules/ignition/src/ignition/IgnitionModel.java b/modules/ignition/src/ignition/IgnitionModel.java new file mode 100644 index 0000000000000000000000000000000000000000..a8232be89cd049ceae0e340800b14a28698ccf11 --- /dev/null +++ b/modules/ignition/src/ignition/IgnitionModel.java @@ -0,0 +1,19 @@ +package ignition; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.Building; + +import java.util.Set; + +/** + A model for determining which buildings ignite at any timestep. + */ +public interface IgnitionModel { + /** + Find out which buildings have ignited. + @param world The world model. + @param time The current time. + @return A list of newly ignited buildings. + */ + Set<Building> findIgnitionPoints(StandardWorldModel world, int time); +} diff --git a/modules/ignition/src/ignition/IgnitionSimulator.java b/modules/ignition/src/ignition/IgnitionSimulator.java new file mode 100644 index 0000000000000000000000000000000000000000..c0df3584f5a7c7380000956842a609bff4616bde --- /dev/null +++ b/modules/ignition/src/ignition/IgnitionSimulator.java @@ -0,0 +1,83 @@ +package ignition; + +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.log.Logger; + +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.GasStation; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + A simulator that determines when new building fires begin. +*/ +public class IgnitionSimulator extends StandardSimulator { + private IgnitionModel ignitionModel; + private int GAS_STATION_EXPLOSION_RANG; + private List<GasStation> notIgnaitedGasStations; + @Override + protected void postConnect() { + super.postConnect(); + ignitionModel = new RandomIgnitionModel(model, config); + GAS_STATION_EXPLOSION_RANG=config.getIntValue("ignition.gas_station.explosion.range"); + notIgnaitedGasStations=new ArrayList<GasStation>(); + for (StandardEntity entity : model.getEntitiesOfType(StandardEntityURN.GAS_STATION)) { + notIgnaitedGasStations.add((GasStation) entity); + } + } + + @Override + protected void processCommands(KSCommands c, ChangeSet changes) { + long start = System.currentTimeMillis(); + int time = c.getTime(); + Logger.info("Timestep " + time); + + explosionGasStations(changes); + + Logger.info("Ignating after shock "); + // Find out which buildings have ignited. + Set<Building> buildings = ignitionModel.findIgnitionPoints(model, c.getTime()); + for (Building next : buildings) { + Logger.info("Igniting " + next); + next.setIgnition(true); + changes.addChange(next, next.getIgnitionProperty()); + } + long end = System.currentTimeMillis(); + Logger.info("Timestep " + time + " took " + (end - start) + " ms"); + } + + private void explosionGasStations(ChangeSet changes) { + Logger.info("explosion Gas Stations "); + for (Iterator<GasStation> iterator= notIgnaitedGasStations.iterator(); iterator.hasNext();) { + GasStation gasStation = iterator.next(); + if(gasStation.isFierynessDefined()&&gasStation.getFieryness()==1){ + explode(gasStation,changes); + iterator.remove(); + } + } + } + + private void explode(GasStation gasStation, ChangeSet changes) { + Logger.info(gasStation+" Ignited ==> explosion" ); + for (StandardEntity rangeEntity : model.getObjectsInRange(gasStation, GAS_STATION_EXPLOSION_RANG)) { + if(rangeEntity instanceof Building){ + Building rangeBuilding = (Building)rangeEntity; + Logger.info("Igniting " + rangeBuilding); + rangeBuilding.setIgnition(true); + changes.addChange(rangeBuilding, rangeBuilding.getIgnitionProperty()); + } + } + } + + @Override + public String getName() { + return "Ignition simulator"; + } +} \ No newline at end of file diff --git a/modules/ignition/src/ignition/RandomIgnitionModel.java b/modules/ignition/src/ignition/RandomIgnitionModel.java new file mode 100644 index 0000000000000000000000000000000000000000..3655dc3d36f8d35c7aa289b188ee65cb59bd64bf --- /dev/null +++ b/modules/ignition/src/ignition/RandomIgnitionModel.java @@ -0,0 +1,58 @@ +package ignition; + +import rescuecore2.config.Config; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.Building; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Collections; + +import org.uncommons.maths.random.PoissonGenerator; + +/** + An IgnitionModel that ignites unburnt buildings in a random order. The number of ignitions per timestep is drawn from a Poisson distribution. +*/ +public class RandomIgnitionModel implements IgnitionModel { + private static final String MEAN_KEY = "ignition.random.lambda"; + + private PoissonGenerator generator; + private Iterator<Building> it; + + /** + Construct a RandomIgnitionModel. + @param world The world model. + @param config The system configuration. + */ + public RandomIgnitionModel(StandardWorldModel world, Config config) { + List<Building> unburnt = new ArrayList<Building>(); + for (StandardEntity next : world) { + if (next instanceof Building) { + unburnt.add((Building)next); + } + } + Collections.shuffle(unburnt, config.getRandom()); + double mean = config.getFloatValue(MEAN_KEY); + generator = new PoissonGenerator(mean, config.getRandom()); + it = unburnt.iterator(); + } + + @Override + public Set<Building> findIgnitionPoints(StandardWorldModel world, int time) { + Set<Building> result = new HashSet<Building>(); + if (it.hasNext()) { + int number = generator.nextValue(); + Logger.debug("Igniting " + number + " buildings"); + for (int i = 0; i < number && it.hasNext(); ++i) { + result.add(it.next()); + } + } + return result; + } +} diff --git a/modules/kernel/src/kernel/AbstractCommandFilter.java b/modules/kernel/src/kernel/AbstractCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..caa582c24347f95ce04198fde116c603b756e2d9 --- /dev/null +++ b/modules/kernel/src/kernel/AbstractCommandFilter.java @@ -0,0 +1,33 @@ +package kernel; + +import java.util.Collection; +import java.util.Iterator; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; + +/** + An abstract base class for command filters. + */ +public abstract class AbstractCommandFilter implements CommandFilter { + @Override + public void initialise(Config config) { + } + + @Override + public void filter(Collection<Command> commands, KernelState state) { + for (Iterator<Command> it = commands.iterator(); it.hasNext();) { + if (!allowed(it.next(), state)) { + it.remove(); + } + } + } + + /** + Find out if a particular command is allowed. + @param command The command. + @param state The kernel state. + @return True iff the command is allowed. + */ + protected abstract boolean allowed(Command command, KernelState state); +} diff --git a/modules/kernel/src/kernel/AbstractCommunicationModel.java b/modules/kernel/src/kernel/AbstractCommunicationModel.java new file mode 100644 index 0000000000000000000000000000000000000000..c8b308c0cdb0a4d68864413375ec06a34d949d4b --- /dev/null +++ b/modules/kernel/src/kernel/AbstractCommunicationModel.java @@ -0,0 +1,65 @@ +package kernel; + +import java.util.Collection; +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.Arrays; + +import rescuecore2.messages.Command; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.misc.collections.LazyMap; + +/** + Abstract base class for communication models. + */ +public abstract class AbstractCommunicationModel implements CommunicationModel { + private Map<Entity, List<Command>> hearing; + + /** + Construct an AbstractCommunicationModel. + */ + public AbstractCommunicationModel() { + hearing = new LazyMap<Entity, List<Command>>() { + @Override + public List<Command> createValue() { + return new LinkedList<Command>(); + } + }; + } + + @Override + public void initialise(Config config, WorldModel<? extends Entity> model) { + hearing.clear(); + } + + @Override + public void process(int time, Collection<? extends Command> agentCommands) { + hearing.clear(); + } + + @Override + public Collection<Command> getHearing(Entity agent) { + return hearing.get(agent); + } + + /** + Register a set of heard messages for an agent. + @param agent The agent. + @param c The messages heard. + */ + protected void addHearing(Entity agent, Command... c) { + addHearing(agent, Arrays.asList(c)); + } + + /** + Register a set of heard messages for an agent. + @param agent The agent. + @param c The messages heard. + */ + protected void addHearing(Entity agent, Collection<? extends Command> c) { + hearing.get(agent).addAll(c); + } +} diff --git a/modules/kernel/src/kernel/AbstractKernelComponent.java b/modules/kernel/src/kernel/AbstractKernelComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..8f3030b59311c4a970d9dbdfb8ad067516d2b225 --- /dev/null +++ b/modules/kernel/src/kernel/AbstractKernelComponent.java @@ -0,0 +1,65 @@ +package kernel; + +import rescuecore2.messages.Message; +import rescuecore2.messages.control.Shutdown; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.log.Logger; + +import java.util.Collection; +import java.util.Collections; + +/** + Abstract base class for KernelComponent implementations. + */ +public abstract class AbstractKernelComponent implements KernelComponent { + private Connection connection; + private String name; + + /** + Construct a new abstract component. + @param name The name of this component. + @param c The connection this component is using. + */ + protected AbstractKernelComponent(String name, Connection c) { + this.name = name; + this.connection = c; + } + + @Override + public void send(Collection<? extends Message> messages) { + if (!connection.isAlive()) { + return; + } + try { + connection.sendMessages(messages); + } + catch (ConnectionException e) { + Logger.error("Error sending message", e); + } + } + + @Override + public Connection getConnection() { + return connection; + } + + @Override + public void shutdown() { + send(new Shutdown()); + connection.shutdown(); + } + + @Override + public String getName() { + return name; + } + + /** + Send a single message. + @param message The message to send. + */ + protected void send(Message message) { + send(Collections.singleton(message)); + } +} diff --git a/modules/kernel/src/kernel/AgentProxy.java b/modules/kernel/src/kernel/AgentProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..a919da5dc43426528eb2df943ed16aa4f637f086 --- /dev/null +++ b/modules/kernel/src/kernel/AgentProxy.java @@ -0,0 +1,117 @@ +package kernel; + +import java.util.Collection; +import java.util.Map; +import java.util.ArrayList; + +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.messages.Message; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KASense; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.log.Logger; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.registry.Registry; + +/** + This class is the kernel interface to an agent. + */ +public class AgentProxy extends AbstractKernelComponent { + private Entity entity; + private Map<Integer, Collection<Command>> commands; + + /** + Construct an agent. + @param name The name of the controlling agent. + @param e The entity controlled by the agent. + @param c The connection to the agent. + */ + public AgentProxy(String name, Entity e, Connection c) { + super(name, c); + this.entity = e; + commands = new LazyMap<Integer, Collection<Command>>() { + @Override + public Collection<Command> createValue() { + return new ArrayList<Command>(); + } + }; + c.addConnectionListener(new AgentConnectionListener()); + } + + @Override + public String toString() { + return getName() + ": " + Registry.getCurrentRegistry().toPrettyName(entity.getURN()) + " " + entity.getID(); + } + + /** + Get the entity controlled by this agent. + @return The entity controlled by this agent. + */ + public Entity getControlledEntity() { + return entity; + } + + /** + Get all agent commands at a particular time. + @param timestep The current timestep. + @return A collection of messages representing the commands + */ + public Collection<Command> getAgentCommands(int timestep) { + Collection<Command> result; + synchronized (commands) { + result = new ArrayList<>(commands.get(timestep)); + } + Logger.trace(entity.toString() + " getAgentCommands(" + timestep + ") returning " + result); + return result; + } + + /** + Notify the of a perception update. + @param time The current timestep. + @param visible The set of visible changes. + @param heard The set of communication messages that the agent heard. + */ + public void sendPerceptionUpdate(int time, ChangeSet visible, Collection<? extends Command> heard) { + KASense sense = new KASense(getControlledEntity().getID(), time, visible, heard); + send(sense); + } + + /** + Register an agent command received. + @param c The command that was received. + */ + protected void commandReceived(Command c) { + // Check that the command is for the right agent + if (!c.getAgentID().equals(entity.getID())) { + Logger.warn("Ignoring bogus command: Agent " + entity.getID() + " tried to send a command for agent " + c.getAgentID()); + return; + } + int time = c.getTime(); + Logger.trace("AgentProxy " + entity + " received " + c); + synchronized (commands) { + Collection<Command> result = commands.get(time); + result.add(c); + commands.notifyAll(); + } + } + + private class AgentConnectionListener implements ConnectionListener { + @Override + public void messageReceived(Connection c, Message msg) { + if (msg instanceof Command) { + EntityID id = ((Command)msg).getAgentID(); + if (id.equals(getControlledEntity().getID())) { + commandReceived((Command)msg); + } + } + } + } + + @Override + public int hashCode() { + return entity.getID().hashCode(); + } +} diff --git a/modules/kernel/src/kernel/AgentRegistrar.java b/modules/kernel/src/kernel/AgentRegistrar.java new file mode 100644 index 0000000000000000000000000000000000000000..00448211a9222a883092a24faf856c613ca1cb60 --- /dev/null +++ b/modules/kernel/src/kernel/AgentRegistrar.java @@ -0,0 +1,19 @@ +package kernel; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.config.Config; + +/** + Implementations of this decide which entities are controlled by agents and what each agent can see on startup. + */ +public interface AgentRegistrar { + /** + Process a WorldModel and Config and tell the ComponentManager which entities are agent-controlled and what they can see on connection. + @param world The WorldModel. + @param config The Config. + @param manager The ComponentManager. + @throws KernelException If there is a problem registering agents. + */ + void registerAgents(WorldModel<? extends Entity> world, Config config, ComponentManager manager) throws KernelException; +} diff --git a/modules/kernel/src/kernel/ChainedCommandFilter.java b/modules/kernel/src/kernel/ChainedCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..9b7b23ff5d1fe1451f87152274b3b72788e35107 --- /dev/null +++ b/modules/kernel/src/kernel/ChainedCommandFilter.java @@ -0,0 +1,52 @@ +package kernel; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; + +/** + A CommandFilter that chains together a set of filters. + */ +public class ChainedCommandFilter implements CommandFilter { + private List<CommandFilter> filters; + + /** + Construct an empty ChainedCommandFilter. + */ + public ChainedCommandFilter() { + filters = new ArrayList<CommandFilter>(); + } + + /** + Add a CommandFilter to the chain. + @param filter The filter to add. + */ + public void addFilter(CommandFilter filter) { + filters.add(filter); + } + + /** + Remove a CommandFilter from the chain. + @param filter The filter to remove. + */ + public void removeFilter(CommandFilter filter) { + filters.remove(filter); + } + + @Override + public void initialise(Config config) { + for (CommandFilter next : filters) { + next.initialise(config); + } + } + + @Override + public void filter(Collection<Command> commands, KernelState state) { + for (CommandFilter next : filters) { + next.filter(commands, state); + } + } +} diff --git a/modules/kernel/src/kernel/CommandCollector.java b/modules/kernel/src/kernel/CommandCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..3a66d22abb7501833386f08a38f9d777c05ae0be --- /dev/null +++ b/modules/kernel/src/kernel/CommandCollector.java @@ -0,0 +1,26 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; + +import java.util.Collection; + +/** + The CommandCollector gathers commands from agents. +*/ +public interface CommandCollector { + /** + Collect all commands from agents. + @param agents The agents. + @param timestep The timestep. + @return All agent commands. + @throws InterruptedException If the thread is interrupted. + */ + Collection<Command> getAgentCommands(Collection<AgentProxy> agents, int timestep) throws InterruptedException; + + /** + Initialise this command collector. + @param config The kernel configuration. + */ + void initialise(Config config); +} diff --git a/modules/kernel/src/kernel/CommandFilter.java b/modules/kernel/src/kernel/CommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..d41060128d427603c930ab032ddc465890829f21 --- /dev/null +++ b/modules/kernel/src/kernel/CommandFilter.java @@ -0,0 +1,24 @@ +package kernel; + +import java.util.Collection; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; + +/** + An interface for allowing the kernel to filter out agent commands. + */ +public interface CommandFilter { + /** + Initialise this filter. + @param config The kernel configuration. + */ + void initialise(Config config); + + /** + Filter a set of agent commands. Any illegal commands should be removed from the given collection. + @param commands The commands to filter. This collection should be modified to remove any illegal commands. + @param state The state of the kernel. + */ + void filter(Collection<Command> commands, KernelState state); +} diff --git a/modules/kernel/src/kernel/CommunicationModel.java b/modules/kernel/src/kernel/CommunicationModel.java new file mode 100644 index 0000000000000000000000000000000000000000..f3ee6cda148f6c6e8f8e9c57899395499faf6f2c --- /dev/null +++ b/modules/kernel/src/kernel/CommunicationModel.java @@ -0,0 +1,34 @@ +package kernel; + +import java.util.Collection; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +/** + A model of communication. Implementers are responsible for determining what communications are received by each agent in the world. + */ +public interface CommunicationModel { + /** + Initialise this communication model. + @param config The kernel configuration. + @param world The world model. + */ + void initialise(Config config, WorldModel<? extends Entity> world); + + /** + Process a set of agent commands and work out what communications each agent can hear. + @param time The current time. + @param agentCommands The set of all agent commands this timestep. + */ + void process(int time, Collection<? extends Command> agentCommands); + + /** + Get the set of hear commands an agent can hear. + @param agent The agent controlled entity. + @return Set set of hear commands the agent can hear. + */ + Collection<Command> getHearing(Entity agent); +} diff --git a/modules/kernel/src/kernel/ComponentManager.java b/modules/kernel/src/kernel/ComponentManager.java new file mode 100644 index 0000000000000000000000000000000000000000..a9f7a49c72fed90c4414905df2843f13994ee0e9 --- /dev/null +++ b/modules/kernel/src/kernel/ComponentManager.java @@ -0,0 +1,575 @@ +package kernel; + +import java.util.Set; +import java.util.HashSet; +import java.util.Queue; +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.connection.ConnectionManagerListener; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.KSAfterShocksInfo; +import rescuecore2.messages.control.VKConnect; +import rescuecore2.registry.Registry; +import rescuecore2.messages.control.VKAcknowledge; +import rescuecore2.messages.control.KVConnectOK; +import rescuecore2.messages.control.SKConnect; +import rescuecore2.messages.control.SKAcknowledge; +import rescuecore2.messages.control.KSConnectOK; +import rescuecore2.messages.control.AKConnect; +import rescuecore2.messages.control.AKAcknowledge; +import rescuecore2.messages.control.KAConnectError; +import rescuecore2.messages.control.KAConnectOK; +import rescuecore2.scenario.Scenario; +import rescuecore2.scenario.exceptions.UncompatibleScenarioException; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.GUIComponent; +import rescuecore2.log.Logger; + +import kernel.ui.ComponentManagerGUI; + +import javax.swing.JComponent; + +/** + * Class that manages connecting components (agents, simulators, viewers) to the + * kernel. + */ +public class ComponentManager implements ConnectionManagerListener, + GUIComponent { + private static final int STARTING_ID = 1; + + private static final int WAIT_TIME = 10000; + + private Kernel kernel; + private ComponentManagerGUI gui; + + // Entities that have no controller yet. Map from type to list of entities. + private Map<Integer, Queue<ControlledEntityInfo>> uncontrolledEntities; + + // Connected agents + private Set<AgentAck> agentsToAcknowledge; + + // Connected simulators + private Set<SimulatorAck> simsToAcknowledge; + private int nextID; + + // Connected viewers + private Set<ViewerAck> viewersToAcknowledge; + + // World information + private WorldModel<? extends Entity> world; + + private Config config; + + /** Lock objects. */ + private final Object agentLock = new Object(); + private final Object simLock = new Object(); + private final Object viewerLock = new Object(); + private final Object idLock = new Object(); + + private final Scenario scenario; + + /** + * Create a ComponentManager. + * + * @param kernel + * The kernel. + * @param world + * The world model. + * @param config + * The kernel configuration. + */ + public ComponentManager(Kernel kernel, WorldModel<? extends Entity> world, + Config config, Scenario scenario) { + this.kernel = kernel; + this.world = world; + this.config = config; + this.scenario = scenario; + uncontrolledEntities = new HashMap<Integer, Queue<ControlledEntityInfo>>(); + agentsToAcknowledge = new HashSet<AgentAck>(); + simsToAcknowledge = new HashSet<SimulatorAck>(); + viewersToAcknowledge = new HashSet<ViewerAck>(); + nextID = STARTING_ID; + gui = new ComponentManagerGUI(); + } + + /** + * Register an agent-controlled entity. + * + * @param entity + * The entity that is agent-controlled. + * @param visibleOnStartup + * The set of entities that the agent should be sent on startup. + * If this is null then all entities will be sent. + * @param agentConfig + * A view of the system configuration that should be shared with + * the agent. + */ + public void registerAgentControlledEntity(Entity entity, + Collection<? extends Entity> visibleOnStartup, Config agentConfig) { + Logger.info("Agent controlled entity registered: " + entity); + synchronized (agentLock) { + Queue<ControlledEntityInfo> q = uncontrolledEntities.get(entity + .getURN()); + if (q == null) { + q = new LinkedList<ControlledEntityInfo>(); + uncontrolledEntities.put(entity.getURN(), q); + } + if (visibleOnStartup == null) { + visibleOnStartup = world.getAllEntities(); + } + q.add(new ControlledEntityInfo(entity, visibleOnStartup, + agentConfig)); + } + updateGUIUncontrolledAgents(); + } + + /** + * Wait for all agents to connect. This method will block until all agent + * entities have controllers. + * + * @throws InterruptedException + * If the thread is interrupted. + */ + public void waitForAllAgents() throws InterruptedException { + synchronized (agentLock) { + boolean done = false; + do { + done = true; + for (Entry<Integer, Queue<ControlledEntityInfo>> next : uncontrolledEntities + .entrySet()) { + if (!next.getValue().isEmpty()) { + done = false; + Logger.info("Waiting for " + next.getValue().size() + + " entities of type " + next.getKey()); + } + } + if (!agentsToAcknowledge.isEmpty()) { + done = false; + Logger.info("Waiting for " + agentsToAcknowledge.size() + + " agents to acknowledge"); + } + if (!done) { + agentLock.wait(WAIT_TIME); + } + } while (!done); + } + } + + /** + * Wait until all simulators have acknowledged. + * + * @throws InterruptedException + * If the thread is interrupted. + */ + public void waitForAllSimulators() throws InterruptedException { + synchronized (simLock) { + while (!simsToAcknowledge.isEmpty()) { + simLock.wait(WAIT_TIME); + Logger.info("Waiting for " + simsToAcknowledge.size() + + " simulators to acknowledge"); + } + } + } + + /** + * Wait until all viewers have acknowledged. + * + * @throws InterruptedException + * If the thread is interrupted. + */ + public void waitForAllViewers() throws InterruptedException { + synchronized (viewerLock) { + while (!viewersToAcknowledge.isEmpty()) { + viewerLock.wait(WAIT_TIME); + Logger.info("Waiting for " + viewersToAcknowledge.size() + + " viewers to acknowledge"); + } + } + } + + @Override + public void newConnection(Connection c) { + c.addConnectionListener(new ComponentConnectionListener()); + } + + @Override + public JComponent getGUIComponent() { + return gui; + } + + @Override + public String getGUIComponentName() { + return "Component manager"; + } + + private boolean agentAcknowledge(int requestID, EntityID agentID, + Connection c) { + synchronized (agentLock) { + for (AgentAck next : agentsToAcknowledge) { + if (next.requestID == requestID && next.agentID.equals(agentID) + && next.connection == c) { + agentsToAcknowledge.remove(next); + kernel.addAgent(next.agent); + agentLock.notifyAll(); + return true; + } + } + return false; + } + } + + private boolean simAcknowledge(int requestID, int simulatorID, Connection c) { + synchronized (simLock) { + for (SimulatorAck next : simsToAcknowledge) { + if (next.requestID == requestID + && next.simulatorID == simulatorID + && next.connection == c) { + simsToAcknowledge.remove(next); + kernel.addSimulator(next.sim); + simLock.notifyAll(); + return true; + } + } + return false; + } + } + + private boolean viewerAcknowledge(int requestID, int viewerID, Connection c) { + synchronized (viewerLock) { + for (ViewerAck next : viewersToAcknowledge) { + if (next.requestID == requestID && next.viewerID == viewerID + && next.connection == c) { + viewersToAcknowledge.remove(next); + kernel.addViewer(next.viewer); + viewerLock.notifyAll(); + return true; + } + } + return false; + } + } + + private int getNextSimulatorID() { + synchronized (idLock) { + return nextID++; + } + } + + private int getNextViewerID() { + synchronized (idLock) { + return nextID++; + } + } + + private ControlledEntityInfo findEntityToControl(List<Integer> types) { + Logger.debug("Finding entity to control. Requested types: " + types); + for (Integer next : types) { + Queue<ControlledEntityInfo> q = uncontrolledEntities.get(next); + Logger.debug("Uncontrolled entities of type " + next + ": " + q); + if (q != null) { + ControlledEntityInfo info = q.poll(); + if (info != null) { + return info; + } + } + } + return null; + } + + private void updateGUIUncontrolledAgents() { + List<String> data = new ArrayList<String>(); + synchronized (agentLock) { + for (Queue<ControlledEntityInfo> q : uncontrolledEntities.values()) { + for (ControlledEntityInfo info : q) { + data.add(Registry.SYSTEM_REGISTRY.toPrettyName(info.entity.getURN()) + " " + info.entity.getID()); + } + } + } + gui.updateUncontrolledAgents(data); + } + + private void updateGUIAgentAck() { + List<String> data = new ArrayList<String>(); + synchronized (agentLock) { + for (AgentAck ack : agentsToAcknowledge) { + data.add(ack.toString()); + } + } + gui.updateAgentAck(data); + } + + private void updateGUISimulatorAck() { + List<String> data = new ArrayList<String>(); + synchronized (simLock) { + for (SimulatorAck ack : simsToAcknowledge) { + data.add(ack.toString()); + } + } + gui.updateSimulatorAck(data); + } + + private void updateGUIViewerAck() { + List<String> data = new ArrayList<String>(); + synchronized (viewerLock) { + for (ViewerAck ack : viewersToAcknowledge) { + data.add(ack.toString()); + } + } + gui.updateViewerAck(data); + } + + private class ComponentConnectionListener implements ConnectionListener { + @Override + public void messageReceived(Connection connection, Message msg) { + if (msg instanceof AKConnect) { + handleAKConnect((AKConnect) msg, connection); + } + if (msg instanceof AKAcknowledge) { + handleAKAcknowledge((AKAcknowledge) msg, connection); + } + try { + if (msg instanceof SKConnect) { + handleSKConnect((SKConnect) msg, connection); + } + } catch (UncompatibleScenarioException e) { + e.printStackTrace(); + } + if (msg instanceof SKAcknowledge) { + handleSKAcknowledge((SKAcknowledge) msg, connection); + } + if (msg instanceof VKConnect) { + handleVKConnect((VKConnect) msg, connection); + } + if (msg instanceof VKAcknowledge) { + handleVKAcknowledge((VKAcknowledge) msg, connection); + } + } + + private void handleAKConnect(AKConnect connect, Connection connection) { + // Pull out the request ID and requested entity type list + int requestID = connect.getRequestID(); + List<Integer> types = connect.getRequestedEntityTypes(); + // See if we can find an entity for this agent to control. + Message reply = null; + Logger.debug("AKConnect received: " + types); + synchronized (agentLock) { + ControlledEntityInfo result = findEntityToControl(types); + if (result == null) { + Logger.debug("No suitable entities found"); + // Send an error + reply = new KAConnectError(requestID, "No more agents"); + } else { + Logger.debug("Found entity to control: " + result); + Entity entity = result.entity; + AgentProxy agent = new AgentProxy(connect.getAgentName(), + entity, connection); + agentsToAcknowledge.add(new AgentAck(agent, entity.getID(), + requestID, connection)); + Logger.info("Agent '" + connect.getAgentName() + "' id " + + entity.getID() + " (" + connection + + " request ID " + requestID + ") connected"); + // Send an OK + reply = new KAConnectOK(requestID, entity.getID(), + result.visibleSet, result.config); + } + } + if (reply != null) { + try { + connection.sendMessage(reply); + } catch (ConnectionException e) { + Logger.error("Error sending reply", e); + } + } + updateGUIUncontrolledAgents(); + updateGUIAgentAck(); + } + + private void handleAKAcknowledge(AKAcknowledge msg, + Connection connection) { + int requestID = msg.getRequestID(); + EntityID agentID = msg.getAgentID(); + if (agentAcknowledge(requestID, agentID, connection)) { + Logger.info("Agent " + agentID + " (" + connection + + " request ID " + requestID + ") acknowledged"); + } else { + Logger.warn("Unexpected acknowledge from agent " + agentID + + " (request ID " + requestID + ")"); + } + updateGUIAgentAck(); + } + + private void handleSKConnect(SKConnect msg, Connection connection) + throws UncompatibleScenarioException { + int simID = getNextSimulatorID(); + int requestID = msg.getRequestID(); + Logger.info("Simulator '" + msg.getSimulatorName() + "' id " + + simID + " (" + connection + " request ID " + requestID + + ") connected"); + SimulatorProxy sim = new SimulatorProxy(msg.getSimulatorName(), + simID, connection); + synchronized (simLock) { + simsToAcknowledge.add(new SimulatorAck(sim, simID, requestID, + connection)); + } + // Send an OK + sim.send(Collections.singleton(new KSConnectOK(simID, requestID, + world.getAllEntities(), config))); + sendAdditionalInfoToSim(sim); + updateGUISimulatorAck(); + } + + /** + * Used to send info other than world model's to simulators. Information + * such as Aftershocks' properties and etc. + * + * @param sim + * @throws UncompatibleScenarioException + */ + private void sendAdditionalInfoToSim(SimulatorProxy sim) + throws UncompatibleScenarioException { + sim.send(Collections.singleton(new KSAfterShocksInfo(scenario))); + } + + private void handleSKAcknowledge(SKAcknowledge msg, + Connection connection) { + int requestID = msg.getRequestID(); + int simID = msg.getSimulatorID(); + if (simAcknowledge(requestID, simID, connection)) { + Logger.info("Simulator " + simID + " (" + connection + + " request ID " + requestID + ") acknowledged"); + } else { + Logger.warn("Unexpected acknowledge from simulator " + simID + + " (request ID " + requestID + ")"); + } + updateGUISimulatorAck(); + } + + private void handleVKConnect(VKConnect msg, Connection connection) { + int requestID = msg.getRequestID(); + int viewerID = getNextViewerID(); + Logger.info("Viewer '" + msg.getViewerName() + "' id " + viewerID + + " (" + connection + " request ID " + requestID + + ") connected"); + ViewerProxy viewer = new ViewerProxy(msg.getViewerName(), viewerID, + connection); + synchronized (viewerLock) { + viewersToAcknowledge.add(new ViewerAck(viewer, viewerID, + requestID, connection)); + } + // Send an OK + viewer.send(Collections.singleton(new KVConnectOK(viewerID, + requestID, world.getAllEntities(), config))); + updateGUIViewerAck(); + } + + private void handleVKAcknowledge(VKAcknowledge msg, + Connection connection) { + int requestID = msg.getRequestID(); + int viewerID = msg.getViewerID(); + if (viewerAcknowledge(requestID, viewerID, connection)) { + Logger.info("Viewer " + viewerID + " (" + connection + + " request ID " + requestID + ") acknowledged"); + } else { + Logger.warn("Unexpected acknowledge from viewer " + viewerID + + " (" + requestID + ")"); + } + updateGUIViewerAck(); + } + } + + private static class AgentAck { + AgentProxy agent; + EntityID agentID; + int requestID; + Connection connection; + + public AgentAck(AgentProxy agent, EntityID agentID, int requestID, + Connection c) { + this.agent = agent; + this.agentID = agentID; + this.requestID = requestID; + this.connection = c; + } + + @Override + public String toString() { + return agent.getName() + ": " + + Registry.SYSTEM_REGISTRY.toPrettyName(agent.getControlledEntity().getURN()) + " " + + agent.getControlledEntity().getID() + "(" + connection + + " request ID " + requestID + ")"; + } + } + + private static class SimulatorAck { + SimulatorProxy sim; + int simulatorID; + int requestID; + Connection connection; + + public SimulatorAck(SimulatorProxy sim, int simID, int requestID, + Connection c) { + this.sim = sim; + this.simulatorID = simID; + this.requestID = requestID; + this.connection = c; + } + + @Override + public String toString() { + return sim + " " + simulatorID + "(connection request ID " + + requestID + ")"; + } + } + + private static class ViewerAck { + ViewerProxy viewer; + int viewerID; + int requestID; + Connection connection; + + public ViewerAck(ViewerProxy viewer, int viewerID, int requestID, + Connection c) { + this.viewer = viewer; + this.viewerID = viewerID; + this.requestID = requestID; + this.connection = c; + } + + @Override + public String toString() { + return viewer + " " + viewerID + "(connection request ID " + + requestID + ")"; + } + } + + private static class ControlledEntityInfo { + Entity entity; + Collection<? extends Entity> visibleSet; + Config config; + + public ControlledEntityInfo(Entity entity, + Collection<? extends Entity> visibleSet, Config config) { + this.entity = entity; + this.visibleSet = visibleSet; + this.config = config; + } + + @Override + public String toString() { + return entity.toString(); + } + } +} diff --git a/modules/kernel/src/kernel/CompositeCommandCollector.java b/modules/kernel/src/kernel/CompositeCommandCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..1f5a088248e732390369c926cafab051660302b7 --- /dev/null +++ b/modules/kernel/src/kernel/CompositeCommandCollector.java @@ -0,0 +1,109 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.log.Logger; +import rescuecore2.messages.Command; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + A CommandCollector that waits for any of a set of child CommandCollectors to return a result. +*/ +public class CompositeCommandCollector implements CommandCollector { + private Set<CommandCollector> children; + private ExecutorService executorService; + + /** + Construct a CompositeCommandCollector with no children. + */ + public CompositeCommandCollector() { + children = new HashSet<CommandCollector>(); + } + + @Override + public void initialise(Config config) { + for (CommandCollector next : children) { + next.initialise(config); + } + executorService = Executors.newFixedThreadPool(children.size()); + } + + @Override + public Collection<Command> getAgentCommands(Collection<AgentProxy> agents, int timestep) throws InterruptedException { + Collection<Command> result = new ArrayList<Command>(); + if (agents.size() == 0) { + return result; + } + ExecutorCompletionService<Collection<Command>> service = new ExecutorCompletionService<Collection<Command>>(executorService); + Set<Future<Collection<Command>>> futures = new HashSet<Future<Collection<Command>>>(); + for (CommandCollector next : children) { + futures.add(service.submit(new ChildCommandsFetcher(next, agents, timestep))); + } + try { + for (int i = 0; i < children.size(); ++i) { + try { + result = service.take().get(); + break; + } + catch (ExecutionException e) { + Logger.error("Error while getting agent commands", e); + } + } + } + finally { + for (Future<Collection<Command>> next : futures) { + next.cancel(true); + } + } + return result; + } + + /** + Add a child command collector. + @param child The child to add. + */ + public void addCommandCollector(CommandCollector child) { + children.add(child); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("CompositeCommandCollector ["); + for (Iterator<CommandCollector> it = children.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } + + private static final class ChildCommandsFetcher implements Callable<Collection<Command>> { + private CommandCollector child; + private Collection<AgentProxy> agents; + private int timestep; + + ChildCommandsFetcher(CommandCollector child, Collection<AgentProxy> agents, int timestep) { + this.child = child; + this.agents = agents; + this.timestep = timestep; + } + + @Override + public Collection<Command> call() throws Exception { + return child.getAgentCommands(agents, timestep); + } + } +} diff --git a/modules/kernel/src/kernel/EntityIDGenerator.java b/modules/kernel/src/kernel/EntityIDGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..64cecc9ff409d1c84a1480d9800b609d0da4bbd7 --- /dev/null +++ b/modules/kernel/src/kernel/EntityIDGenerator.java @@ -0,0 +1,14 @@ +package kernel; + +import rescuecore2.worldmodel.EntityID; + +/** + Interface for objects that can generate new EntityIDs. + */ +public interface EntityIDGenerator { + /** + Create a new EntityID. + @return A new EntityID. + */ + EntityID generateID(); +} diff --git a/modules/kernel/src/kernel/FrozenAgentsCommandFilter.java b/modules/kernel/src/kernel/FrozenAgentsCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..29e35dfd1a655ce655c9895609099907cbb1bfcd --- /dev/null +++ b/modules/kernel/src/kernel/FrozenAgentsCommandFilter.java @@ -0,0 +1,28 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +import java.util.Collection; + +/** + A CommandFilter that ignores agent commands for some number of timesteps. + */ +public class FrozenAgentsCommandFilter implements CommandFilter { + private int freezeTime; + + @Override + public void initialise(Config config) { + freezeTime = config.getIntValue(KernelConstants.IGNORE_AGENT_COMMANDS_KEY, 0); + } + + @Override + public void filter(Collection<Command> commands, KernelState state) { + int time = state.getTime(); + if (time < freezeTime) { + Logger.info("Ignoring early commands: " + time + " < " + freezeTime); + commands.clear(); + } + } +} diff --git a/modules/kernel/src/kernel/InlineComponentLauncher.java b/modules/kernel/src/kernel/InlineComponentLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..d03b78e89a37c973aba5a5580d495bf6c5de5b5c --- /dev/null +++ b/modules/kernel/src/kernel/InlineComponentLauncher.java @@ -0,0 +1,34 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.components.ComponentLauncher; +import rescuecore2.connection.Connection; +import rescuecore2.connection.StreamConnection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.misc.Pair; + +/** + A class that knows how to connect components to the kernel using inline streams. + */ +public class InlineComponentLauncher extends ComponentLauncher { + private ComponentManager manager; + + /** + Construct a new InlineComponentLauncher. + @param manager The component manager. + @param config The system configuration. + */ + public InlineComponentLauncher(ComponentManager manager, Config config) { + super(config); + this.manager = manager; + } + + @Override + protected Connection makeConnection() throws ConnectionException { + Pair<Connection, Connection> connections = StreamConnection.createConnectionPair(); + connections.first().setRegistry(getDefaultRegistry()); + connections.first().startup(); + manager.newConnection(connections.first()); + return connections.second(); + } +} diff --git a/modules/kernel/src/kernel/Kernel.java b/modules/kernel/src/kernel/Kernel.java new file mode 100644 index 0000000000000000000000000000000000000000..7c0b743fe53e20532884c95be813c0d27666e90a --- /dev/null +++ b/modules/kernel/src/kernel/Kernel.java @@ -0,0 +1,604 @@ +package kernel; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import rescuecore2.Constants; +import rescuecore2.Timestep; +import rescuecore2.config.Config; +import rescuecore2.log.CommandsRecord; +import rescuecore2.log.ConfigRecord; +import rescuecore2.log.EndLogRecord; +//import rescuecore2.log.FileLogWriter; +import rescuecore2.log.InitialConditionsRecord; +import rescuecore2.log.LogException; +import rescuecore2.log.LogWriter; +import rescuecore2.log.Logger; +import rescuecore2.log.PerceptionRecord; +import rescuecore2.log.RCRSLogFactory; +import rescuecore2.log.StartLogRecord; +import rescuecore2.log.UpdatesRecord; +import rescuecore2.messages.Command; +import rescuecore2.score.ScoreFunction; +//import rescuecore2.misc.gui.ChangeSetComponent; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +/** + * The Robocup Rescue kernel. + */ +public class Kernel { + /** The log context for kernel log messages. */ + public static final String KERNEL_LOG_CONTEXT = "kernel"; + + private Config config; + private Perception perception; + private CommunicationModel communicationModel; + private WorldModel<? extends Entity> worldModel; + private LogWriter log; + + private Set<KernelListener> listeners; + + private Collection<AgentProxy> agents; + private Collection<SimulatorProxy> sims; + private Collection<ViewerProxy> viewers; + private int time; + private Timestep previousTimestep; + + private EntityIDGenerator idGenerator; + private CommandFilter commandFilter; + + private TerminationCondition termination; + private ScoreFunction score; + private CommandCollector commandCollector; + + private boolean isShutdown; + + // private ChangeSetComponent simulatorChanges; + + /** + * Construct a kernel. + * + * @param config The configuration to use. + * @param perception A perception calculator. + * @param communicationModel A communication model. + * @param worldModel The world model. + * @param idGenerator An EntityIDGenerator. + * @param commandFilter An optional command filter. This may be null. + * @param termination The termination condition. + * @param score The score function. + * @param collector The CommandCollector to use. + * @throws KernelException If there is a problem constructing the kernel. + */ + public Kernel(Config config, Perception perception, + CommunicationModel communicationModel, + WorldModel<? extends Entity> worldModel, + EntityIDGenerator idGenerator, CommandFilter commandFilter, + TerminationCondition termination, ScoreFunction score, + CommandCollector collector) throws KernelException { + try { + Logger.pushLogContext(KERNEL_LOG_CONTEXT); + this.config = config; + this.perception = perception; + this.communicationModel = communicationModel; + this.worldModel = worldModel; + this.commandFilter = commandFilter; + this.score = score; + this.termination = termination; + this.commandCollector = collector; + this.idGenerator = idGenerator; + listeners = new HashSet<KernelListener>(); + agents = new TreeSet<AgentProxy>(new Comparator<AgentProxy>() { + @Override + public int compare(AgentProxy o1, AgentProxy o2) { + return Integer.compare(o1.hashCode(), o2.hashCode()); + } + }); + sims = new HashSet<SimulatorProxy>(); + viewers = new HashSet<ViewerProxy>(); + time = 0; + try { + String logName = config.getValue("kernel.logname"); + Logger.info("Logging to " + logName); + File logFile = new File(logName); + if (logFile.getParentFile().mkdirs()) { + Logger.info("Created log directory: " + + logFile.getParentFile().getAbsolutePath()); + } + if (logFile.createNewFile()) { + Logger.info( + "Created log file: " + logFile.getAbsolutePath()); + } + log = RCRSLogFactory.getLogWriter(logFile); + log.writeRecord(new StartLogRecord()); + log.writeRecord(new InitialConditionsRecord(worldModel)); + log.writeRecord(new ConfigRecord(config)); + } catch (IOException e) { + throw new KernelException("Couldn't open log file for writing", + e); + } catch (LogException e) { + throw new KernelException("Couldn't open log file for writing", + e); + } + config.setValue(Constants.COMMUNICATION_MODEL_KEY, + communicationModel.getClass().getName()); + config.setValue(Constants.PERCEPTION_KEY, + perception.getClass().getName()); + + // simulatorChanges = new ChangeSetComponent(); + + // Initialise + perception.initialise(config, worldModel); + communicationModel.initialise(config, worldModel); + commandFilter.initialise(config); + score.initialise(worldModel, config); + termination.initialise(config); + commandCollector.initialise(config); + + isShutdown = false; + + Logger.info("Kernel initialised"); + Logger.info("Perception module: " + perception); + Logger.info("Communication module: " + communicationModel); + Logger.info("Command filter: " + commandFilter); + Logger.info("Score function: " + score); + Logger.info("Termination condition: " + termination); + Logger.info("Command collector: " + collector); + } finally { + Logger.popLogContext(); + } + } + + /** + * Get the kernel's configuration. + * + * @return The configuration. + */ + public Config getConfig() { + return config; + } + + /** + * Get a snapshot of the kernel's state. + * + * @return A new KernelState snapshot. + */ + public KernelState getState() { + return new KernelState(getTime(), getWorldModel()); + } + + /** + * Add an agent to the system. + * + * @param agent The agent to add. + */ + public void addAgent(AgentProxy agent) { + synchronized (this) { + agents.add(agent); + } + fireAgentAdded(agent); + } + + /** + * Remove an agent from the system. + * + * @param agent The agent to remove. + */ + public void removeAgent(AgentProxy agent) { + synchronized (this) { + agents.remove(agent); + } + fireAgentRemoved(agent); + } + + /** + * Get all agents in the system. + * + * @return An unmodifiable view of all agents. + */ + public Collection<AgentProxy> getAllAgents() { + synchronized (this) { + return Collections.unmodifiableCollection(agents); + } + } + + /** + * Add a simulator to the system. + * + * @param sim The simulator to add. + */ + public void addSimulator(SimulatorProxy sim) { + synchronized (this) { + sims.add(sim); + sim.setEntityIDGenerator(idGenerator); + } + fireSimulatorAdded(sim); + } + + /** + * Remove a simulator from the system. + * + * @param sim The simulator to remove. + */ + public void removeSimulator(SimulatorProxy sim) { + synchronized (this) { + sims.remove(sim); + } + fireSimulatorRemoved(sim); + } + + /** + * Get all simulators in the system. + * + * @return An unmodifiable view of all simulators. + */ + public Collection<SimulatorProxy> getAllSimulators() { + synchronized (this) { + return Collections.unmodifiableCollection(sims); + } + } + + /** + * Add a viewer to the system. + * + * @param viewer The viewer to add. + */ + public void addViewer(ViewerProxy viewer) { + synchronized (this) { + viewers.add(viewer); + } + fireViewerAdded(viewer); + } + + /** + * Remove a viewer from the system. + * + * @param viewer The viewer to remove. + */ + public void removeViewer(ViewerProxy viewer) { + synchronized (this) { + viewers.remove(viewer); + } + fireViewerRemoved(viewer); + } + + /** + * Get all viewers in the system. + * + * @return An unmodifiable view of all viewers. + */ + public Collection<ViewerProxy> getAllViewers() { + synchronized (this) { + return Collections.unmodifiableCollection(viewers); + } + } + + /** + * Add a KernelListener. + * + * @param l The listener to add. + */ + public void addKernelListener(KernelListener l) { + synchronized (listeners) { + listeners.add(l); + } + } + + /** + * Remove a KernelListener. + * + * @param l The listener to remove. + */ + public void removeKernelListener(KernelListener l) { + synchronized (listeners) { + listeners.remove(l); + } + } + + /** + * Get the current time. + * + * @return The current time. + */ + public int getTime() { + synchronized (this) { + return time; + } + } + + /** + * Get the world model. + * + * @return The world model. + */ + public WorldModel<? extends Entity> getWorldModel() { + return worldModel; + } + + /** + * Find out if the kernel has terminated. + * + * @return True if the kernel has terminated, false otherwise. + */ + public boolean hasTerminated() { + synchronized (this) { + return isShutdown || termination.shouldStop(getState()); + } + } + + /** + * Run a single timestep. + * + * @throws InterruptedException If this thread is interrupted during the + * timestep. + * @throws KernelException If there is a problem executing the + * timestep. + * @throws LogException If there is a problem writing the log. + */ + public void timestep() + throws InterruptedException, KernelException, LogException { + try { + Logger.pushLogContext(KERNEL_LOG_CONTEXT); + synchronized (this) { + if (time == 0) { + fireStarted(); + } + if (isShutdown) { + return; + } + ++time; + // Work out what the agents can see and hear (using the commands + // from the previous timestep). + // Wait for new commands + // Send commands to simulators and wait for updates + // Collate updates and broadcast to simulators + // Send perception, commands and updates to viewers + Timestep nextTimestep = new Timestep(time); + Logger.info("Timestep " + time); + Logger.debug("Sending agent updates"); + long start = System.currentTimeMillis(); + sendAgentUpdates(nextTimestep, + previousTimestep == null ? new HashSet<Command>() + : previousTimestep.getCommands()); + long perceptionTime = System.currentTimeMillis(); + Logger.debug("Waiting for commands"); + Collection<Command> commands = waitForCommands(time); + nextTimestep.setCommands(commands); + log.writeRecord(new CommandsRecord(time, commands)); + long commandsTime = System.currentTimeMillis(); + Logger.debug("Broadcasting commands"); + ChangeSet changes = sendCommandsToSimulators(time, commands); + // simulatorUpdates.show(changes); + nextTimestep.setChangeSet(changes); + log.writeRecord(new UpdatesRecord(time, changes)); + long updatesTime = System.currentTimeMillis(); + // Merge updates into world model + worldModel.merge(changes); + long mergeTime = System.currentTimeMillis(); + Logger.debug("Broadcasting updates"); + sendUpdatesToSimulators(time, changes); + sendToViewers(nextTimestep); + long broadcastTime = System.currentTimeMillis(); + Logger.debug("Computing score"); + double s = score.score(worldModel, nextTimestep); + long scoreTime = System.currentTimeMillis(); + nextTimestep.setScore(s); + Logger.info("Timestep " + time + " complete"); + Logger.debug("Score: " + s); + Logger.debug("Perception took : " + + (perceptionTime - start) + "ms"); + Logger.debug("Agent commands took : " + + (commandsTime - perceptionTime) + "ms"); + Logger.debug("Simulator updates took : " + + (updatesTime - commandsTime) + "ms"); + Logger.debug("World model merge took : " + + (mergeTime - updatesTime) + "ms"); + Logger.debug("Update broadcast took : " + + (broadcastTime - mergeTime) + "ms"); + Logger.debug("Score calculation took : " + + (scoreTime - broadcastTime) + "ms"); + Logger.debug("Total time : " + (scoreTime - start) + + "ms"); + fireTimestepCompleted(nextTimestep); + previousTimestep = nextTimestep; + Logger.debug("Commands: " + commands); + Logger.debug( + "Timestep commands: " + previousTimestep.getCommands()); + } + } finally { + Logger.popLogContext(); + } + } + + /** + * Shut down the kernel. This method will notify all + * agents/simulators/viewers of the shutdown. + */ + public void shutdown() { + synchronized (this) { + if (isShutdown) { + return; + } + Logger.info("Kernel is shutting down"); + ExecutorService service = Executors.newFixedThreadPool( + agents.size() + sims.size() + viewers.size()); + List<Callable<Object>> callables = new ArrayList<Callable<Object>>(); + for (AgentProxy next : agents) { + final AgentProxy proxy = next; + callables.add(Executors.callable(new Runnable() { + @Override + public void run() { + proxy.shutdown(); + } + })); + } + for (SimulatorProxy next : sims) { + final SimulatorProxy proxy = next; + callables.add(Executors.callable(new Runnable() { + @Override + public void run() { + proxy.shutdown(); + } + })); + } + for (ViewerProxy next : viewers) { + final ViewerProxy proxy = next; + callables.add(Executors.callable(new Runnable() { + @Override + public void run() { + proxy.shutdown(); + } + })); + } + try { + service.invokeAll(callables); + } catch (InterruptedException e) { + Logger.warn("Interrupted during shutdown"); + } + try { + log.writeRecord(new EndLogRecord()); + log.close(); + } catch (LogException e) { + Logger.error("Error closing log", e); + } + Logger.info("Kernel has shut down"); + isShutdown = true; + fireShutdown(); + } + } + + private void sendAgentUpdates(Timestep timestep, + Collection<Command> commandsLastTimestep) + throws InterruptedException, KernelException, LogException { + perception.setTime(time); + communicationModel.process(time, commandsLastTimestep); + for (AgentProxy next : agents) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + ChangeSet visible = perception.getVisibleEntities(next); + Collection<Command> heard = communicationModel + .getHearing(next.getControlledEntity()); + EntityID id = next.getControlledEntity().getID(); + timestep.registerPerception(id, visible, heard); + log.writeRecord(new PerceptionRecord(time, id, visible, heard)); + next.sendPerceptionUpdate(time, visible, heard); + } + } + + private Collection<Command> waitForCommands(int timestep) + throws InterruptedException { + Collection<Command> commands = commandCollector.getAgentCommands(agents, + timestep); + Logger.debug("Raw commands: " + commands); + commandFilter.filter(commands, getState()); + Logger.debug("Filtered commands: " + commands); + return commands; + } + + /** + * Send commands to all simulators and return which entities have been + * updated by the simulators. + */ + private ChangeSet sendCommandsToSimulators(int timestep, + Collection<Command> commands) throws InterruptedException { + for (SimulatorProxy next : sims) { + next.sendAgentCommands(timestep, commands); + } + // Wait until all simulators have sent updates + ChangeSet result = new ChangeSet(); + for (SimulatorProxy next : sims) { + Logger.debug("Fetching updates from " + next); + result.merge(next.getUpdates(timestep)); + } + return result; + } + + private void sendUpdatesToSimulators(int timestep, ChangeSet updates) + throws InterruptedException { + for (SimulatorProxy next : sims) { + next.sendUpdate(timestep, updates); + } + } + + private void sendToViewers(Timestep timestep) { + for (ViewerProxy next : viewers) { + next.sendTimestep(timestep); + } + } + + private Set<KernelListener> getListeners() { + Set<KernelListener> result; + synchronized (listeners) { + result = new HashSet<KernelListener>(listeners); + } + return result; + } + + private void fireStarted() { + for (KernelListener next : getListeners()) { + next.simulationStarted(this); + } + } + + private void fireShutdown() { + for (KernelListener next : getListeners()) { + next.simulationEnded(this); + } + } + + private void fireTimestepCompleted(Timestep timestep) { + for (KernelListener next : getListeners()) { + next.timestepCompleted(this, timestep); + } + } + + private void fireAgentAdded(AgentProxy agent) { + for (KernelListener next : getListeners()) { + next.agentAdded(this, agent); + } + } + + private void fireAgentRemoved(AgentProxy agent) { + for (KernelListener next : getListeners()) { + next.agentRemoved(this, agent); + } + } + + private void fireSimulatorAdded(SimulatorProxy sim) { + for (KernelListener next : getListeners()) { + next.simulatorAdded(this, sim); + } + } + + private void fireSimulatorRemoved(SimulatorProxy sim) { + for (KernelListener next : getListeners()) { + next.simulatorRemoved(this, sim); + } + } + + private void fireViewerAdded(ViewerProxy viewer) { + for (KernelListener next : getListeners()) { + next.viewerAdded(this, viewer); + } + } + + private void fireViewerRemoved(ViewerProxy viewer) { + for (KernelListener next : getListeners()) { + next.viewerRemoved(this, viewer); + } + } +} diff --git a/modules/kernel/src/kernel/KernelComponent.java b/modules/kernel/src/kernel/KernelComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..fcca62baf4e4d5b48d39967c7467e1ce51bd7a18 --- /dev/null +++ b/modules/kernel/src/kernel/KernelComponent.java @@ -0,0 +1,34 @@ +package kernel; + +import rescuecore2.connection.Connection; +import rescuecore2.messages.Message; + +import java.util.Collection; + +/** + This class is the kernel interface to components (agents, viewers, simulators). + */ +public interface KernelComponent { + /** + Send a set of messages to this component. + @param m The messages to send. + */ + void send(Collection<? extends Message> m); + + /** + Shut this component down. + */ + void shutdown(); + + /** + Get this component's connection. + @return The connection to the component. + */ + Connection getConnection(); + + /** + Get the name of this component. + @return The name of the component. + */ + String getName(); +} diff --git a/modules/kernel/src/kernel/KernelConstants.java b/modules/kernel/src/kernel/KernelConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..19a172dd07aa973934a18fd0ac403f70236002f1 --- /dev/null +++ b/modules/kernel/src/kernel/KernelConstants.java @@ -0,0 +1,35 @@ +package kernel; + +/** + Some useful constants for the kernel. +*/ +public final class KernelConstants { + /** The config key for gis implementations. */ + public static final String GIS_KEY = "kernel.gis"; + + /** The config key for perception implementations. */ + public static final String PERCEPTION_KEY = "kernel.perception"; + + /** The config key for communication model implementations. */ + public static final String COMMUNICATION_MODEL_KEY = "kernel.communication"; + + /** The config key for agent implementations. */ + public static final String AGENTS_KEY = "kernel.agents"; + + /** The config key for simulator implementations. */ + public static final String SIMULATORS_KEY = "kernel.simulators"; + + /** The config key for viewer implementations. */ + public static final String VIEWERS_KEY = "kernel.viewers"; + + /** The config key for component implementations. */ + public static final String COMPONENTS_KEY = "kernel.components"; + + /** Whether to run the kernel in inline-only mode. */ + public static final String INLINE_ONLY_KEY = "kernel.inline-only"; + + /** The config key for ignoring agent commands at the start of the simulation. */ + public static final String IGNORE_AGENT_COMMANDS_KEY = "kernel.agents.ignoreuntil"; + + private KernelConstants() {} +} diff --git a/modules/kernel/src/kernel/KernelException.java b/modules/kernel/src/kernel/KernelException.java new file mode 100644 index 0000000000000000000000000000000000000000..e1620fdb8426c93a8459ed0b90412e573eac5ce4 --- /dev/null +++ b/modules/kernel/src/kernel/KernelException.java @@ -0,0 +1,39 @@ +package kernel; + +/** + Root of the kernel exception heirarchy. + */ + +public class KernelException extends Exception { + /** + Construct a kernel exception with no information. + */ + public KernelException() { + super(); + } + + /** + Construct a kernel exception with an error message. + @param msg The error message. + */ + public KernelException(String msg) { + super(msg); + } + + /** + Construct a kernel exception that was caused by another exception. + @param cause The cause of this exception. + */ + public KernelException(Throwable cause) { + super(cause); + } + + /** + Construct a kernel exception with an error message and an underlying cause. + @param msg The error message. + @param cause The cause of this exception. + */ + public KernelException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/kernel/src/kernel/KernelListener.java b/modules/kernel/src/kernel/KernelListener.java new file mode 100644 index 0000000000000000000000000000000000000000..da5e5d0555dea7b02fc9c1a858e27b7187c6b8a1 --- /dev/null +++ b/modules/kernel/src/kernel/KernelListener.java @@ -0,0 +1,69 @@ +package kernel; + +import rescuecore2.Timestep; + +/** + Interface for objects that are interested in kernel events. + */ +public interface KernelListener { + /** + Notification that the kernel has started the simulation. + @param kernel The kernel. + */ + void simulationStarted(Kernel kernel); + + /** + Notification that the kernel has ended the simulation and shut down. + @param kernel The kernel. + */ + void simulationEnded(Kernel kernel); + + /** + Notification that a timestep has been completed. + @param kernel The kernel. + @param time The timestep that has just been completed. + */ + void timestepCompleted(Kernel kernel, Timestep time); + + /** + Notification that an agent has been added. + @param kernel The kernel. + @param agent The agent that was added. + */ + void agentAdded(Kernel kernel, AgentProxy agent); + + /** + Notification that an agent has been removed. + @param kernel The kernel. + @param agent The agent that was removed. + */ + void agentRemoved(Kernel kernel, AgentProxy agent); + + /** + Notification that a simulator has been added. + @param kernel The kernel. + @param simulator The simulator that was added. + */ + void simulatorAdded(Kernel kernel, SimulatorProxy simulator); + + /** + Notification that a simulator has been removed. + @param kernel The kernel. + @param simulator The simulator that was removed. + */ + void simulatorRemoved(Kernel kernel, SimulatorProxy simulator); + + /** + Notification that a viewer has been added. + @param kernel The kernel. + @param viewer The viewer that was added. + */ + void viewerAdded(Kernel kernel, ViewerProxy viewer); + + /** + Notification that a viewer has been removed. + @param kernel The kernel. + @param viewer The viewer that was removed. + */ + void viewerRemoved(Kernel kernel, ViewerProxy viewer); +} diff --git a/modules/kernel/src/kernel/KernelListenerAdapter.java b/modules/kernel/src/kernel/KernelListenerAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..dc56055b5f11574f0900043b49feacbe36d5db92 --- /dev/null +++ b/modules/kernel/src/kernel/KernelListenerAdapter.java @@ -0,0 +1,35 @@ +package kernel; + +import rescuecore2.Timestep; + +/** + Abstract class for objects that want to implement a subset of the KernelListener interface. All default method implementations do nothing. + */ +public class KernelListenerAdapter implements KernelListener { + @Override + public void simulationStarted(Kernel kernel) {} + + @Override + public void simulationEnded(Kernel kernel) {} + + @Override + public void timestepCompleted(Kernel kernel, Timestep time) {} + + @Override + public void agentAdded(Kernel kernel, AgentProxy agent) {} + + @Override + public void agentRemoved(Kernel kernel, AgentProxy agent) {} + + @Override + public void simulatorAdded(Kernel kernel, SimulatorProxy simulator) {} + + @Override + public void simulatorRemoved(Kernel kernel, SimulatorProxy simulator) {} + + @Override + public void viewerAdded(Kernel kernel, ViewerProxy viewer) {} + + @Override + public void viewerRemoved(Kernel kernel, ViewerProxy viewer) {} +} diff --git a/modules/kernel/src/kernel/KernelStartupOptions.java b/modules/kernel/src/kernel/KernelStartupOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..e43ef97ee183e9fc58f7e9e4c0a56658b7b7a247 --- /dev/null +++ b/modules/kernel/src/kernel/KernelStartupOptions.java @@ -0,0 +1,305 @@ +package kernel; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.misc.Pair; +import rescuecore2.config.Config; +import rescuecore2.components.Component; +import rescuecore2.components.Simulator; +import rescuecore2.components.Viewer; +import rescuecore2.components.Agent; +import rescuecore2.log.Logger; + +/** + Container class for all kernel startup options. +*/ +public class KernelStartupOptions { + private static final String AUTO_SUFFIX = ".auto"; + + private List<WorldModelCreator> worldOptions; + private List<Perception> perceptionOptions; + private List<CommunicationModel> commsOptions; + + private Map<Simulator, Integer> sims; + private Map<Viewer, Integer> viewers; + private Map<Agent, Integer> agents; + private Map<Component, Integer> other; + + private WorldModelCreator world; + private Perception perception; + private CommunicationModel comms; + + /** + Create a KernelStartupOptions. + @param config The system configuration. + */ + public KernelStartupOptions(Config config) { + Pair<List<WorldModelCreator>, Integer> w = createOptions(config, KernelConstants.GIS_KEY, WorldModelCreator.class); + worldOptions = w.first(); + world = worldOptions.get(w.second()); + + Pair<List<Perception>, Integer> p = createOptions(config, KernelConstants.PERCEPTION_KEY, Perception.class); + perceptionOptions = p.first(); + perception = perceptionOptions.get(p.second()); + + Pair<List<CommunicationModel>, Integer> c = createOptions(config, KernelConstants.COMMUNICATION_MODEL_KEY, CommunicationModel.class); + commsOptions = c.first(); + comms = commsOptions.get(c.second()); + + sims = createComponentOptions(config, KernelConstants.SIMULATORS_KEY, Simulator.class); + viewers = createComponentOptions(config, KernelConstants.VIEWERS_KEY, Viewer.class); + agents = createComponentOptions(config, KernelConstants.AGENTS_KEY, Agent.class); + other = createComponentOptions(config, KernelConstants.COMPONENTS_KEY, Component.class); + } + + /** + Get the names of all components that should be started inline. + @return All inline component class names and the requested number of each. + */ + public Collection<Pair<String, Integer>> getInlineComponents() { + List<Pair<String, Integer>> result = new ArrayList<Pair<String, Integer>>(); + for (Map.Entry<Simulator, Integer> next : sims.entrySet()) { + result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue())); + } + for (Map.Entry<Viewer, Integer> next : viewers.entrySet()) { + result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue())); + } + for (Map.Entry<Agent, Integer> next : agents.entrySet()) { + result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue())); + } + for (Map.Entry<Component, Integer> next : other.entrySet()) { + result.add(new Pair<String, Integer>(next.getKey().getClass().getName(), next.getValue())); + } + return result; + } + + /** + Get the WorldModelCreator the kernel should use. + @return The selected WorldModelCreator. + */ + public WorldModelCreator getWorldModelCreator() { + return world; + } + + /** + Set the WorldModelCreator the kernel should use. + @param creator The selected WorldModelCreator. + */ + public void setWorldModelCreator(WorldModelCreator creator) { + this.world = creator; + } + + /** + Get the list of available WorldModelCreator implementations. + @return All known WorldModelCreators. + */ + public List<WorldModelCreator> getAvailableWorldModelCreators() { + return Collections.unmodifiableList(worldOptions); + } + + /** + Get the Perception module the kernel should use. + @return The selected Perception. + */ + public Perception getPerception() { + return perception; + } + + /** + Set the Perception module the kernel should use. + @param p The selected Perception. + */ + public void setPerception(Perception p) { + perception = p; + } + + /** + Get the list of available Perception implementations. + @return All known Perceptions. + */ + public List<Perception> getAvailablePerceptions() { + return Collections.unmodifiableList(perceptionOptions); + } + + /** + Get the CommunicationModel the kernel should use. + @return The selected CommunicationModel. + */ + public CommunicationModel getCommunicationModel() { + return comms; + } + + /** + Set the CommunicationModel the kernel should use. + @param c The selected CommunicationModel. + */ + public void setCommunicationModel(CommunicationModel c) { + comms = c; + } + + /** + Get the list of available CommunicationModel implementations. + @return All known CommunicationModels. + */ + public List<CommunicationModel> getAvailableCommunicationModels() { + return Collections.unmodifiableList(commsOptions); + } + + /** + Get the list of available Simulator components. + @return All known Simulators. + */ + public Collection<Simulator> getAvailableSimulators() { + return Collections.unmodifiableSet(sims.keySet()); + } + + /** + Get the list of available Viewer components. + @return All known Viewers. + */ + public Collection<Viewer> getAvailableViewers() { + return Collections.unmodifiableSet(viewers.keySet()); + } + + /** + Get the list of available Agent components. + @return All known Agents. + */ + public Collection<Agent> getAvailableAgents() { + return Collections.unmodifiableSet(agents.keySet()); + } + + /** + Get the list of available components that are not simulators, viewers or agents. + @return All known Components that are not simulators, viewers or agents. + */ + public Collection<Component> getAvailableComponents() { + return Collections.unmodifiableSet(other.keySet()); + } + + /** + Get the number of instances of a type of component to start. + @param c The component type. + @return The number of instances to start. + */ + public int getInstanceCount(Component c) { + if (sims.containsKey(c)) { + return sims.get(c); + } + if (viewers.containsKey(c)) { + return viewers.get(c); + } + if (agents.containsKey(c)) { + return agents.get(c); + } + if (other.containsKey(c)) { + return other.get(c); + } + throw new IllegalArgumentException("Component " + c + " not recognised"); + } + + /** + Set the number of instances of a type of component to start. + @param c The component type. + @param count The number of instances to start. + */ + public void setInstanceCount(Component c, int count) { + if (c instanceof Simulator) { + sims.put((Simulator)c, count); + } + else if (c instanceof Viewer) { + viewers.put((Viewer)c, count); + } + else if (c instanceof Agent) { + agents.put((Agent)c, count); + } + else { + other.put(c, count); + } + } + + private <T> Pair<List<T>, Integer> createOptions(Config config, String key, Class<T> expectedClass) { + List<T> instances = new ArrayList<T>(); + int index = 0; + int selectedIndex = 0; + Logger.trace("Loading options: " + key); + List<String> classNames = config.getArrayValue(key); + String auto = config.getValue(key + AUTO_SUFFIX, null); + boolean autoFound = false; + for (String next : classNames) { + Logger.trace("Option found: '" + next + "'"); + T t = instantiate(next, expectedClass); + if (t != null) { + instances.add(t); + if (next.equals(auto)) { + selectedIndex = index; + autoFound = true; + } + ++index; + } + } + if (auto != null && !autoFound) { + Logger.warn("Could not find class " + auto + " in config key " + key + ". Values found: " + classNames); + } + return new Pair<List<T>, Integer>(instances, selectedIndex); + } + + private <T> Map<T, Integer> createComponentOptions(Config config, String key, Class<T> expectedClass) { + Logger.trace("Loading component options: " + key); + Map<T, Integer> result = new HashMap<T, Integer>(); + List<String> classNames = config.getArrayValue(key, ""); + List<String> autoClassNames = config.getArrayValue(key + AUTO_SUFFIX, ""); + Set<String> allClassNames = new HashSet<String>(classNames); + allClassNames.addAll(strip(autoClassNames)); + for (String next : allClassNames) { + Logger.trace("Option found: '" + next + "'"); + T t = instantiate(next, expectedClass); + if (t != null) { + int count = getStartCount(next, autoClassNames); + result.put(t, count); + } + } + return result; + } + + private int getStartCount(String className, List<String> auto) { + for (String next : auto) { + if (next.startsWith(className)) { + int index = next.indexOf("*"); + if (index == -1) { + return 1; + } + String arg = next.substring(index + 1); + if ("n".equals(arg)) { + return Integer.MAX_VALUE; + } + return Integer.parseInt(arg); + } + } + return 0; + } + + private List<String> strip(List<String> autoClassNames) { + List<String> result = new ArrayList<String>(autoClassNames.size()); + // Remove any trailing *n + for (String s : autoClassNames) { + int index = s.indexOf("*"); + if (index != -1) { + result.add(s.substring(0, index)); + } + else { + result.add(s); + } + } + return result; + } +} diff --git a/modules/kernel/src/kernel/KernelState.java b/modules/kernel/src/kernel/KernelState.java new file mode 100644 index 0000000000000000000000000000000000000000..2e6ba2505445c5eda840f059806580edba6f7cf1 --- /dev/null +++ b/modules/kernel/src/kernel/KernelState.java @@ -0,0 +1,38 @@ +package kernel; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +/** + A class for obtaining information about the state of the kernel. +*/ +public class KernelState { + private int time; + private WorldModel<? extends Entity> model; + + /** + Construct a snapshot of the kernel state. + @param time The current time. + @param model The world model snapshot. + */ + public KernelState(int time, WorldModel<? extends Entity> model) { + this.time = time; + this.model = model; + } + + /** + Get the current time. + @return The current time. + */ + public int getTime() { + return time; + } + + /** + Get the world model. + @return The world model. + */ + public WorldModel<? extends Entity> getWorldModel() { + return model; + } +} diff --git a/modules/kernel/src/kernel/OrTerminationCondition.java b/modules/kernel/src/kernel/OrTerminationCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..4ccefd8e13912587a0b9b0f570489b9e54df7fbf --- /dev/null +++ b/modules/kernel/src/kernel/OrTerminationCondition.java @@ -0,0 +1,53 @@ +package kernel; + +import rescuecore2.config.Config; + +import java.util.Collection; +import java.util.Iterator; + +/** + A TerminationCondition that returns true if any of its children return true. +*/ +public class OrTerminationCondition implements TerminationCondition { + private Collection<TerminationCondition> children; + + /** + Construct a new OrTerminationCondition. + @param children The child conditions. This must have at least one element. + */ + public OrTerminationCondition(Collection<TerminationCondition> children) { + if (children == null || children.size() == 0) { + throw new IllegalArgumentException("Must have at least one child"); + } + this.children = children; + } + + @Override + public boolean shouldStop(KernelState state) { + for (TerminationCondition next : children) { + if (next.shouldStop(state)) { + return true; + } + } + return false; + } + + @Override + public void initialise(Config config) { + for (TerminationCondition next : children) { + next.initialise(config); + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + for (Iterator<TerminationCondition> it = children.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(" | "); + } + } + return result.toString(); + } +} diff --git a/modules/kernel/src/kernel/Perception.java b/modules/kernel/src/kernel/Perception.java new file mode 100644 index 0000000000000000000000000000000000000000..6508a165a09a5949a20e5aafc145a16d3b76cc2b --- /dev/null +++ b/modules/kernel/src/kernel/Perception.java @@ -0,0 +1,31 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.ChangeSet; + +/** + Implementations of this interface are responsible for determining what entities/properties each agent can see. + */ +public interface Perception { + /** + Initialise this perception object. + @param config The kernel configuration. + @param world The world model. + */ + void initialise(Config config, WorldModel<? extends Entity> world); + + /** + Determine what Entities are visible to a particular agent. The returned Entities should be copies of Entities in the ground-truth WorldModel. Only visible properties should have defined values. + @param agent The agent that is perceiving the world. + @return A collection of entities that the agent can perceive. + */ + ChangeSet getVisibleEntities(AgentProxy agent); + + /** + Notify this perception object of the current time. + @param timestep The current timestep. + */ + void setTime(int timestep); +} diff --git a/modules/kernel/src/kernel/RemoteGISWorldModelCreator.java b/modules/kernel/src/kernel/RemoteGISWorldModelCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..875adb7afb0d18c832445a5e021dd4dae2a44c45 --- /dev/null +++ b/modules/kernel/src/kernel/RemoteGISWorldModelCreator.java @@ -0,0 +1,121 @@ +package kernel; + + + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +import org.dom4j.DocumentException; + +import rescuecore2.Constants; +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.TCPConnection; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.scenario.Scenario; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.GKConnectOK; +import rescuecore2.messages.control.GKConnectError; +import rescuecore2.messages.control.KGConnect; +import rescuecore2.messages.control.KGAcknowledge; +import rescuecore2.log.Logger; + +/** + * A WorldModelCreator that talks to a remote GIS. + */ +public class RemoteGISWorldModelCreator implements WorldModelCreator { + private int nextID; + + @Override + public WorldModel<? extends Entity> buildWorldModel(Config config) + throws KernelException { + Logger.info("Connecting to remote GIS..."); + DefaultWorldModel<Entity> world = DefaultWorldModel.create(); + CountDownLatch latch = new CountDownLatch(1); + int gisPort = config.getIntValue(Constants.GIS_PORT_NUMBER_KEY, + Constants.DEFAULT_GIS_PORT_NUMBER); + Connection conn; + try { + conn = new TCPConnection(gisPort); + conn.addConnectionListener(new GISConnectionListener(latch, world)); + conn.startup(); + conn.sendMessage(new KGConnect(1)); + } catch (IOException e) { + throw new KernelException("Couldn't connect to GIS", e); + } catch (ConnectionException e) { + throw new KernelException("Couldn't connect to GIS", e); + } + // Wait for a reply + try { + latch.await(); + } catch (InterruptedException e) { + throw new KernelException("Interrupted while connecting to GIS", e); + } + conn.shutdown(); + return world; + } + + @Override + public String toString() { + return "Remote GIS"; + } + + @Override + public EntityID generateID() { + synchronized (this) { + return new EntityID(nextID++); + } + } + + /** + * Listener for the GIS connection. + */ + private class GISConnectionListener implements ConnectionListener { + private CountDownLatch latch; + private DefaultWorldModel<Entity> model; + + public GISConnectionListener(CountDownLatch latch, + DefaultWorldModel<Entity> model) { + this.latch = latch; + this.model = model; + } + + public void messageReceived(Connection c, Message m) { + if (m instanceof GKConnectOK) { + try { + // Update the internal world model + model.removeAllEntities(); + model.addEntities(((GKConnectOK) m).getEntities()); + // Send an acknowledgement + c.sendMessage(new KGAcknowledge()); + Logger.info("GIS connected OK"); + // Trigger the countdown latch + latch.countDown(); + nextID = 0; + for (Entity next : model) { + nextID = Math.max(nextID, next.getID().getValue()); + } + ++nextID; + } catch (ConnectionException e) { + Logger.error("RemoteGISWorldModelCreator.messageReceived", + e); + } + } + if (m instanceof GKConnectError) { + Logger.error("Error connecting to remote GIS: " + + ((GKConnectError) m).getReason()); + latch.countDown(); + } + } + } + + @Override + public Scenario getScenario(Config config) throws DocumentException{ + return null;// TODO Salim implement + } +} diff --git a/modules/kernel/src/kernel/SimulatorProxy.java b/modules/kernel/src/kernel/SimulatorProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..af1c33699f35cbbaa48dc09e140f7daafa833d9f --- /dev/null +++ b/modules/kernel/src/kernel/SimulatorProxy.java @@ -0,0 +1,146 @@ +package kernel; + +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.messages.Message; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.SKUpdate; +import rescuecore2.messages.control.KSUpdate; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.messages.control.EntityIDRequest; +import rescuecore2.messages.control.EntityIDResponse; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.log.Logger; + +import java.util.Collection; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +/** + This class is the kernel interface to a simulator. + */ +public class SimulatorProxy extends AbstractKernelComponent { + private Map<Integer, ChangeSet> updates; + private int id; + private EntityIDGenerator idGenerator; + + /** + Construct a new simulator. + @param name The name of the simulator. + @param id The ID of the simulator. + @param c The connection this simulator is using. + */ + public SimulatorProxy(String name, int id, Connection c) { + super(name, c); + this.id = id; + updates = new HashMap<Integer, ChangeSet>(); + c.addConnectionListener(new SimulatorConnectionListener()); + } + + /** + Get updates from this simulator. This method may block until updates are available. + @param time The timestep to get updates for. + @return A ChangeSet representing the updates from this simulator. + @throws InterruptedException If this thread is interrupted while waiting for updates. + */ + public ChangeSet getUpdates(int time) throws InterruptedException { + ChangeSet result = null; + synchronized (updates) { + while (result == null) { + result = updates.get(time); + if (result == null) { + updates.wait(1000); + } + } + } + return result; + } + + /** + Send an update message to this simulator. + @param time The simulation time. + @param update The updated entities. + */ + public void sendUpdate(int time, ChangeSet update) { + send(new KSUpdate(id, time, update)); + } + + /** + Send a set of agent commands to this simulator. + @param time The current time. + @param commands The agent commands to send. + */ + public void sendAgentCommands(int time, Collection<? extends Command> commands) { + send(new KSCommands(id, time, commands)); + } + + @Override + public String toString() { + return getName() + " (" + id + "): " + getConnection().toString(); + } + + /** + Set the EntityIDGenerator. + @param generator The new EntityIDGenerator. + */ + public void setEntityIDGenerator(EntityIDGenerator generator) { + idGenerator = generator; + } + + /** + Register an update from the simulator. + @param time The timestep of the update. + @param changes The set of changes. + */ + protected void updateReceived(int time, ChangeSet changes) { + synchronized (updates) { + ChangeSet c = updates.get(time); + if (c == null) { + c = new ChangeSet(); + updates.put(time, c); + } + c.merge(changes); + updates.notifyAll(); + } + } + + private class SimulatorConnectionListener implements ConnectionListener { + @Override + public void messageReceived(Connection connection, Message msg) { + Logger.pushLogContext(Kernel.KERNEL_LOG_CONTEXT); + try { + if (msg instanceof SKUpdate) { + SKUpdate update = (SKUpdate)msg; + if (update.getSimulatorID() == id) { + updateReceived(update.getTime(), update.getChangeSet()); + } + } + if (msg instanceof EntityIDRequest) { + EntityIDRequest req = (EntityIDRequest)msg; + Logger.debug("Simulator proxy " + id + " received entity ID request: " + msg); + if (req.getSimulatorID() == id) { + int requestID = req.getRequestID(); + int count = req.getCount(); + List<EntityID> result = new ArrayList<EntityID>(count); + for (int i = 0; i < count; ++i) { + result.add(idGenerator.generateID()); + } + Logger.debug("Simulator proxy " + id + " sending new IDs: " + result); + send(new EntityIDResponse(id, requestID, result)); + } + } + } + finally { + Logger.popLogContext(); + } + } + } + + @Override + public int hashCode() { + return Integer.hashCode(id); + } +} diff --git a/modules/kernel/src/kernel/StartKernel.java b/modules/kernel/src/kernel/StartKernel.java new file mode 100644 index 0000000000000000000000000000000000000000..cfd0ac3e124945330f31e4978bdd18a28115b0ec --- /dev/null +++ b/modules/kernel/src/kernel/StartKernel.java @@ -0,0 +1,512 @@ +package kernel; + +import static rescuecore2.misc.java.JavaTools.instantiate; +import static rescuecore2.misc.java.JavaTools.instantiateFactory; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; + +import kernel.ui.KernelGUI; +import kernel.ui.KernelStartupPanel; +import kernel.ui.ScoreGraph; +import kernel.ui.ScoreTable; + +import org.dom4j.DocumentException; + +import rescuecore2.Constants; +import rescuecore2.GUIComponent; +import rescuecore2.components.Component; +import rescuecore2.components.ComponentConnectionException; +import rescuecore2.components.ComponentInitialisationException; +import rescuecore2.components.ComponentLauncher; +import rescuecore2.config.ClassNameSetValueConstraint; +import rescuecore2.config.ClassNameValueConstraint; +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.config.IntegerValueConstraint; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionManager; +import rescuecore2.log.LogException; +import rescuecore2.log.Logger; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.MutableBoolean; +import rescuecore2.misc.Pair; +import rescuecore2.misc.java.LoadableType; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.registry.Factory; +import rescuecore2.registry.Registry; +import rescuecore2.scenario.Scenario; +import rescuecore2.score.ScoreFunction; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * A class for launching the kernel. + */ +public final class StartKernel { + private static final String NO_STARTUP_MENU = "--nomenu"; + private static final String NO_GUI = "--nogui"; + private static final String AUTORUN = "--autorun"; + + private static final String GIS_MANIFEST_KEY = "Gis"; + private static final String PERCEPTION_MANIFEST_KEY = "Perception"; + private static final String COMMUNICATION_MANIFEST_KEY = "CommunicationModel"; + + private static final String COMMAND_COLLECTOR_KEY = "kernel.commandcollectors"; + + private static final String TERMINATION_KEY = "kernel.termination"; + + private static final String GIS_REGEX = "(.+WorldModelCreator).class"; + private static final String PERCEPTION_REGEX = "(.+Perception).class"; + private static final String COMMUNICATION_REGEX = "(.+CommunicationModel).class"; + + private static final LoadableType GIS_LOADABLE_TYPE = new LoadableType(GIS_MANIFEST_KEY, GIS_REGEX, + WorldModelCreator.class); + private static final LoadableType PERCEPTION_LOADABLE_TYPE = new LoadableType(PERCEPTION_MANIFEST_KEY, + PERCEPTION_REGEX, Perception.class); + private static final LoadableType COMMUNICATION_LOADABLE_TYPE = new LoadableType(COMMUNICATION_MANIFEST_KEY, + COMMUNICATION_REGEX, CommunicationModel.class); + + private static final String KERNEL_STARTUP_TIME_KEY = "kernel.startup.connect-time"; + + private static final String COMMAND_FILTERS_KEY = "kernel.commandfilters"; + private static final String AGENT_REGISTRAR_KEY = "kernel.agents.registrar"; + private static final String GUI_COMPONENTS_KEY = "kernel.ui.components"; + + /** Utility class: private constructor. */ + private StartKernel() { + } + + /** + * Start a kernel. + * + * @param args Command line arguments. + * @throws DocumentException + */ + public static void main(String[] args) throws DocumentException { + Config config = new Config(); + boolean showStartupMenu = true; + boolean showGUI = true; + boolean autorun = false; + Logger.setLogContext("startup"); + try { + args = CommandLineOptions.processArgs(args, config); + for (String arg : args) { + if (arg.equalsIgnoreCase(NO_GUI)) { + showGUI = false; + } else if (arg.equalsIgnoreCase(NO_STARTUP_MENU)) { + showStartupMenu = false; + } else if (arg.equalsIgnoreCase(AUTORUN)) { + autorun = true; + } else { + Logger.warn("Unrecognised option: " + arg); + } + } + // Process jar files + processJarFiles(config); + Registry localRegistry = new Registry("Kernel local registry"); + // Register preferred message, entity and property factories + for (String next : config.getArrayValue(Constants.FACTORY_KEY, "")) { + Factory factory = instantiateFactory(next, Factory.class); + if (factory != null) { + localRegistry.registerFactory(factory); + Logger.info("Registered local factory: " + next); + } + } + + config.addConstraint(new IntegerValueConstraint(Constants.KERNEL_PORT_NUMBER_KEY, 1, 65535)); + config.addConstraint(new IntegerValueConstraint(KERNEL_STARTUP_TIME_KEY, 0, Integer.MAX_VALUE)); + config.addConstraint(new ClassNameSetValueConstraint(Constants.FACTORY_KEY, Factory.class)); + config.addConstraint(new ClassNameSetValueConstraint(COMMAND_FILTERS_KEY, CommandFilter.class)); + config.addConstraint(new ClassNameSetValueConstraint(TERMINATION_KEY, TerminationCondition.class)); + config.addConstraint(new ClassNameSetValueConstraint(COMMAND_COLLECTOR_KEY, CommandCollector.class)); + config.addConstraint(new ClassNameSetValueConstraint(GUI_COMPONENTS_KEY, GUIComponent.class)); + config.addConstraint(new ClassNameValueConstraint(AGENT_REGISTRAR_KEY, AgentRegistrar.class)); + config.addConstraint(new ClassNameValueConstraint(Constants.SCORE_FUNCTION_KEY, ScoreFunction.class)); + + Logger.setLogContext("kernel"); + final KernelInfo kernelInfo = createKernel(config, showStartupMenu); + if (kernelInfo == null) { + System.exit(0); + } + KernelGUI gui = null; + if (showGUI) { + gui = new KernelGUI(kernelInfo.kernel, kernelInfo.componentManager, config, localRegistry, !autorun); + for (GUIComponent next : kernelInfo.guiComponents) { + gui.addGUIComponent(next); + if (next instanceof KernelListener) { + kernelInfo.kernel.addKernelListener((KernelListener) next); + } + } + JFrame frame = new JFrame("Kernel GUI"); + frame.setExtendedState(JFrame.MAXIMIZED_BOTH); + frame.getContentPane().add(gui); + frame.pack(); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + kernelInfo.kernel.shutdown(); + System.exit(0); + } + }); + frame.setVisible(true); + } + initialiseKernel(kernelInfo, config, localRegistry); + autostartComponents(kernelInfo, localRegistry, gui, config); + if (!showGUI || autorun) { + waitForComponentManager(kernelInfo, config); + Kernel kernel = kernelInfo.kernel; + while (!kernel.hasTerminated()) { + kernel.timestep(); + } + kernel.shutdown(); + } + } catch (ConfigException e) { + Logger.fatal("Couldn't start kernel", e); + } catch (KernelException e) { + Logger.fatal("Couldn't start kernel", e); + } catch (IOException e) { + Logger.fatal("Couldn't start kernel", e); + } catch (LogException e) { + Logger.fatal("Couldn't write log", e); + } catch (InterruptedException e) { + Logger.fatal("Kernel interrupted"); + } catch (DocumentException e) { + Logger.fatal("Document Exception ", e); + } + } + + private static KernelInfo createKernel(Config config, boolean showMenu) throws KernelException, DocumentException { + KernelStartupOptions options = new KernelStartupOptions(config); + // Show the chooser GUI + if (showMenu) { + JFrame frame = new JFrame() { + private static final long serialVersionUID = 1L; + { + setUndecorated(true); + setVisible(true); + setLocationRelativeTo(null); + setTitle("RCRS Start options"); + setVisible(true); + java.awt.Toolkit.getDefaultToolkit().beep(); + setAlwaysOnTop(true); + setAlwaysOnTop(false); + } + }; + final JDialog dialog = new JDialog(frame, "Setup kernel options", true) { + private static final long serialVersionUID = 1L; + + @Override + public void setVisible(boolean b) { + super.setVisible(b); + if (!isVisible()) + frame.dispose(); + } + }; + KernelStartupPanel panel = new KernelStartupPanel(config, options); + JButton okButton = new JButton("OK"); + JButton cancelButton = new JButton("Cancel"); + JPanel buttons = new JPanel(new FlowLayout()); + buttons.add(okButton); + buttons.add(cancelButton); + dialog.getContentPane().add(panel, BorderLayout.CENTER); + dialog.getContentPane().add(buttons, BorderLayout.SOUTH); + final MutableBoolean ok = new MutableBoolean(true); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ok.set(true); + dialog.setVisible(false); + dialog.dispose(); + } + }); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ok.set(false); + dialog.setVisible(false); + dialog.dispose(); + } + }); + dialog.pack(); + dialog.setAlwaysOnTop(true); + dialog.setAlwaysOnTop(false); + dialog.setVisible(true); + if (!ok.get()) { + return null; + } + } + WorldModelCreator gis = options.getWorldModelCreator(); + Perception perception = options.getPerception(); + CommunicationModel comms = options.getCommunicationModel(); + CommandFilter filter = makeCommandFilter(config); + TerminationCondition termination = makeTerminationCondition(config); + ScoreFunction score = makeScoreFunction(config); + CommandCollector collector = makeCommandCollector(config); + + // Get the world model + WorldModel<? extends Entity> worldModel = gis.buildWorldModel(config); + Scenario scenario = gis.getScenario(config); + // Create the kernel + ScoreGraph graph = new ScoreGraph(score); + Kernel kernel = new Kernel(config, perception, comms, worldModel, gis, filter, termination, graph, collector); + // Create the component manager + ComponentManager componentManager = new ComponentManager(kernel, worldModel, config, scenario); + KernelInfo result = new KernelInfo(kernel, options, componentManager, + makeGUIComponents(config, componentManager, perception, comms, termination, filter, graph, collector, score)); + return result; + } + + private static void initialiseKernel(KernelInfo kernel, Config config, Registry registry) throws KernelException { + registerInitialAgents(config, kernel.componentManager, kernel.kernel.getWorldModel()); + if (!config.getBooleanValue(KernelConstants.INLINE_ONLY_KEY, false)) { + // Start the connection manager + ConnectionManager connectionManager = new ConnectionManager(); + try { + connectionManager.listen(config.getIntValue(Constants.KERNEL_PORT_NUMBER_KEY), registry, + kernel.componentManager); + } catch (IOException e) { + throw new KernelException("Couldn't open kernel port", e); + } + } + } + + private static void waitForComponentManager(final KernelInfo kernel, Config config) throws KernelException { + // Wait for all connections + // Set up a CountDownLatch + final CountDownLatch latch = new CountDownLatch(1); + final long timeout = config.getIntValue(KERNEL_STARTUP_TIME_KEY, -1); + Thread timeoutThread = null; + if (timeout > 0) { + timeoutThread = new Thread() { + public void run() { + try { + Thread.sleep(timeout); + latch.countDown(); + } catch (InterruptedException e) { + } + } + }; + } + Thread waitThread = new Thread() { + public void run() { + try { + kernel.componentManager.waitForAllAgents(); + kernel.componentManager.waitForAllSimulators(); + kernel.componentManager.waitForAllViewers(); + } catch (InterruptedException e) { + } + latch.countDown(); + } + }; + waitThread.start(); + if (timeoutThread != null) { + timeoutThread.start(); + } + // Wait at the latch until either everything is connected or the + // connection timeout expires + Logger.info("Waiting for all agents, simulators and viewers to connect."); + if (timeout > -1) { + Logger.info("Connection timeout is " + timeout + "ms"); + } + try { + latch.await(); + } catch (InterruptedException e) { + waitThread.interrupt(); + if (timeoutThread != null) { + timeoutThread.interrupt(); + } + throw new KernelException("Interrupted"); + } + } + + private static void autostartComponents(KernelInfo info, Registry registry, KernelGUI gui, Config config) + throws InterruptedException { + KernelStartupOptions options = info.options; + Collection<Callable<Void>> all = new ArrayList<Callable<Void>>(); + Config launchConfig = new Config(config); + launchConfig.removeExcept(Constants.RANDOM_SEED_KEY, Constants.RANDOM_CLASS_KEY); + for (Pair<String, Integer> next : options.getInlineComponents()) { + if (next.second() > 0) { + all.add(new ComponentStarter(next.first(), info.componentManager, next.second(), registry, gui, launchConfig)); + } + } + ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + service.invokeAll(all); + } + + private static void registerInitialAgents(Config config, ComponentManager c, WorldModel<? extends Entity> model) + throws KernelException { + AgentRegistrar ar = instantiate(config.getValue(AGENT_REGISTRAR_KEY), AgentRegistrar.class); + if (ar == null) { + throw new KernelException("Couldn't instantiate agent registrar"); + } + ar.registerAgents(model, config, c); + } + + private static CommandFilter makeCommandFilter(Config config) { + ChainedCommandFilter result = new ChainedCommandFilter(); + List<String> classNames = config.getArrayValue(COMMAND_FILTERS_KEY, null); + for (String next : classNames) { + Logger.debug("Command filter found: '" + next + "'"); + CommandFilter f = instantiate(next, CommandFilter.class); + if (f != null) { + result.addFilter(f); + } + } + return result; + } + + private static TerminationCondition makeTerminationCondition(Config config) { + List<TerminationCondition> result = new ArrayList<TerminationCondition>(); + for (String next : config.getArrayValue(TERMINATION_KEY, null)) { + TerminationCondition t = instantiate(next, TerminationCondition.class); + if (t != null) { + result.add(t); + } + } + return new OrTerminationCondition(result); + } + + private static ScoreFunction makeScoreFunction(Config config) { + String className = config.getValue(Constants.SCORE_FUNCTION_KEY); + ScoreFunction result = instantiate(className, ScoreFunction.class); + return new ScoreTable(result); + } + + private static CommandCollector makeCommandCollector(Config config) { + List<String> classNames = config.getArrayValue(COMMAND_COLLECTOR_KEY); + CompositeCommandCollector result = new CompositeCommandCollector(); + for (String next : classNames) { + CommandCollector c = instantiate(next, CommandCollector.class); + if (c != null) { + result.addCommandCollector(c); + } + } + return result; + } + + private static List<GUIComponent> makeGUIComponents(Config config, ComponentManager componentManager, + Object... objectsToTest) { + List<GUIComponent> result = new ArrayList<GUIComponent>(); + result.add(componentManager); + List<String> classNames = config.getArrayValue(GUI_COMPONENTS_KEY, null); + for (String next : classNames) { + Logger.debug("GUI component found: '" + next + "'"); + GUIComponent c = instantiate(next, GUIComponent.class); + if (c != null) { + result.add(c); + } + } + for (Object next : objectsToTest) { + if (next instanceof GUIComponent) { + result.add((GUIComponent) next); + } + } + return result; + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.addConfigUpdater(LoadableType.AGENT, config, KernelConstants.AGENTS_KEY); + processor.addConfigUpdater(LoadableType.SIMULATOR, config, KernelConstants.SIMULATORS_KEY); + processor.addConfigUpdater(LoadableType.VIEWER, config, KernelConstants.VIEWERS_KEY); + processor.addConfigUpdater(LoadableType.COMPONENT, config, KernelConstants.COMPONENTS_KEY); + processor.addConfigUpdater(GIS_LOADABLE_TYPE, config, KernelConstants.GIS_KEY); + processor.addConfigUpdater(PERCEPTION_LOADABLE_TYPE, config, KernelConstants.PERCEPTION_KEY); + processor.addConfigUpdater(COMMUNICATION_LOADABLE_TYPE, config, KernelConstants.COMMUNICATION_MODEL_KEY); + Logger.info("Looking for gis, perception, communication, agent, simulator and viewer implementations"); + processor.process(); + } + + private static class ComponentStarter implements Callable<Void> { + private String className; + private ComponentManager componentManager; + private int count; + private Registry registry; + private KernelGUI gui; + private Config config; + + public ComponentStarter(String className, ComponentManager componentManager, int count, Registry registry, + KernelGUI gui, Config config) { + this.className = className; + this.componentManager = componentManager; + this.count = count; + this.registry = registry; + this.gui = gui; + this.config = config; + Logger.debug("New ComponentStarter: " + className + " * " + count); + } + + public Void call() throws InterruptedException { + Logger.debug("ComponentStarter running: " + className + " * " + count); + ComponentLauncher launcher = new InlineComponentLauncher(componentManager, config); + launcher.setDefaultRegistry(registry); + Logger.info("Launching " + count + " instances of component '" + className + "'..."); + for (int i = 0; i < count; ++i) { + Component c = instantiate(className, Component.class); + if (c == null) { + break; + } + Logger.info("Launching " + className + " instance " + (i + 1) + "..."); + try { + c.initialise(); + launcher.connect(c); + if (gui != null && c instanceof GUIComponent) { + gui.addGUIComponent((GUIComponent) c); + } + Logger.info(className + "instance " + (i + 1) + " launched successfully"); + } catch (ComponentConnectionException e) { + Logger.info(className + "instance " + (i + 1) + " failed: " + e.getMessage()); + break; + } catch (ComponentInitialisationException e) { + Logger.info(className + "instance " + (i + 1) + " failed", e); + } catch (ConnectionException e) { + Logger.info(className + "instance " + (i + 1) + " failed", e); + } + } + return null; + } + } + + private static class KernelInfo { + Kernel kernel; + KernelStartupOptions options; + ComponentManager componentManager; + List<GUIComponent> guiComponents; + + public KernelInfo(Kernel kernel, KernelStartupOptions options, ComponentManager componentManager, + List<GUIComponent> otherComponents) { + this.kernel = kernel; + this.options = options; + this.componentManager = componentManager; + guiComponents = new ArrayList<GUIComponent>(otherComponents); + } + } + + public class DummyFrame extends JFrame { + public DummyFrame(String title) { + super(title); + setUndecorated(true); + setVisible(true); + setLocationRelativeTo(null); + } + } +} \ No newline at end of file diff --git a/modules/kernel/src/kernel/TerminationCondition.java b/modules/kernel/src/kernel/TerminationCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..d90b3a536914efab1bb962dee2b8fa3bd4cd218f --- /dev/null +++ b/modules/kernel/src/kernel/TerminationCondition.java @@ -0,0 +1,21 @@ +package kernel; + +import rescuecore2.config.Config; + +/** + Termination conditions tell the kernel when to stop running a simulation. + */ +public interface TerminationCondition { + /** + Initialise this termination condition. + @param config The configuration. + */ + void initialise(Config config); + + /** + Return whether this termination condition has been met. + @param state The state of the kernel. + @return True if this termination condition has been met and the simulation should stop, false otherwise. + */ + boolean shouldStop(KernelState state); +} diff --git a/modules/kernel/src/kernel/TimedCommandCollector.java b/modules/kernel/src/kernel/TimedCommandCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..e347973e35ecd0918ca8128e9f2d22ca6746f9bd --- /dev/null +++ b/modules/kernel/src/kernel/TimedCommandCollector.java @@ -0,0 +1,48 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +import java.util.Collection; +import java.util.ArrayList; + +/** + A CommandCollector that waits for a certain amount of time before returning agent commands. +*/ +public class TimedCommandCollector implements CommandCollector { + private static final int DEFAULT_TIME = 1000; + private static final String TIME_KEY = "kernel.agents.think-time"; + + private long time; + + @Override + public void initialise(Config config) { + time = config.getIntValue(TIME_KEY, DEFAULT_TIME); + } + + @Override + public Collection<Command> getAgentCommands(Collection<AgentProxy> agents, int timestep) throws InterruptedException { + long now = System.currentTimeMillis(); + long end = now + time; + while (now < end) { + long diff = end - now; + Logger.trace(this + " waiting for " + diff + "ms"); + Thread.sleep(diff); + now = System.currentTimeMillis(); + } + Collection<Command> result = new ArrayList<Command>(); + for (AgentProxy next : agents) { + Collection<Command> commands = next.getAgentCommands(timestep); + result.addAll(commands); + } + Logger.trace(this + " returning " + result.size() + " commands"); + Logger.trace(this + " returning " + result); + return result; + } + + @Override + public String toString() { + return "Timed command collector"; + } +} diff --git a/modules/kernel/src/kernel/TimestepTerminationCondition.java b/modules/kernel/src/kernel/TimestepTerminationCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..e6517d9768c028e657a9d2a5da34e318bea6987e --- /dev/null +++ b/modules/kernel/src/kernel/TimestepTerminationCondition.java @@ -0,0 +1,35 @@ +package kernel; + +import rescuecore2.config.Config; +import rescuecore2.log.Logger; + +/** + A TerminationCondition that terminates the simulation after a specified timestep. +*/ +public class TimestepTerminationCondition implements TerminationCondition { + /** + The config key describing the number of timesteps to run. + */ + private static final String TIMESTEPS_KEY = "kernel.timesteps"; + + private int time; + + @Override + public void initialise(Config config) { + time = config.getIntValue(TIMESTEPS_KEY); + } + + @Override + public boolean shouldStop(KernelState state) { + if (state.getTime() >= time) { + Logger.info("TimestepTerminationCondition fired: " + state.getTime() + " >= " + time); + return true; + } + return false; + } + + @Override + public String toString() { + return "Timestep >= " + time + ""; + } +} diff --git a/modules/kernel/src/kernel/ViewerProxy.java b/modules/kernel/src/kernel/ViewerProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..f3b036d8837f13bf47b485701d9460da7b359599 --- /dev/null +++ b/modules/kernel/src/kernel/ViewerProxy.java @@ -0,0 +1,41 @@ +package kernel; + +import rescuecore2.connection.Connection; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.Timestep; + +/** + This class is the kernel interface to a viewer. + */ +public class ViewerProxy extends AbstractKernelComponent { + private int id; + + /** + Construct a viewer. + @param name The name of the viewer. + @param id The ID of the viewer. + @param c The connection to the viewer. + */ + public ViewerProxy(String name, int id, Connection c) { + super(name, c); + this.id = id; + } + + /** + Send a Timestep structure to this viewer. + @param time The Timestep to send. + */ + public void sendTimestep(Timestep time) { + send(new KVTimestep(id, time.getTime(), time.getCommands(), time.getChangeSet())); + } + + @Override + public String toString() { + return getName() + " (" + id + "): " + getConnection().toString(); + } + + @Override + public int hashCode() { + return Integer.hashCode(id); + } +} diff --git a/modules/kernel/src/kernel/WorldModelCreator.java b/modules/kernel/src/kernel/WorldModelCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..9e526dec8272ae5d0f49df9202036171114a3870 --- /dev/null +++ b/modules/kernel/src/kernel/WorldModelCreator.java @@ -0,0 +1,35 @@ +package kernel; + + + +import org.dom4j.DocumentException; + +import rescuecore2.scenario.Scenario; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.config.Config; + +/** + * The interface for world model creators, e.g. GIS systems. + */ +public interface WorldModelCreator extends EntityIDGenerator { + /** + * Create a new WorldModel. + * + * @param config + * The config to use. + * @return A new world model. + * @throws KernelException + * If there is a problem building the world model. + */ + WorldModel<? extends Entity> buildWorldModel(Config config) + throws KernelException; + + /** + * Returns the scenario of the simulation + * + * @param config + * @return a scenario + */ + public Scenario getScenario(Config config) throws DocumentException; +} diff --git a/modules/kernel/src/kernel/WrongTimeCommandFilter.java b/modules/kernel/src/kernel/WrongTimeCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..18a6050f435a50ab77fd3d72536e9b8009ee298b --- /dev/null +++ b/modules/kernel/src/kernel/WrongTimeCommandFilter.java @@ -0,0 +1,18 @@ +package kernel; + +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +/** + A CommandFilter that ignores agent commands that have the wrong timestamp. + */ +public class WrongTimeCommandFilter extends AbstractCommandFilter { + @Override + protected boolean allowed(Command command, KernelState state) { + if (command.getTime() == state.getTime()) { + return true; + } + Logger.info("Ignoring command " + command + ": Wrong timestamp: " + command.getTime() + " should be " + state.getTime()); + return false; + } +} diff --git a/modules/kernel/src/kernel/ui/ComponentManagerGUI.java b/modules/kernel/src/kernel/ui/ComponentManagerGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..430e9c5f21ce0458824a048f5a5b23bb38c082af --- /dev/null +++ b/modules/kernel/src/kernel/ui/ComponentManagerGUI.java @@ -0,0 +1,105 @@ +package kernel.ui; + +import javax.swing.JPanel; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.BorderFactory; +import javax.swing.SwingUtilities; +import java.awt.GridLayout; + +import java.util.List; + +import rescuecore2.misc.gui.ListModelList; + +/** + A user interface component for viewing that state of the ComponentManager. + */ +public class ComponentManagerGUI extends JPanel { + private JList uncontrolledAgents; + private JList agentAck; + private JList simulatorAck; + private JList viewerAck; + private ListModelList<String> uncontrolledAgentsModel; + private ListModelList<String> agentAckModel; + private ListModelList<String> simulatorAckModel; + private ListModelList<String> viewerAckModel; + + /** + Construct a new ComponentManagerGUI. + */ + public ComponentManagerGUI() { + // CHECKSTYLE:OFF:MagicNumber + super(new GridLayout(4, 1)); + // CHECKSTYLE:ON:MagicNumber + uncontrolledAgentsModel = new ListModelList<String>(); + agentAckModel = new ListModelList<String>(); + simulatorAckModel = new ListModelList<String>(); + viewerAckModel = new ListModelList<String>(); + uncontrolledAgents = new JList(uncontrolledAgentsModel); + agentAck = new JList(agentAckModel); + simulatorAck = new JList(simulatorAckModel); + viewerAck = new JList(viewerAckModel); + add(uncontrolledAgents, "Agents with no controller"); + add(agentAck, "Agents that have not acknowledged"); + add(simulatorAck, "Simulators that have not acknowledged"); + add(viewerAck, "Viewers that have not acknowledged"); + } + + /** + Update the list of uncontrolled agents. + @param data A list of uncontrolled agent descriptions. This list will be displated verbatim. + */ + public void updateUncontrolledAgents(final List<String> data) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + uncontrolledAgentsModel.clear(); + uncontrolledAgentsModel.addAll(data); + } + }); + } + + /** + Update the list of agents that have not acknowledged the connection. + @param data A list of unacknowledged agent descriptions. This list will be displayed verbatim. + */ + public void updateAgentAck(final List<String> data) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + agentAckModel.clear(); + agentAckModel.addAll(data); + } + }); + } + + /** + Update the list of simulators that have not acknowledged the connection. + @param data A list of unacknowledged simulator descriptions. This list will be displayed verbatim. + */ + public void updateSimulatorAck(final List<String> data) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + simulatorAckModel.clear(); + simulatorAckModel.addAll(data); + } + }); + } + + /** + Update the list of viewers that have not acknowledged the connection. + @param data A list of unacknowledged viewer descriptions. This list will be displayed verbatim. + */ + public void updateViewerAck(final List<String> data) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + viewerAckModel.clear(); + viewerAckModel.addAll(data); + } + }); + } + + private void add(JList list, String title) { + JScrollPane scroll = new JScrollPane(list); + scroll.setBorder(BorderFactory.createTitledBorder(title)); + add(scroll); + } +} diff --git a/modules/kernel/src/kernel/ui/KernelControlPanel.java b/modules/kernel/src/kernel/ui/KernelControlPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..e3e5693e9fcdf38e2df2fc4622fcff5d3ae2ab06 --- /dev/null +++ b/modules/kernel/src/kernel/ui/KernelControlPanel.java @@ -0,0 +1,325 @@ +package kernel.ui; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.GridLayout; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JPanel; +import javax.swing.JButton; +import javax.swing.SwingUtilities; +import javax.swing.JOptionPane; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; + +import kernel.Kernel; +import kernel.KernelException; +import kernel.ComponentManager; +import kernel.InlineComponentLauncher; + +import rescuecore2.misc.WorkerThread; +import rescuecore2.config.Config; +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.connection.ConnectionException; +import rescuecore2.components.Component; +import rescuecore2.components.ComponentLauncher; +import rescuecore2.components.ComponentInitialisationException; +import rescuecore2.components.ComponentConnectionException; +import rescuecore2.log.LogException; +import rescuecore2.log.Logger; +import rescuecore2.registry.Registry; + +/** + A JComponent containing various controls for the kernel GUI. + */ +public class KernelControlPanel extends JPanel { + private Kernel kernel; + private Config config; + private Registry registry; + private ComponentManager componentManager; + private ComponentLauncher launcher; + private Collection<JButton> controlButtons; + private JButton stepButton; + private JButton runButton; + + private volatile boolean running; + private volatile boolean step; + private RunThread runThread; + + private final Object runLock = new Object(); + + /** + Create a KernelControlPanel component. + @param kernel The kernel to control. + @param config The kernel configuration. + @param componentManager The kernel component manager. + @param registry The registry to use for new connections. + */ + public KernelControlPanel(Kernel kernel, Config config, ComponentManager componentManager, Registry registry) { + super(new GridLayout(0, 1)); + this.kernel = kernel; + this.config = config; + this.componentManager = componentManager; + this.registry = registry; + controlButtons = new ArrayList<JButton>(); + JButton addAgent = new JButton("Add agent"); + JButton removeAgent = new JButton("Remove agent"); + JButton addSim = new JButton("Add simulator"); + JButton removeSim = new JButton("Remove simulator"); + JButton addViewer = new JButton("Add viewer"); + JButton removeViewer = new JButton("Remove viewer"); + stepButton = new JButton("Step"); + runButton = new JButton("Run"); + add(addAgent); + add(removeAgent); + add(addSim); + add(removeSim); + add(addViewer); + add(removeViewer); + add(stepButton); + add(runButton); + controlButtons.add(addAgent); + controlButtons.add(removeAgent); + controlButtons.add(addSim); + controlButtons.add(removeSim); + controlButtons.add(addViewer); + controlButtons.add(removeViewer); + addAgent.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + addAgent(); + } + }); + removeAgent.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + removeAgent(); + } + }); + addSim.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + addSim(); + } + }); + removeSim.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + removeSim(); + } + }); + addViewer.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + addViewer(); + } + }); + removeViewer.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + removeViewer(); + } + }); + stepButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + stepButtonPressed(); + } + }); + runButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + runButtonPressed(); + } + }); + runThread = new RunThread(); + running = false; + } + + /** + Activate this control panel. + */ + public void activate() { + runThread.start(); + launcher = new InlineComponentLauncher(componentManager, config); + launcher.setDefaultRegistry(Registry.getCurrentRegistry()); + } + + private void addAgent() { + Component[] as = createComponents("agents"); + addComponent(as, "agent"); + } + + private void removeAgent() { + } + + private void addSim() { + Component[] ss = createComponents("simulators"); + addComponent(ss, "simulator"); + } + + private void removeSim() { + } + + private void addViewer() { + Component[] vs = createComponents("viewers"); + addComponent(vs, "viewer"); + } + + private void removeViewer() { + } + + private void addComponent(Component[] options, String type) { + if (options.length == 0) { + return; + } + Component c = (Component)JOptionPane.showInputDialog(this, "Choose a " + type, "Choose a " + type, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); + if (c == null) { + return; + } + try { + c.initialise(); + launcher.connect(c); + } + catch (NoSuchConfigOptionException e) { + JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); + } + catch (ComponentInitialisationException e) { + JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); + } + catch (ComponentConnectionException e) { + JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e.getMessage()); + } + catch (ConnectionException e) { + JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); + } + catch (InterruptedException e) { + JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e); + } + } + + private void stepButtonPressed() { + // This method should only be called on the event dispatch thread so it's OK to update the GUI. + // Do a sanity check just in case. + if (!SwingUtilities.isEventDispatchThread()) { + throw new RuntimeException("stepButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread."); + } + synchronized (runLock) { + if (!running) { + step = true; + stepButton.setText("Working"); + stepButton.setEnabled(false); + runButton.setEnabled(false); + setControlButtonsEnabled(false); + runLock.notifyAll(); + } + } + } + + private void endStep() { + step = false; + stepButton.setText("Step"); + stepButton.setEnabled(true); + runButton.setEnabled(true); + setControlButtonsEnabled(true); + } + + private void runButtonPressed() { + // This method should only be called on the event dispatch thread so it's OK to update the GUI. + // Do a sanity check just in case. + if (!SwingUtilities.isEventDispatchThread()) { + throw new RuntimeException("runButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread."); + } + synchronized (runLock) { + if (running) { + setControlButtonsEnabled(true); + stepButton.setEnabled(true); + runButton.setText("Run"); + } + else { + setControlButtonsEnabled(false); + stepButton.setEnabled(false); + runButton.setText("Stop"); + } + running = !running; + runLock.notifyAll(); + } + } + + private void setControlButtonsEnabled(boolean b) { + for (JButton next : controlButtons) { + next.setEnabled(b); + } + } + + private void disableAllButtons() throws InterruptedException { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + setControlButtonsEnabled(false); + stepButton.setEnabled(false); + runButton.setEnabled(false); + } + }); + } + catch (java.lang.reflect.InvocationTargetException e) { + // Should never happen + Logger.error("KernelControlPanel.disableAllButtons", e); + } + } + + private boolean shouldStep() { + synchronized (runLock) { + return running || step; + } + } + + private Component[] createComponents(String type) { + List<String> classNames = config.getArrayValue("kernel." + type, null); + List<Component> instances = new ArrayList<Component>(); + for (String next : classNames) { + Component c = instantiate(next, Component.class); + if (c != null) { + instances.add(c); + } + } + return instances.toArray(new Component[0]); + } + + private class RunThread extends WorkerThread { + @Override + public boolean work() throws InterruptedException { + if (shouldStep()) { + if (!kernel.hasTerminated()) { + try { + kernel.timestep(); + } + catch (KernelException e) { + Logger.error("Kernel error", e); + kernel.shutdown(); + disableAllButtons(); + return false; + } + catch (LogException e) { + Logger.error("Log error", e); + kernel.shutdown(); + disableAllButtons(); + return false; + } + synchronized (runLock) { + if (step) { + endStep(); + } + } + return true; + } + else { + kernel.shutdown(); + disableAllButtons(); + return false; + } + } + else { + synchronized (runLock) { + runLock.wait(1000); + } + return true; + } + } + } +} diff --git a/modules/kernel/src/kernel/ui/KernelGUI.java b/modules/kernel/src/kernel/ui/KernelGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..ebcb16022ba0f25e7ccef1842d623363f83289bf --- /dev/null +++ b/modules/kernel/src/kernel/ui/KernelGUI.java @@ -0,0 +1,68 @@ +package kernel.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +import kernel.Kernel; +import kernel.ComponentManager; + +import rescuecore2.config.Config; +import rescuecore2.registry.Registry; +import rescuecore2.GUIComponent; + +/** + A GUI for the kernel. + */ +public class KernelGUI extends JPanel { + private static final int STATUS_SIZE = 300; + + private Kernel kernel; + private KernelStatus status; + private KernelControlPanel control; + private JTabbedPane tabs; + private Config config; + + /** + Construct a KernelGUI component. + @param kernel The kernel to watch. + @param componentManager The kernel component manager. + @param config The kernel configuration. + @param registry The registry to use for new connections. + @param controls Whether to show the control panel or not. + */ + public KernelGUI(Kernel kernel, ComponentManager componentManager, Config config, Registry registry, boolean controls) { + super(new BorderLayout()); + this.kernel = kernel; + this.config = config; + status = new KernelStatus(kernel); + status.setPreferredSize(new Dimension(STATUS_SIZE, STATUS_SIZE)); + add(status, BorderLayout.EAST); + tabs = new JTabbedPane(); + add(tabs, BorderLayout.CENTER); + if (controls) { + control = new KernelControlPanel(kernel, config, componentManager, registry); + add(control, BorderLayout.WEST); + control.activate(); + } + addGUIComponent(componentManager); + + } + + /** + Add a kernel GUI component. + @param c The GUI component to add. + */ + public void addGUIComponent(final GUIComponent c) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (c.getGUIComponent() != null) { + tabs.addTab(c.getGUIComponentName(), c.getGUIComponent()); + } + } + }); + } +} diff --git a/modules/kernel/src/kernel/ui/KernelStartupPanel.java b/modules/kernel/src/kernel/ui/KernelStartupPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..912ebf2ae8e43a6339e4708c3127987a9e1b4d3f --- /dev/null +++ b/modules/kernel/src/kernel/ui/KernelStartupPanel.java @@ -0,0 +1,241 @@ +package kernel.ui; + +import javax.swing.JPanel; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.JScrollPane; +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JSplitPane; + +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeEvent; + +import java.awt.BorderLayout; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import java.util.Collection; +import java.util.List; + +import kernel.WorldModelCreator; +import kernel.Perception; +import kernel.CommunicationModel; +import kernel.KernelStartupOptions; + +import rescuecore2.config.Config; +import rescuecore2.components.Component; +import rescuecore2.misc.gui.ConfigTree; + +/** + A JPanel for displaying and editing kernel startup options. +*/ +public class KernelStartupPanel extends JPanel { + private static final String AUTO_SUFFIX = ".auto"; + + /** + Create a kernel launch GUI. + @param config The system configuration. + @param options The kernel startup options. + */ + public KernelStartupPanel(Config config, final KernelStartupOptions options) { + super(new BorderLayout()); + final JComboBox gis = createComboBox(options.getAvailableWorldModelCreators(), options.getWorldModelCreator()); + final JComboBox perception = createComboBox(options.getAvailablePerceptions(), options.getPerception()); + final JComboBox comms = createComboBox(options.getAvailableCommunicationModels(), options.getCommunicationModel()); + CheckboxPanel simulators = new CheckboxPanel(options.getAvailableSimulators(), options); + CheckboxPanel viewers = new CheckboxPanel(options.getAvailableViewers(), options); + SpinnerPanel agents = new SpinnerPanel(options.getAvailableAgents(), options); + CheckboxPanel otherComponents = new CheckboxPanel(options.getAvailableComponents(), options); + JScrollPane simulatorsScroll = new JScrollPane(simulators); + simulatorsScroll.setBorder(BorderFactory.createTitledBorder("Simulators")); + JScrollPane viewersScroll = new JScrollPane(viewers); + viewersScroll.setBorder(BorderFactory.createTitledBorder("Viewers")); + JScrollPane agentsScroll = new JScrollPane(agents); + agentsScroll.setBorder(BorderFactory.createTitledBorder("Agents")); + JScrollPane componentsScroll = new JScrollPane(otherComponents); + componentsScroll.setBorder(BorderFactory.createTitledBorder("Other components")); + ConfigTree configTree = new ConfigTree(config); + JScrollPane configTreeScroll = new JScrollPane(configTree); + configTreeScroll.setBorder(BorderFactory.createTitledBorder("Config")); + + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + JPanel optionsPanel = new JPanel(layout); + JSplitPane top = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, optionsPanel, configTreeScroll); + add(top, BorderLayout.CENTER); + + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.CENTER; + JLabel l = new JLabel("GIS:"); + layout.setConstraints(l, c); + optionsPanel.add(l); + c.gridy = 1; + l = new JLabel("Perception:"); + layout.setConstraints(l, c); + optionsPanel.add(l); + c.gridy = 2; + l = new JLabel("Communication model:"); + layout.setConstraints(l, c); + optionsPanel.add(l); + c.gridy = 0; + c.gridx = 1; + c.weightx = 1; + layout.setConstraints(gis, c); + optionsPanel.add(gis); + c.gridy = 1; + layout.setConstraints(perception, c); + optionsPanel.add(perception); + c.gridy = 2; + layout.setConstraints(comms, c); + optionsPanel.add(comms); + + // Simulators, viewers, agents, other components + c.gridx = 0; + ++c.gridy; + c.gridwidth = 2; + c.weightx = 1; + c.weighty = 1; + layout.setConstraints(simulatorsScroll, c); + optionsPanel.add(simulatorsScroll); + ++c.gridy; + layout.setConstraints(viewersScroll, c); + optionsPanel.add(viewersScroll); + ++c.gridy; + layout.setConstraints(agentsScroll, c); + optionsPanel.add(agentsScroll); + ++c.gridy; + layout.setConstraints(componentsScroll, c); + optionsPanel.add(componentsScroll); + + // Event listeners + gis.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + options.setWorldModelCreator((WorldModelCreator)gis.getSelectedItem()); + } + }); + perception.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + options.setPerception((Perception)perception.getSelectedItem()); + } + }); + comms.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + options.setCommunicationModel((CommunicationModel)comms.getSelectedItem()); + } + }); + } + + private <T> JComboBox createComboBox(List<T> options, T selected) { + Object[] choices = options.toArray(); + JComboBox result = new JComboBox(choices); + result.setSelectedItem(selected); + result.setEnabled(choices.length > 1); + return result; + } + + private static final class CheckboxPanel extends JPanel { + private CheckboxPanel(Collection<? extends Component> available, final KernelStartupOptions options) { + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.weightx = 1; + c.weighty = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.CENTER; + setLayout(layout); + for (Component t : available) { + c.gridx = 0; + c.weightx = 1; + JLabel l = new JLabel(t.getName()); + layout.setConstraints(l, c); + add(l); + c.gridx = 1; + c.weightx = 0; + final JCheckBox check = new JCheckBox(); + check.setSelected(options.getInstanceCount(t) > 0); + options.setInstanceCount(t, check.isSelected() ? 1 : 0); + layout.setConstraints(check, c); + add(check); + ++c.gridy; + final Component comp = t; + check.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + options.setInstanceCount(comp, check.isSelected() ? 1 : 0); + } + }); + } + } + } + + private static final class SpinnerPanel extends JPanel { + private SpinnerPanel(Collection<? extends Component> available, final KernelStartupOptions options) { + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.weightx = 1; + c.weighty = 1; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.CENTER; + setLayout(layout); + for (Component t : available) { + c.gridx = 0; + c.weightx = 1; + JLabel l = new JLabel(t.getName()); + layout.setConstraints(l, c); + add(l); + int count = options.getInstanceCount(t); + boolean all = count == Integer.MAX_VALUE; + final JSpinner spinner = new JSpinner(new SpinnerNumberModel(count == Integer.MAX_VALUE ? 0 : count, 0, Integer.MAX_VALUE, 1)); + final JCheckBox check = new JCheckBox("Maximum"); + check.setSelected(all); + spinner.setEnabled(!all); + final Component comp = t; + check.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + spinner.setEnabled(!check.isSelected()); + if (check.isSelected()) { + options.setInstanceCount(comp, Integer.MAX_VALUE); + } + else { + options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue()); + } + } + }); + spinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue()); + } + }); + c.gridx = 1; + c.weightx = 0; + layout.setConstraints(spinner, c); + add(spinner); + c.gridx = 2; + layout.setConstraints(check, c); + add(check); + ++c.gridy; + } + } + } +} diff --git a/modules/kernel/src/kernel/ui/KernelStatus.java b/modules/kernel/src/kernel/ui/KernelStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..1205f53783223570f94186e40d4e86be6a4ee564 --- /dev/null +++ b/modules/kernel/src/kernel/ui/KernelStatus.java @@ -0,0 +1,128 @@ +package kernel.ui; + +import java.util.ArrayList; +import java.awt.BorderLayout; +import java.awt.GridLayout; +import javax.swing.JPanel; +import javax.swing.JList; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.BorderFactory; +import javax.swing.SwingUtilities; + +import kernel.KernelListener; +import kernel.AgentProxy; +import kernel.SimulatorProxy; +import kernel.ViewerProxy; +import kernel.Kernel; + +import rescuecore2.misc.gui.ListModelList; +import rescuecore2.Timestep; + +/** + A status panel for the kernel. + */ +public class KernelStatus extends JPanel implements KernelListener { + private Kernel kernel; + + private ListModelList<AgentProxy> agents; + private ListModelList<SimulatorProxy> simulators; + private ListModelList<ViewerProxy> viewers; + + private JList agentsList; + private JList simulatorsList; + private JList viewersList; + private JLabel timeLabel; + private JLabel scoreLabel; + + /** + Construct a KernelStatus component. + @param kernel The Kernel to watch. + */ + public KernelStatus(Kernel kernel) { + super(new BorderLayout()); + this.kernel = kernel; + agents = new ListModelList<AgentProxy>(new ArrayList<AgentProxy>()); + simulators = new ListModelList<SimulatorProxy>(new ArrayList<SimulatorProxy>()); + viewers = new ListModelList<ViewerProxy>(new ArrayList<ViewerProxy>()); + kernel.addKernelListener(this); + agentsList = new JList(agents); + simulatorsList = new JList(simulators); + viewersList = new JList(viewers); + // CHECKSTYLE:OFF:MagicNumber + JPanel lists = new JPanel(new GridLayout(3, 1)); + // CHECKSTYLE:ON:MagicNumber + JScrollPane agentsScroll = new JScrollPane(agentsList); + JScrollPane simulatorsScroll = new JScrollPane(simulatorsList); + JScrollPane viewersScroll = new JScrollPane(viewersList); + agentsScroll.setBorder(BorderFactory.createTitledBorder("Agents")); + simulatorsScroll.setBorder(BorderFactory.createTitledBorder("Simulators")); + viewersScroll.setBorder(BorderFactory.createTitledBorder("Viewers")); + lists.add(agentsScroll); + lists.add(simulatorsScroll); + lists.add(viewersScroll); + add(lists, BorderLayout.CENTER); + timeLabel = new JLabel("Time: not started", JLabel.CENTER); + scoreLabel = new JLabel("Score: not started", JLabel.CENTER); + JPanel labels = new JPanel(new GridLayout(1, 2)); + labels.add(timeLabel); + labels.add(scoreLabel); + add(labels, BorderLayout.NORTH); + agents.addAll(kernel.getAllAgents()); + simulators.addAll(kernel.getAllSimulators()); + viewers.addAll(kernel.getAllViewers()); + } + + @Override + public void simulationStarted(Kernel k) { + } + + @Override + public void simulationEnded(Kernel k) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + timeLabel.setText("Time: ended"); + } + }); + } + + @Override + public void timestepCompleted(Kernel k, final Timestep time) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + timeLabel.setText("Time: " + time.getTime()); + scoreLabel.setText("Score: " + time.getScore()); + } + }); + } + + @Override + public void agentAdded(Kernel k, AgentProxy info) { + agents.add(info); + } + + @Override + public void agentRemoved(Kernel k, AgentProxy info) { + agents.remove(info); + } + + @Override + public void simulatorAdded(Kernel k, SimulatorProxy info) { + simulators.add(info); + } + + @Override + public void simulatorRemoved(Kernel k, SimulatorProxy info) { + simulators.remove(info); + } + + @Override + public void viewerAdded(Kernel k, ViewerProxy info) { + viewers.add(info); + } + + @Override + public void viewerRemoved(Kernel k, ViewerProxy info) { + viewers.remove(info); + } +} diff --git a/modules/kernel/src/kernel/ui/ScoreGraph.java b/modules/kernel/src/kernel/ui/ScoreGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..e017c34ad72093ca2c8de7451cf9a3ab9abe0ecc --- /dev/null +++ b/modules/kernel/src/kernel/ui/ScoreGraph.java @@ -0,0 +1,129 @@ +package kernel.ui; + +import rescuecore2.score.ScoreFunction; +import rescuecore2.score.CompositeScoreFunction; +import rescuecore2.score.DelegatingScoreFunction; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.config.Config; +import rescuecore2.Timestep; +import rescuecore2.GUIComponent; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.Box; +import javax.swing.JCheckBox; +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeEvent; + +import java.awt.BorderLayout; + +import java.util.Set; +import java.util.List; +import java.util.ArrayList; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.data.xy.XYSeries; + +/** + A ScoreFunction that also provides a components for graphing the components of the score. + */ +public class ScoreGraph extends DelegatingScoreFunction implements GUIComponent { + private JFreeChart chart; + private List<SeriesInfo> allSeries; + + /** + Construct a ScoreGraph that wraps a child score function. + @param child The child score function. + */ + public ScoreGraph(ScoreFunction child) { + super("Score graph", child); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + super.initialise(world, config); + allSeries = new ArrayList<SeriesInfo>(); + XYSeriesCollection data = new XYSeriesCollection(); + createSeries(child, data); + PlotOrientation orientation = PlotOrientation.VERTICAL; + chart = ChartFactory.createXYLineChart("Score", "Time", "Score", data, orientation, true, false, false); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + update(world, timestep); + return super.score(world, timestep); + } + + @Override + public JComponent getGUIComponent() { + JComponent selectionPanel = Box.createVerticalBox(); + final XYItemRenderer renderer = ((XYPlot)chart.getPlot()).getRenderer(); + for (SeriesInfo next : allSeries) { + final ScoreFunction f = next.function; + final int index = next.index; + final JCheckBox checkBox = new JCheckBox(f.getName(), true); + selectionPanel.add(checkBox); + checkBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + boolean selected = checkBox.isSelected(); + renderer.setSeriesVisible(index, selected); + } + }); + } + JPanel result = new JPanel(); + result.add(new ChartPanel(chart), BorderLayout.CENTER); + result.add(selectionPanel, BorderLayout.EAST); + return result; + } + + @Override + public String getGUIComponentName() { + return "Score chart"; + } + + private void createSeries(ScoreFunction root, XYSeriesCollection data) { + if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) { + XYSeries next = new XYSeries(root.getName()); + allSeries.add(new SeriesInfo(root, next, allSeries.size())); + data.addSeries(next); + } + if (root instanceof DelegatingScoreFunction) { + createSeries(((DelegatingScoreFunction)root).getChildFunction(), data); + } + if (root instanceof CompositeScoreFunction) { + Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions(); + for (ScoreFunction f : children) { + createSeries(f, data); + } + } + } + + private void update(WorldModel<? extends Entity> world, Timestep timestep) { + for (SeriesInfo next : allSeries) { + ScoreFunction f = next.function; + XYSeries data = next.series; + double d = f.score(world, timestep); + data.add(timestep.getTime(), d); + } + } + + private static class SeriesInfo { + ScoreFunction function; + XYSeries series; + int index; + + public SeriesInfo(ScoreFunction function, XYSeries series, int index) { + this.function = function; + this.series = series; + this.index = index; + } + } +} diff --git a/modules/kernel/src/kernel/ui/ScoreTable.java b/modules/kernel/src/kernel/ui/ScoreTable.java new file mode 100644 index 0000000000000000000000000000000000000000..3fd7de65db7fd8e08f7b972b4cf5fba3691e4391 --- /dev/null +++ b/modules/kernel/src/kernel/ui/ScoreTable.java @@ -0,0 +1,190 @@ +package kernel.ui; + +import rescuecore2.score.ScoreFunction; +import rescuecore2.score.CompositeScoreFunction; +import rescuecore2.score.DelegatingScoreFunction; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.config.Config; +import rescuecore2.Timestep; +import rescuecore2.GUIComponent; + +import javax.swing.JComponent; +import javax.swing.JTable; +import javax.swing.JList; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.AbstractListModel; +import javax.swing.UIManager; +import javax.swing.ListCellRenderer; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; + +/** + A ScoreFunction that also provides a JTable for viewing the components of the score. + */ +public class ScoreTable extends DelegatingScoreFunction implements GUIComponent { + private ScoreModel model; + + /** + Construct a ScoreTable that wraps a child score function. + @param child The child score function. + */ + public ScoreTable(ScoreFunction child) { + super("Score Table", child); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + super.initialise(world, config); + model = new ScoreModel(child); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + model.update(world, timestep); + return super.score(world, timestep); + } + + @Override + public JComponent getGUIComponent() { + JTable table = new JTable(model.table); + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + JScrollPane scroll = new JScrollPane(table); + JList rowHeader = new JList(model.list); + rowHeader.setFixedCellHeight(table.getRowHeight()); + rowHeader.setCellRenderer(new RowHeaderRenderer(table)); + rowHeader.setBackground(table.getBackground()); + rowHeader.setOpaque(true); + scroll.setRowHeaderView(rowHeader); + return scroll; + } + + @Override + public String getGUIComponentName() { + return "Score"; + } + + private static class RowHeaderRenderer extends JLabel implements ListCellRenderer { + RowHeaderRenderer(JTable table) { + JTableHeader header = table.getTableHeader(); + setOpaque(true); + setBorder(UIManager.getBorder("TableHeader.cellBorder")); + setHorizontalAlignment(LEFT); + setForeground(header.getForeground()); + setBackground(header.getBackground()); + setFont(header.getFont()); + } + + @Override + public JLabel getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + setText((value == null) ? "" : value.toString()); + return this; + } + } + + private static class ScoreModel { + ScoreTableModel table; + ScoreListModel list; + private int steps; + private List<ScoreFunctionEntry> entries; + + ScoreModel(ScoreFunction root) { + steps = 0; + entries = new ArrayList<ScoreFunctionEntry>(); + populateEntries(root, ""); + table = new ScoreTableModel(); + list = new ScoreListModel(); + } + + private void populateEntries(ScoreFunction root, String prefix) { + String suffix = ""; + if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) { + entries.add(new ScoreFunctionEntry(root, prefix)); + suffix = "--"; + } + if (root instanceof DelegatingScoreFunction) { + populateEntries(((DelegatingScoreFunction)root).getChildFunction(), prefix + suffix); + } + if (root instanceof CompositeScoreFunction) { + Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions(); + for (ScoreFunction next : children) { + populateEntries(next, prefix + suffix); + } + } + } + + void update(WorldModel<? extends Entity> world, Timestep timestep) { + for (ScoreFunctionEntry next : entries) { + next.update(world, timestep); + } + steps = timestep.getTime(); + table.fireTableStructureChanged(); + } + + private class ScoreTableModel extends AbstractTableModel { + @Override + public String getColumnName(int col) { + return String.valueOf(col + 1); + } + + @Override + public int getRowCount() { + return entries.size(); + } + + @Override + public int getColumnCount() { + return steps; + } + + @Override + public Object getValueAt(int row, int column) { + return entries.get(row).getScore(column + 1); + } + } + + private class ScoreListModel extends AbstractListModel { + @Override + public int getSize() { + return entries.size(); + } + + @Override + public Object getElementAt(int row) { + return entries.get(row).getScoreFunctionName(); + } + } + + private static class ScoreFunctionEntry { + private ScoreFunction function; + private String prefix; + private Map<Integer, Double> scores; + + public ScoreFunctionEntry(ScoreFunction f, String prefix) { + function = f; + this.prefix = prefix; + scores = new HashMap<Integer, Double>(); + } + + public String getScoreFunctionName() { + return prefix + function.getName(); + } + + public double getScore(int step) { + return scores.containsKey(step) ? scores.get(step) : Double.NaN; + } + + void update(WorldModel<? extends Entity> world, Timestep timestep) { + double d = function.score(world, timestep); + scores.put(timestep.getTime(), d); + } + } + } +} diff --git a/modules/maps/src/maps/ConstantConversion.java b/modules/maps/src/maps/ConstantConversion.java new file mode 100644 index 0000000000000000000000000000000000000000..5c14909e91060d53809caa64d95aa9508c02797f --- /dev/null +++ b/modules/maps/src/maps/ConstantConversion.java @@ -0,0 +1,26 @@ +package maps; + +/** + A coordinate conversion that multiplies by a number. +*/ +public class ConstantConversion implements CoordinateConversion { + private double constant; + + /** + Construct a ConstantConversion. + @param c The constant. + */ + public ConstantConversion(double c) { + constant = c; + } + + @Override + public double convertX(double x) { + return x * constant; + } + + @Override + public double convertY(double y) { + return y * constant; + } +} diff --git a/modules/maps/src/maps/CoordinateConversion.java b/modules/maps/src/maps/CoordinateConversion.java new file mode 100644 index 0000000000000000000000000000000000000000..4f8ec565cb6af85a8a93578a1d97323e54d583b6 --- /dev/null +++ b/modules/maps/src/maps/CoordinateConversion.java @@ -0,0 +1,20 @@ +package maps; + +/** + Interface for converting coordinates from one system to another. +*/ +public interface CoordinateConversion { + /** + Convert an X coordinate. + @param x The coordinate to convert. + @return The converted coordinate. + */ + double convertX(double x); + + /** + Convert a Y coordinate. + @param y The coordinate to convert. + @return The converted coordinate. + */ + double convertY(double y); +} diff --git a/modules/maps/src/maps/IdentityConversion.java b/modules/maps/src/maps/IdentityConversion.java new file mode 100644 index 0000000000000000000000000000000000000000..3c61a00cfdd2752c15b934f19251b778f7ca007b --- /dev/null +++ b/modules/maps/src/maps/IdentityConversion.java @@ -0,0 +1,16 @@ +package maps; + +/** + A no-op coordinate conversion. +*/ +public class IdentityConversion implements CoordinateConversion { + @Override + public double convertX(double x) { + return x; + } + + @Override + public double convertY(double y) { + return y; + } +} diff --git a/modules/maps/src/maps/Map.java b/modules/maps/src/maps/Map.java new file mode 100644 index 0000000000000000000000000000000000000000..f8457cc8c6c1ad6186adecc830fe2e8c255ae733 --- /dev/null +++ b/modules/maps/src/maps/Map.java @@ -0,0 +1,7 @@ +package maps; + +/** + Top-level interface for all types of map. +*/ +public interface Map { +} \ No newline at end of file diff --git a/modules/maps/src/maps/MapException.java b/modules/maps/src/maps/MapException.java new file mode 100644 index 0000000000000000000000000000000000000000..6ba6312f7fa804cb4c5c250829b599917dc7f23c --- /dev/null +++ b/modules/maps/src/maps/MapException.java @@ -0,0 +1,38 @@ +package maps; + +/** + Exceptions related to maps. +*/ +public class MapException extends Exception { + /** + Construct a MapException with no error message. + */ + public MapException() { + super(); + } + + /** + Construct a MapException with an error message. + @param msg The error message. + */ + public MapException(String msg) { + super(msg); + } + + /** + Construct a MapException with an underlying cause. + @param cause The cause. + */ + public MapException(Throwable cause) { + super(cause); + } + + /** + Construct a MapException with an error message and underlying cause. + @param msg The error message. + @param cause The cause. + */ + public MapException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/maps/src/maps/MapFormat.java b/modules/maps/src/maps/MapFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..ac9d4528cf87f8b9b49058c9d163f746ab39cb01 --- /dev/null +++ b/modules/maps/src/maps/MapFormat.java @@ -0,0 +1,32 @@ +package maps; + +import java.io.File; + +/** + Interface for different types of map format. +*/ +public interface MapFormat { + /** + Read a File and return a Map. + @param file The file to read. + @return A new Map. + @throws MapException If there is a problem reading the map. + */ + Map read(File file) throws MapException; + + /** + Write a map to a file. + @param map The map to write. + @param file The file to write to. + @throws MapException If there is a problem writing the map. + */ + void write(Map map, File file) throws MapException; + + /** + Find out if a file looks valid to this format. + @param file The file to check. + @return True if this format can probably read the file, false otherwise. + @throws MapException If there is a problem reading the file. + */ + boolean canRead(File file) throws MapException; +} diff --git a/modules/maps/src/maps/MapReader.java b/modules/maps/src/maps/MapReader.java new file mode 100644 index 0000000000000000000000000000000000000000..33d4edac200958addc4ddf91bfde04d4f92fba88 --- /dev/null +++ b/modules/maps/src/maps/MapReader.java @@ -0,0 +1,95 @@ +package maps; + +import maps.gml.formats.RobocupFormat; +import maps.gml.formats.OrdnanceSurveyFormat; +import maps.gml.formats.MeijoFormat; +import maps.gml.formats.GeospatialInformationAuthorityFormat; + +import java.io.File; + +import java.util.List; +import java.util.ArrayList; + +import rescuecore2.log.Logger; + +/** + A utility class for reading maps. +*/ +public final class MapReader { + private static final List<MapFormat> ALL_FORMATS = new ArrayList<MapFormat>(); + + static { + ALL_FORMATS.add(RobocupFormat.INSTANCE); + ALL_FORMATS.add(MeijoFormat.INSTANCE); + ALL_FORMATS.add(OrdnanceSurveyFormat.INSTANCE); + ALL_FORMATS.add(GeospatialInformationAuthorityFormat.INSTANCE); + } + + private MapReader() { + } + + /** + Read a Map from a file and guess the format. + @param file The name of the file to read. + @return A Map. + @throws MapException If there is a problem reading the map. + */ + public static Map readMap(String file) throws MapException { + return readMap(file, null); + } + + /** + Read a Map from a file using a particular format. + @param file The name of the file to read. + @param format The format to use. If this is null then the format will be guessed. + @return A Map. + @throws MapException If there is a problem reading the map. + */ + public static Map readMap(String file, MapFormat format) throws MapException { + return readMap(new File(file), format); + } + + /** + Read a Map from a file and guess the format. + @param file The file to read. + @return A Map. + @throws MapException If there is a problem reading the map. + */ + public static Map readMap(File file) throws MapException { + return readMap(file, null); + } + + /** + Read a Map from a file using a particular format. + @param file The file to read. + @param format The format to use. If this is null then the format will be guessed. + @return A Map. + @throws MapException If there is a problem reading the map. + */ + public static Map readMap(File file, MapFormat format) throws MapException { + if (format == null) { + format = guessFormat(file); + } + if (format == null) { + throw new MapException("Unrecognised format"); + } + Logger.debug("Reading " + format.toString() + " format"); + return format.read(file); + } + + /** + Guess the format for a Map. + @param file The file to guess the format of. + @return The most likely format or null if the file type is unrecognised. + @throws MapException If there is a problem reading the file. + */ + public static MapFormat guessFormat(File file) throws MapException { + Logger.debug("Guessing format"); + for (MapFormat next : ALL_FORMATS) { + if (next.canRead(file)) { + return next; + } + } + return null; + } +} diff --git a/modules/maps/src/maps/MapTools.java b/modules/maps/src/maps/MapTools.java new file mode 100644 index 0000000000000000000000000000000000000000..88618a0724812082baf29480c38c421faa658669 --- /dev/null +++ b/modules/maps/src/maps/MapTools.java @@ -0,0 +1,28 @@ +package maps; + +import javax.measure.unit.NonSI; +import javax.measure.unit.SI; +import org.jscience.geography.coordinates.UTM; +import org.jscience.geography.coordinates.LatLong; +import org.jscience.geography.coordinates.crs.ReferenceEllipsoid; + +/** + Utility class for dealing with maps. +*/ +public final class MapTools { + private MapTools() { + } + + /** + Compute the size of one metre in latitude/longitude relative to a reference point. + @param lat The latitude of the reference point. + @param lon The longitude of the reference point. + @return The size of one metre at the reference point. + */ + public static double sizeOf1Metre(double lat, double lon) { + UTM centre = UTM.latLongToUtm(LatLong.valueOf(lat, lon, NonSI.DEGREE_ANGLE), ReferenceEllipsoid.WGS84); + UTM offset = UTM.valueOf(centre.longitudeZone(), centre.latitudeZone(), centre.eastingValue(SI.METRE), centre.northingValue(SI.METRE) + 1, SI.METRE); + LatLong result = UTM.utmToLatLong(offset, ReferenceEllipsoid.WGS84); + return Math.abs(result.latitudeValue(NonSI.DEGREE_ANGLE) - lat); + } +} diff --git a/modules/maps/src/maps/MapWriter.java b/modules/maps/src/maps/MapWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..75b5956ea1f9acf4a72d0876252b69709971407e --- /dev/null +++ b/modules/maps/src/maps/MapWriter.java @@ -0,0 +1,33 @@ +package maps; + +import java.io.File; + +/** + A class for writing maps. +*/ +public final class MapWriter { + private MapWriter() { + } + + /** + Write a map to a file. + @param map The map to write. + @param file The name of the file to write to. + @param format The MapFormat to write. + @throws MapException If there is a problem writing the map. + */ + public static void writeMap(Map map, String file, MapFormat format) throws MapException { + writeMap(map, new File(file), format); + } + + /** + Write a Map to a file. + @param map The map to write. + @param file The file to write to. + @param format The MapFormat to write. + @throws MapException If there is a problem writing the map. + */ + public static void writeMap(Map map, File file, MapFormat format) throws MapException { + format.write(map, file); + } +} diff --git a/modules/maps/src/maps/ScaleConversion.java b/modules/maps/src/maps/ScaleConversion.java new file mode 100644 index 0000000000000000000000000000000000000000..7b1b2e68745817f7359e0a25914089736498c0db --- /dev/null +++ b/modules/maps/src/maps/ScaleConversion.java @@ -0,0 +1,35 @@ +package maps; + +/** + A coordinate conversion that scales and translates coordinates. +*/ +public class ScaleConversion implements CoordinateConversion { + private double xOrigin; + private double yOrigin; + private double xScale; + private double yScale; + + /** + Construct a ScaleConversion. + @param xOrigin The x coordinate of the new origin. + @param yOrigin The y coordinate of the new origin. + @param xScale The scale factor for x coordinates. + @param yScale The scale factor for y coordinates. + */ + public ScaleConversion(double xOrigin, double yOrigin, double xScale, double yScale) { + this.xOrigin = xOrigin; + this.yOrigin = yOrigin; + this.xScale = xScale; + this.yScale = yScale; + } + + @Override + public double convertX(double x) { + return (x - xOrigin) * xScale; + } + + @Override + public double convertY(double y) { + return (y - yOrigin) * yScale; + } +} diff --git a/modules/maps/src/maps/convert/Convert.java b/modules/maps/src/maps/convert/Convert.java new file mode 100644 index 0000000000000000000000000000000000000000..0840ba8b159919737c42e38e03239f80ca6b8104 --- /dev/null +++ b/modules/maps/src/maps/convert/Convert.java @@ -0,0 +1,90 @@ +package maps.convert; + +import maps.MapWriter; +import maps.osm.OSMMap; +import maps.osm.OSMMapViewer; +import maps.osm.OSMException; +import maps.gml.GMLMap; +import maps.gml.view.GMLMapViewer; + +import maps.convert.osm2gml.Convertor; +import maps.gml.formats.RobocupFormat; + +import org.dom4j.DocumentException; + +import java.awt.GridLayout; +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.BorderFactory; +import java.io.File; +import java.io.IOException; + +/** + This class converts maps from one format to another. +*/ +public final class Convert { + // Nodes that are close are deemed to be co-located. + private static final double NEARBY_NODE_THRESHOLD = 0.000001; + + private static final int PROGRESS_WIDTH = 200; + private static final int PROGRESS_HEIGHT = 10; + private static final int VIEWER_SIZE = 500; + private static final int STATUS_WIDTH = 500; + private static final int STATUS_HEIGHT = 10; + private static final int MARGIN = 4; + + // private ShapeDebugFrame debug; + // private List<ShapeDebugFrame.ShapeInfo> allOSMNodes; + // private List<ShapeDebugFrame.ShapeInfo> allGMLNodes; + + private Convert() { + } + + /** + Run the map convertor. + @param args Command line arguments: osm-mapname gml-mapname. + */ + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Usage: Convert <osm-mapname> <gml-mapname>"); + return; + } + try { + OSMMap osmMap = readOSMMap(args[0]); + OSMMapViewer osmViewer = new OSMMapViewer(osmMap); + Convertor convert = new Convertor(); + GMLMap gmlMap = convert.convert(osmMap); + MapWriter.writeMap(gmlMap, args[1], RobocupFormat.INSTANCE); + GMLMapViewer gmlViewer = new GMLMapViewer(gmlMap); + JFrame frame = new JFrame("Convertor"); + JPanel main = new JPanel(new GridLayout(1, 2)); + osmViewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + gmlViewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + osmViewer.setBorder(BorderFactory.createTitledBorder("OSM map")); + gmlViewer.setBorder(BorderFactory.createTitledBorder("GML map")); + main.add(osmViewer); + main.add(gmlViewer); + frame.setContentPane(main); + frame.pack(); + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (Exception e) { + e.printStackTrace(); + } + // CHECKSTYLE:ON:IllegalCatch + } + + private static OSMMap readOSMMap(String file) throws OSMException, IOException, DocumentException { + File f = new File(file); + return new OSMMap(f); + } +} diff --git a/modules/maps/src/maps/convert/ConvertStep.java b/modules/maps/src/maps/convert/ConvertStep.java new file mode 100644 index 0000000000000000000000000000000000000000..e59de093ea970d25c7062baefec347b34a83cb08 --- /dev/null +++ b/modules/maps/src/maps/convert/ConvertStep.java @@ -0,0 +1,154 @@ +package maps.convert; + +import javax.swing.JProgressBar; +import javax.swing.JLabel; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +import rescuecore2.misc.gui.ShapeDebugFrame; +import rescuecore2.log.Logger; + +/** + A step in the map conversion process. +*/ +public abstract class ConvertStep { + /** A ShapeDebugFrame for use by subclasses. */ + protected ShapeDebugFrame debug; + + private JProgressBar progress; + private JLabel status; + + /** + Construct a ConvertStep. + */ + protected ConvertStep() { + this.progress = new JProgressBar(); + this.status = new JLabel(); + progress.setString(""); + progress.setStringPainted(true); + debug = new ShapeDebugFrame(); + } + + /** + Set the progress level. + @param amount The new progress. + */ + protected void setProgress(final int amount) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setValue(amount); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Increase the progress level by one. + */ + protected void bumpProgress() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setValue(progress.getValue() + 1); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Increase the maximum progress level by one. + */ + protected void bumpMaxProgress() { + bumpMaxProgress(1); + } + + /** + Increase the maximum progress level by some amount. + @param amount The amount to increase the maximum progress level. + */ + protected void bumpMaxProgress(final int amount) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setMaximum(progress.getMaximum() + amount); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Set the progress maximum. + @param max The new progress maximum. + */ + protected void setProgressLimit(final int max) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setIndeterminate(false); + progress.setMaximum(max); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Set the status label. + @param s The new status label. + */ + protected void setStatus(final String s) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + status.setText(s); + } + }); + } + + /** + Get the JProgressBar component for this step. + @return The progress bar component. + */ + public JProgressBar getProgressBar() { + return progress; + } + + /** + Get the status component for this step. + @return The status component. + */ + public JComponent getStatusComponent() { + return status; + } + + /** + Perform the conversion step. + */ + public final void doStep() { + try { + Logger.pushLogContext(getClass().getName()); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setIndeterminate(true); + } + }); + step(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setIndeterminate(false); + progress.setValue(progress.getMaximum()); + } + }); + debug.deactivate(); + } + finally { + Logger.popLogContext(); + } + } + + /** + Get a user-friendly description of this step. + @return A description string. + */ + public abstract String getDescription(); + + /** + Perform the step. + */ + protected abstract void step(); +} diff --git a/modules/maps/src/maps/convert/legacy2gml/BuildingInfo.java b/modules/maps/src/maps/convert/legacy2gml/BuildingInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..4a28f40abe17a936eaf409e97d92dd5886ed5e70 --- /dev/null +++ b/modules/maps/src/maps/convert/legacy2gml/BuildingInfo.java @@ -0,0 +1,209 @@ +package maps.convert.legacy2gml; + +import maps.legacy.LegacyBuilding; +import maps.gml.GMLMap; +import maps.gml.GMLBuilding; +import maps.gml.GMLRoad; +import maps.gml.GMLNode; +import maps.gml.GMLDirectedEdge; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Collection; +import java.util.HashSet; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.Pair; +import rescuecore2.log.Logger; + +/** + Container for building information during conversion. +*/ +public class BuildingInfo { + /** + * Width of a generated entrance road. + */ + public static final int ENTRANCE_SIZE = 3000; + + private Point2D roadLeft; + private Point2D roadRight; + + private LegacyBuilding building; + private List<GMLNode> apexes; + + /** + Construct a BuildingInfo object. + @param b The LegacyBuilding to store info about. + */ + public BuildingInfo(LegacyBuilding b) { + building = b; + } + + /** + Set the left corner at the road end. + @param newRoadLeft The new head-left corner. + */ + public void setRoadLeft(Point2D newRoadLeft) { + roadLeft = newRoadLeft; + } + + /** + Set the right corner at the road end. + @param newRoadRight The new head-right corner. + */ + public void setRoadRight(Point2D newRoadRight) { + roadRight = newRoadRight; + } + + /** + Process the building and create a GMLBuilding. + @param gml The GML map. + @param nodeInfo Information about the legacy nodes. + @param roadInfo Information about the legacy roads. + */ + public void process(GMLMap gml, Map<Integer, NodeInfo> nodeInfo, Map<Integer, RoadInfo> roadInfo) { + apexes = new ArrayList<GMLNode>(); + int[] coords = building.getApexes(); + for (int i = 0; i < coords.length; i += 2) { + int x = coords[i]; + int y = coords[i + 1]; + GMLNode node = gml.createNode(x, y); + apexes.add(node); + } + GMLBuilding b = gml.createBuildingFromNodes(apexes); + b.setFloors(building.getFloors()); + b.setCode(building.getCode()); + // Create an entrance + // Logger.debug("Creating entrance for " + building); + if (building.getEntrances().length == 0) { + return; + } + Point2D entrancePoint = nodeInfo.get(building.getEntrances()[0]).getLocation(); + Point2D centre = new Point2D(building.getX(), building.getY()); + Line2D centreLine = new Line2D(centre, entrancePoint); + Vector2D centreVector = centreLine.getDirection(); + Vector2D leftVector = centreVector.getNormal().normalised().scale(ENTRANCE_SIZE / 2.0); + Vector2D rightVector = leftVector.scale(-1); + Line2D left = new Line2D(centre.plus(leftVector), centreVector); + Line2D right = new Line2D(centre.plus(rightVector), centreVector); + // Logger.debug("Entrance point: " + entrancePoint); + // Logger.debug("Building centre: " + centre); + // Logger.debug("Left line: " + left); + // Logger.debug("Right line: " + right); + + Pair<Line2D, GMLDirectedEdge> leftEntrance = getEntrancePoint(left, centreLine, b.getEdges(), true, true); + if (leftEntrance == null) { + Logger.warn(b + ": Left entrance line does not intersect any walls"); + return; + } + GMLNode leftNode = gml.createNode(leftEntrance.first().getOrigin().getX(), leftEntrance.first().getOrigin().getY()); + // Logger.debug("New left node: " + leftNode); + gml.splitEdge(leftEntrance.second().getEdge(), leftNode); + Pair<Line2D, GMLDirectedEdge> rightEntrance = getEntrancePoint(right, centreLine, b.getEdges(), false, true); + if (rightEntrance == null) { + Logger.warn(b + ": Right entrance line does not intersect any walls"); + return; + } + GMLNode rightNode = gml.createNode(rightEntrance.first().getOrigin().getX(), rightEntrance.first().getOrigin().getY()); + // Logger.debug("New right node: " + rightNode); + gml.splitEdge(rightEntrance.second().getEdge(), rightNode); + gml.removeEdge(leftEntrance.second().getEdge()); + gml.removeEdge(rightEntrance.second().getEdge()); + // Now create the new road segment +// Pair<Line2D, GMLDirectedEdge> leftRoad = getEntrancePoint(left, centreLine, getAllRoadEdges(nodeInfo.values(), roadInfo.values()), true, false); +// if (leftRoad == null) { +// Logger.warn(b + ": Left entrance line does not intersect any roads"); +// return; +// } + GMLNode roadLeftNode = gml.createNode(roadLeft.getX(), roadLeft.getY()); + // Logger.debug("New road left node: " + roadLeftNode); +// gml.splitEdge(leftRoad.second().getEdge(), roadLeftNode); +// Pair<Line2D, GMLDirectedEdge> rightRoad = getEntrancePoint(right, centreLine, getAllRoadEdges(nodeInfo.values(), roadInfo.values()), false, false); +// if (rightRoad == null) { +// Logger.warn(b + ": Right entrance line does not intersect any roads"); +// return; +// } + GMLNode roadRightNode = gml.createNode(roadRight.getX(), roadRight.getY()); + // Logger.debug("New road left node: " + roadRightNode); +// gml.splitEdge(rightRoad.second().getEdge(), roadRightNode); + // Create the road + gml.createRoad(gml.apexesToEdges(roadLeftNode, roadRightNode, rightNode, leftNode)); + } + + /** + Trim a line to a set of walls and return the trimmed line and intersecting wall. Returns null if the line does not intersect any walls. + @param line The line to trim. + @param walls The walls to trim to. + @param trimStart True to trim the start of the line, false to trim the end. + */ + private Pair<Line2D, GMLDirectedEdge> trim(Line2D line, Collection<GMLDirectedEdge> walls, boolean trimStart) { + GMLDirectedEdge wall = null; + for (GMLDirectedEdge next : walls) { + Point2D p = GeometryTools2D.getSegmentIntersectionPoint(line, Tools.gmlDirectedEdgeToLine(next)); + if (p != null) { + if (trimStart) { + line = new Line2D(p, line.getEndPoint()); + } + else { + line = new Line2D(line.getOrigin(), p); + } + wall = next; + } + } + if (wall == null) { + return null; + } + return new Pair<Line2D, GMLDirectedEdge>(line, wall); + } + + private Pair<Line2D, GMLDirectedEdge> getEntrancePoint(Line2D line, Line2D centre, Collection<GMLDirectedEdge> walls, boolean left, boolean trimStart) { + Pair<Line2D, GMLDirectedEdge> trimmed = trim(line, walls, trimStart); + if (trimmed == null) { + // Entrance line does not intersect with the building outline. Snap it back to the endpoint of the wall the centre line intersects with. + trimmed = trim(centre, walls, trimStart); + if (trimmed == null) { + // Entrance does not intersect with any walls! + return null; + } + GMLDirectedEdge wall = trimmed.second(); + Line2D wallLine = Tools.gmlDirectedEdgeToLine(wall); + Vector2D centreNormal = centre.getDirection().getNormal().normalised(); + if (!left) { + centreNormal = centreNormal.scale(-1); + } + Vector2D wallDirection = wallLine.getDirection().normalised(); + Point2D end; + if (wallDirection.dot(centreNormal) > 0) { + // Same direction: end point is the end of the wall + end = wallLine.getEndPoint(); + } + else { + // Opposite directions: end point is the start of the wall + end = wallLine.getOrigin(); + } + return new Pair<Line2D, GMLDirectedEdge>(new Line2D(end, line.getEndPoint()), wall); + } + else { + return trimmed; + } + } + + private Collection<GMLDirectedEdge> getAllRoadEdges(Collection<NodeInfo> nodes, Collection<RoadInfo> roads) { + Collection<GMLDirectedEdge> all = new HashSet<GMLDirectedEdge>(); + for (NodeInfo next : nodes) { + GMLRoad r = next.getRoad(); + if (r != null) { + all.addAll(r.getEdges()); + } + } + for (RoadInfo next : roads) { + all.addAll(next.getRoad().getEdges()); + } + return all; + } +} + diff --git a/modules/maps/src/maps/convert/legacy2gml/LegacyToGML.java b/modules/maps/src/maps/convert/legacy2gml/LegacyToGML.java new file mode 100644 index 0000000000000000000000000000000000000000..0907b34a8e74565b53675ac2779a8a4262206118 --- /dev/null +++ b/modules/maps/src/maps/convert/legacy2gml/LegacyToGML.java @@ -0,0 +1,109 @@ +package maps.convert.legacy2gml; + +import maps.legacy.LegacyMap; +import maps.legacy.LegacyMapFormat; +import maps.legacy.LegacyRoad; +import maps.legacy.LegacyNode; +import maps.legacy.LegacyBuilding; +import maps.gml.GMLMap; +import maps.gml.formats.RobocupFormat; +import maps.ScaleConversion; + +import java.io.File; + +import java.util.Map; +import java.util.HashMap; + +import rescuecore2.log.Logger; + +/** + This class converts maps from the legacy format to GML. +*/ +public final class LegacyToGML { + private static final double MM_TO_M = 0.001; + + private LegacyToGML() {} + + /** + Run the map convertor. + @param args Command line arguments: legacy-mapdir gml-mapname. + */ + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("Usage: LegacyToGML <legacy-mapdir> <gml-mapname>"); + return; + } + try { + Logger.info("Reading legacy map"); + LegacyMap legacy = LegacyMapFormat.INSTANCE.read(new File(args[0])); + GMLMap gml = new GMLMap(); + Logger.info("Converting"); + convert(legacy, gml); + Logger.info("Writing GML map"); + RobocupFormat.INSTANCE.write(gml, new File(args[1])); + Logger.info("Done"); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (Exception e) { + e.printStackTrace(); + } + // CHECKSTYLE:ON:IllegalCatch + System.exit(0); + } + + private static void convert(LegacyMap legacy, GMLMap gml) { + Map<Integer, RoadInfo> roadInfo = new HashMap<Integer, RoadInfo>(); + Map<Integer, NodeInfo> nodeInfo = new HashMap<Integer, NodeInfo>(); + Map<Integer, BuildingInfo> buildingInfo = new HashMap<Integer, BuildingInfo>(); + Logger.debug("Reading roads"); + for (LegacyRoad r : legacy.getRoads()) { + roadInfo.put(r.getID(), new RoadInfo()); + } + Logger.debug("Removing duplicate roads"); + for (LegacyNode n : legacy.getNodes()) { + Map<Integer, LegacyRoad> roadToFarNode = new HashMap<Integer, LegacyRoad>(); + for (int rid : n.getEdges()) { + LegacyRoad road = legacy.getRoad(rid); + if (road == null) { + continue; + } + int farNodeId = (n.getID() == road.getHead()) ? road.getTail() : road.getHead(); + + // Use the widest road + LegacyRoad existingRoad = roadToFarNode.get(farNodeId); + if (existingRoad != null && road.getWidth() <= existingRoad.getWidth()) { + roadInfo.remove(road.getID()); + } + else if (existingRoad != null) { + roadInfo.remove(existingRoad.getID()); + roadToFarNode.put(farNodeId, road); + } + else { + roadToFarNode.put(farNodeId, road); + } + } + } + Logger.debug("Reading nodes"); + for (LegacyNode n : legacy.getNodes()) { + nodeInfo.put(n.getID(), new NodeInfo(n)); + } + Logger.debug("Reading buildings"); + for (LegacyBuilding b : legacy.getBuildings()) { + buildingInfo.put(b.getID(), new BuildingInfo(b)); + } + Logger.debug("Creating intersections"); + for (NodeInfo n : nodeInfo.values()) { + n.process(legacy, gml, roadInfo, buildingInfo); + } + Logger.debug("Creating roads"); + for (RoadInfo r : roadInfo.values()) { + r.process(gml); + } + Logger.debug("Creating buildings"); + for (BuildingInfo b : buildingInfo.values()) { + b.process(gml, nodeInfo, roadInfo); + } + // Rescale to m + gml.convertCoordinates(new ScaleConversion(gml.getMinX(), gml.getMinY(), MM_TO_M, MM_TO_M)); + } +} diff --git a/modules/maps/src/maps/convert/legacy2gml/NodeInfo.java b/modules/maps/src/maps/convert/legacy2gml/NodeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..6b93d033feceec506672402677b6edae3c4c3c7c --- /dev/null +++ b/modules/maps/src/maps/convert/legacy2gml/NodeInfo.java @@ -0,0 +1,343 @@ +package maps.convert.legacy2gml; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLRoad; +import maps.legacy.LegacyBuilding; +import maps.legacy.LegacyMap; +import maps.legacy.LegacyNode; +import maps.legacy.LegacyObject; +import maps.legacy.LegacyRoad; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; + +/** + Container for node information during conversion. +*/ +public class NodeInfo { + private LegacyNode node; + private Point2D centre; + private List<GMLNode> apexes; + private GMLRoad road; + + /** + Construct a NodeInfo object. + @param node The LegacyNode to store info about. + */ + public NodeInfo(LegacyNode node) { + this.node = node; + centre = new Point2D(node.getX(), node.getY()); + } + + /** + Get the LegacyNode. + @return The LegacyNode. + */ + public LegacyNode getNode() { + return node; + } + + /** + Get the node location. + @return The node location. + */ + public Point2D getLocation() { + return centre; + } + + /** + Get the generated GMLRoad. + @return The generated road or null if this node did not generate a road segment. + */ + public GMLRoad getRoad() { + return road; + } + + /** + Process the node and create GMLRoad objects if required. + @param legacy The legacy map. + @param gml The GML map. + @param roadInfo A map from road ID to RoadInfo. + @param buildingInfo A map from building ID to BuildingInfo. + */ + public void process(LegacyMap legacy, GMLMap gml, Map<Integer, RoadInfo> roadInfo, Map<Integer, BuildingInfo> buildingInfo) { + apexes = new ArrayList<GMLNode>(); + List<EdgeAspect> edges = new ArrayList<EdgeAspect>(); + for (int id : node.getEdges()) { + LegacyRoad lRoad = legacy.getRoad(id); + if (lRoad != null && roadInfo.containsKey(id)) { + edges.add(new RoadAspect(lRoad, node, legacy, roadInfo.get(id))); + continue; + } + LegacyBuilding lBuilding = legacy.getBuilding(id); + if (lBuilding != null && buildingInfo.containsKey(id)) { + edges.add(new BuildingAspect(lBuilding, node, legacy, buildingInfo.get(id))); + } + } + if (edges.size() == 1) { + EdgeAspect aspect = edges.get(0); + findRoadEdges(aspect, centre); + } + else { + // Sort the roads + CounterClockwiseSort sort = new CounterClockwiseSort(centre); + Collections.sort(edges, sort); + // Now build the apex list + Iterator<EdgeAspect> it = edges.iterator(); + EdgeAspect first = it.next(); + EdgeAspect prev = first; + EdgeAspect next; + while (it.hasNext()) { + next = it.next(); + Point2D[] newApexes = findIncomingEdgeIntersection(prev, next, centre); + for (Point2D apex : newApexes) { + apexes.add(gml.createNode(apex.getX(), apex.getY())); + } + prev = next; + } + Point2D[] newApexes = findIncomingEdgeIntersection(prev, first, centre); + for (Point2D apex : newApexes) { + apexes.add(gml.createNode(apex.getX(), apex.getY())); + } + } + if (apexes.size() > 2) { + road = gml.createRoadFromNodes(apexes); + } + } + + /** + Process two incoming roads and find the intersection of the right edge of the first road and the left edge of the second road. + @param first The road endpoint to check the right edge of. + @param second The road endpoint to check the left edge of. + @param centrePoint The centre of the intersection. + @return The intersection of the two roads. + */ + private Point2D[] findIncomingEdgeIntersection(EdgeAspect first, EdgeAspect second, Point2D centrePoint) { + LegacyObject firstNode = first.getFarNode(); + LegacyObject secondNode = second.getFarNode(); + Point2D firstPoint = new Point2D(firstNode.getX(), firstNode.getY()); + Point2D secondPoint = new Point2D(secondNode.getX(), secondNode.getY()); + // Find the intersection of the incoming road edges + Vector2D firstVector = centrePoint.minus(firstPoint); + Vector2D secondVector = centrePoint.minus(secondPoint); + Vector2D firstNormal = firstVector.getNormal().normalised().scale(-first.getRoadWidth() / 2.0); + Vector2D secondNormal = secondVector.getNormal().normalised().scale(second.getRoadWidth() / 2.0); + Point2D start1Point = firstPoint.plus(firstNormal); + Point2D start2Point = secondPoint.plus(secondNormal); + Line2D line1 = new Line2D(start1Point, firstVector); + Line2D line2 = new Line2D(start2Point, secondVector); + Point2D intersection = GeometryTools2D.getIntersectionPoint(line1, line2); + if (intersection == null) { + // Lines are parallel + // This means the normals are parallel, so we can just add a normal to the centre point to generate an intersection point + intersection = centrePoint.plus(firstNormal); + } + + double maxWidth = Math.max(first.getRoadWidth(), second.getRoadWidth()); + double distance = GeometryTools2D.getDistance(centrePoint, intersection); + Vector2D intersectionVector = intersection.minus(centrePoint).normalised(); + double dp1 = firstVector.normalised().dot(intersectionVector); + double dp2 = secondVector.normalised().dot(intersectionVector); + + if (distance > maxWidth && dp1 > 0 && dp2 > 0) { + //Cap spikes on acute angles + Vector2D cutoffVector = intersectionVector.getNormal().scale(maxWidth); + Point2D cutoffStart = centrePoint.plus(intersectionVector.scale(maxWidth)).plus(cutoffVector); + // CHECKSTYLE:OFF:MagicNumber + Line2D cutoffLine = new Line2D(cutoffStart, cutoffVector.scale(-2.0)); + // CHECKSTYLE:ON:MagicNumber + Point2D end1 = GeometryTools2D.getIntersectionPoint(line1, cutoffLine); + Point2D end2 = GeometryTools2D.getIntersectionPoint(line2, cutoffLine); + first.setRightEnd(end1); + second.setLeftEnd(end2); + return new Point2D[] {end1, end2}; + } + else if (distance > maxWidth && (dp1 > 0 || dp2 > 0)) { + // Prevent too distant intersections on obtuse angles + // Those usually happen on intersections between roads with (very) different widths. + // For now, just use the intersection that would have occured between two roads + // of the averages width + double avgWidth = (first.getRoadWidth() + second.getRoadWidth()) / 2.0; + firstNormal = firstVector.getNormal().normalised().scale(-avgWidth / 2.0); + secondNormal = secondVector.getNormal().normalised().scale(avgWidth / 2.0); + start1Point = firstPoint.plus(firstNormal); + start2Point = secondPoint.plus(secondNormal); + line1 = new Line2D(start1Point, firstVector); + line2 = new Line2D(start2Point, secondVector); + intersection = GeometryTools2D.getIntersectionPoint(line1, line2); + + first.setRightEnd(intersection); + second.setLeftEnd(intersection); + } + else if (distance > maxWidth) { + // Inner acute angle: just cut off at twice the max. road width. + intersection = centrePoint.plus(intersectionVector.scale(maxWidth * 2.0)); + first.setRightEnd(intersection); + second.setLeftEnd(intersection); + } + else { + first.setRightEnd(intersection); + second.setLeftEnd(intersection); + } + return new Point2D[] {intersection}; + } + + private void findRoadEdges(EdgeAspect aspect, Point2D centrePoint) { + LegacyObject farNode = aspect.getFarNode(); + Point2D roadPoint = new Point2D(farNode.getX(), farNode.getY()); + Vector2D vector = centrePoint.minus(roadPoint); + Vector2D leftNormal = vector.getNormal().normalised().scale(aspect.getRoadWidth() / 2.0); + Vector2D rightNormal = leftNormal.scale(-1); + Point2D left = centrePoint.plus(leftNormal); + Point2D right = centrePoint.plus(rightNormal); + aspect.setLeftEnd(left); + aspect.setRightEnd(right); + } + + private interface EdgeAspect { + int getRoadWidth(); + LegacyObject getFarNode(); + void setLeftEnd(Point2D p); + void setRightEnd(Point2D p); + } + + private static class RoadAspect implements EdgeAspect { + private boolean forward; + private LegacyNode farNode; + private RoadInfo info; + private int width; + + RoadAspect(LegacyRoad road, LegacyNode intersection, LegacyMap map, RoadInfo info) { + forward = intersection.getID() == road.getTail(); + farNode = map.getNode(forward ? road.getHead() : road.getTail()); + width = road.getWidth(); + this.info = info; + } + + public int getRoadWidth() { + return width; + } + + public LegacyObject getFarNode() { + return farNode; + } + + public void setLeftEnd(Point2D p) { + if (forward) { + info.setHeadLeft(p); + } + else { + info.setTailRight(p); + } + } + + public void setRightEnd(Point2D p) { + if (forward) { + info.setHeadRight(p); + } + else { + info.setTailLeft(p); + } + } + } + + private static class BuildingAspect implements EdgeAspect { + private LegacyBuilding building; + private BuildingInfo info; + + BuildingAspect(LegacyBuilding building, LegacyNode intersection, LegacyMap map, BuildingInfo info) { + this.building = building; + this.info = info; + } + + public int getRoadWidth() { + return BuildingInfo.ENTRANCE_SIZE; + } + + public LegacyObject getFarNode() { + return building; + } + + public void setLeftEnd(Point2D p) { + info.setRoadLeft(p); + } + + public void setRightEnd(Point2D p) { + info.setRoadRight(p); + } + } + + private static class CounterClockwiseSort implements Comparator<EdgeAspect> { + private Point2D centre; + + /** + Construct a CounterClockwiseSort with a reference point. + @param centre The reference point. + */ + public CounterClockwiseSort(Point2D centre) { + this.centre = centre; + } + + @Override + public int compare(EdgeAspect first, EdgeAspect second) { + double d1 = score(first); + double d2 = score(second); + if (d1 < d2) { + return 1; + } + else if (d1 > d2) { + return -1; + } + else { + return 0; + } + } + + /** + Compute the score for a RoadAspect - the amount of clockwiseness from 12 o'clock. + @param aspect The RoadAspect. + @return The amount of clockwiseness. This will be in the range [0..4) with 0 representing 12 o'clock, 1 representing 3 o'clock and so on. + */ + public double score(EdgeAspect aspect) { + LegacyObject node = aspect.getFarNode(); + Point2D point = new Point2D(node.getX(), node.getY()); + Vector2D v = point.minus(centre); + double sin = v.getX() / v.getLength(); + double cos = v.getY() / v.getLength(); + if (Double.isNaN(sin) || Double.isNaN(cos)) { + System.out.println(v); + System.out.println(v.getLength()); + } + return convert(sin, cos); + } + + // CHECKSTYLE:OFF:MagicNumber + private double convert(double sin, double cos) { + if (sin >= 0 && cos >= 0) { + return sin; + } + if (sin >= 0 && cos < 0) { + return 2 - sin; + } + if (sin < 0 && cos < 0) { + return 2 - sin; + } + if (sin < 0 && cos >= 0) { + return 4 + sin; + } + throw new IllegalArgumentException("This should be impossible! What's going on? sin=" + sin + ", cos=" + cos); + } + // CHECKSTYLE:ON:MagicNumber + } +} + diff --git a/modules/maps/src/maps/convert/legacy2gml/RoadInfo.java b/modules/maps/src/maps/convert/legacy2gml/RoadInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..7fb1629c413947ab9b4f94d3265131401d8a6587 --- /dev/null +++ b/modules/maps/src/maps/convert/legacy2gml/RoadInfo.java @@ -0,0 +1,81 @@ +package maps.convert.legacy2gml; + +import rescuecore2.misc.geometry.Point2D; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLRoad; + +import java.util.List; +import java.util.ArrayList; + +/** + Container for road information during conversion. +*/ +public class RoadInfo { + private Point2D headLeft; + private Point2D headRight; + private Point2D tailLeft; + private Point2D tailRight; + private GMLRoad road; + + /** + Construct a RoadInfo. + */ + public RoadInfo() { + } + + /** + Set the left corner at the head end. + @param newHeadLeft The new head-left corner. + */ + public void setHeadLeft(Point2D newHeadLeft) { + headLeft = newHeadLeft; + } + + /** + Set the right corner at the head end. + @param newHeadRight The new head-right corner. + */ + public void setHeadRight(Point2D newHeadRight) { + headRight = newHeadRight; + } + + /** + Set the left corner at the tail end. + @param newTailLeft The new tail-left corner. + */ + public void setTailLeft(Point2D newTailLeft) { + tailLeft = newTailLeft; + } + + /** + Set the right corner at the tail end. + @param newTailRight The new tail-right corner. + */ + public void setTailRight(Point2D newTailRight) { + tailRight = newTailRight; + } + + /** + Get the generated GMLRoad. + @return The generated road. + */ + public GMLRoad getRoad() { + return road; + } + + /** + Process this RoadInfo and generate a GMLRoad object. + @param gml The GML map. + */ + public void process(GMLMap gml) { + List<GMLNode> apexes = new ArrayList<GMLNode>(); + apexes.add(gml.createNode(headLeft.getX(), headLeft.getY())); + apexes.add(gml.createNode(tailLeft.getX(), tailLeft.getY())); + apexes.add(gml.createNode(tailRight.getX(), tailRight.getY())); + apexes.add(gml.createNode(headRight.getX(), headRight.getY())); + road = gml.createRoadFromNodes(apexes); + } +} + diff --git a/modules/maps/src/maps/convert/legacy2gml/Tools.java b/modules/maps/src/maps/convert/legacy2gml/Tools.java new file mode 100644 index 0000000000000000000000000000000000000000..d567a1586c1db1aa58d70b6a1a7642efb7f0bba4 --- /dev/null +++ b/modules/maps/src/maps/convert/legacy2gml/Tools.java @@ -0,0 +1,50 @@ +package maps.convert.legacy2gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +import maps.gml.GMLNode; +import maps.gml.GMLDirectedEdge; + +/** + Utilities for the legacy2gml covertor. +*/ +public final class Tools { + private Tools() {} + + /** + Convert a list of GML nodes to Line2D objects. + @param nodes The node list. + @return A new Line2D list. + */ + public static List<Line2D> nodeListToLineList(List<GMLNode> nodes) { + List<Line2D> result = new ArrayList<Line2D>(nodes.size()); + Iterator<GMLNode> it = nodes.iterator(); + GMLNode first = it.next(); + GMLNode prev = first; + while (it.hasNext()) { + GMLNode next = it.next(); + result.add(new Line2D(new Point2D(prev.getX(), prev.getY()), new Point2D(next.getX(), next.getY()))); + prev = next; + } + result.add(new Line2D(new Point2D(prev.getX(), prev.getY()), new Point2D(first.getX(), first.getY()))); + return result; + } + + /** + Convert a GMLDirectedEdge to a Line2D. + @param edge The edge to convert. + @return A new Line2D. + */ + public static Line2D gmlDirectedEdgeToLine(GMLDirectedEdge edge) { + GMLNode start = edge.getStartNode(); + GMLNode end = edge.getEndNode(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/convert/osm2gml/CleanOSMStep.java b/modules/maps/src/maps/convert/osm2gml/CleanOSMStep.java new file mode 100644 index 0000000000000000000000000000000000000000..e2c9d619ef8492141ac4c8d7f8e9034039928fb3 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/CleanOSMStep.java @@ -0,0 +1,212 @@ +package maps.convert.osm2gml; + +import maps.osm.OSMMap; +import maps.osm.OSMNode; +import maps.osm.OSMRoad; +import maps.osm.OSMBuilding; +import maps.osm.OSMWay; +import maps.convert.ConvertStep; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.misc.geometry.Line2D; +//import rescuecore2.log.Logger; + +/** + This step cleans the OpenStreetMap data by removing duplicate nodes and way, fixing degenerate ways, and fixing building edge orderings. +*/ +public class CleanOSMStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a CleanOSMStep. + @param map The TemporaryMap to clean. + */ + public CleanOSMStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Cleaning OpenStreetMap data"; + } + + @Override + protected void step() { + OSMMap osm = map.getOSMMap(); + setProgressLimit(osm.getNodes().size() + (osm.getRoads().size() + osm.getBuildings().size()) * 2 + osm.getBuildings().size()); + setStatus("Looking for duplicate nodes"); + int nodes = fixNodes(); + setStatus("Fixing degenerate ways"); + int fixed = fixDegenerateWays(osm.getRoads()); + fixed += fixDegenerateWays(osm.getBuildings()); + setStatus("Looking for duplicate ways"); + int ways = fixDuplicateWays(osm.getRoads()); + ways += fixDuplicateWays(osm.getBuildings()); + setStatus("Fixing building direction"); + int b = fixBuildingDirection(osm.getBuildings()); + setStatus("Removed " + nodes + " duplicate nodes and " + ways + " duplicate ways, fixed " + fixed + " degenerate ways, fixed " + b + " clockwise buildings"); + } + + private int fixNodes() { + OSMMap osm = map.getOSMMap(); + int count = 0; + double threshold = ConvertTools.nearbyThreshold(osm, map.getNearbyThreshold()); + Set<OSMNode> removed = new HashSet<OSMNode>(); + for (OSMNode next : osm.getNodes()) { + if (removed.contains(next)) { + bumpProgress(); + continue; + } + for (OSMNode test : osm.getNodes()) { + if (next == test) { + continue; + } + if (removed.contains(test)) { + continue; + } + if (nearby(next, test, threshold)) { + // Remove the test node and replace all references to it with 'next' + osm.replaceNode(test, next); + removed.add(test); + // Logger.debug("Removed duplicate node " + test.getID()); + ++count; + } + } + bumpProgress(); + } + return count; + } + + private int fixDegenerateWays(Collection<? extends OSMWay> ways) { + int count = 0; + for (OSMWay way : ways) { + // Check that no nodes are listed multiple times in sequence + List<Long> ids = new ArrayList<Long>(way.getNodeIDs()); + Iterator<Long> it = ids.iterator(); + if (!it.hasNext()) { + // Empty way. Remove it. + remove(way); + ++count; + continue; + } + long last = it.next(); + boolean fixed = false; + while (it.hasNext()) { + long next = it.next(); + if (next == last) { + // Duplicate node + it.remove(); + // Logger.debug("Removed node " + next + " from way " + way.getID()); + fixed = true; + } + last = next; + } + if (fixed) { + way.setNodeIDs(ids); + ++count; + } + bumpProgress(); + } + return count; + } + + private int fixDuplicateWays(Collection<? extends OSMWay> ways) { + int count = 0; + Set<OSMWay> removed = new HashSet<OSMWay>(); + for (OSMWay next : ways) { + if (removed.contains(next)) { + bumpProgress(); + continue; + } + // Look at all other roads and see if any are subpaths of this road + for (OSMWay test : ways) { + if (next == test) { + continue; + } + if (removed.contains(test)) { + continue; + } + List<Long> testIDs = test.getNodeIDs(); + if (isSubList(testIDs, next.getNodeIDs())) { + remove(test); + removed.add(test); + ++count; + // Logger.debug("Removed way " + test.getID()); + } + else { + Collections.reverse(testIDs); + if (isSubList(testIDs, next.getNodeIDs())) { + remove(test); + removed.add(test); + ++count; + // Logger.debug("Removed way " + test.getID()); + } + } + } + bumpProgress(); + } + return count; + } + + /** + Make sure all buildings have their nodes listed in clockwise order. + */ + private int fixBuildingDirection(Collection<OSMBuilding> buildings) { + OSMMap osm = map.getOSMMap(); + // Sum the angles of all right-hand turns + // If the total is +360 then order is clockwise, -360 means counterclockwise. + int count = 0; + for (OSMBuilding building : buildings) { + // Logger.debug("Building " + building + " angle sum: " + ConvertTools.getAnglesSum(building, osm)); + if (ConvertTools.isClockwise(building, osm)) { + // Reverse the order + List<Long> ids = building.getNodeIDs(); + Collections.reverse(ids); + building.setNodeIDs(ids); + ++count; + } + bumpProgress(); + } + return count; + } + + private boolean nearby(OSMNode first, OSMNode second, double threshold) { + double dx = first.getLongitude() - second.getLongitude(); + double dy = first.getLatitude() - second.getLatitude(); + return (dx >= -threshold + && dx <= threshold + && dy >= -threshold + && dy <= threshold); + } + + private boolean isSubList(List<Long> first, List<Long> second) { + return Collections.indexOfSubList(second, first) != -1; + } + + private void remove(OSMWay way) { + OSMMap osm = map.getOSMMap(); + if (way instanceof OSMRoad) { + osm.removeRoad((OSMRoad)way); + } + else if (way instanceof OSMBuilding) { + osm.removeBuilding((OSMBuilding)way); + } + else { + throw new IllegalArgumentException("Don't know how to handle this type of OSMWay: " + way.getClass().getName()); + } + } + + private Line2D makeLine(long first, long second) { + OSMMap osm = map.getOSMMap(); + OSMNode n1 = osm.getNode(first); + OSMNode n2 = osm.getNode(second); + return new Line2D(n1.getLongitude(), n1.getLatitude(), n2.getLongitude() - n1.getLongitude(), n2.getLatitude() - n1.getLatitude()); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/ComputePassableEdgesStep.java b/modules/maps/src/maps/convert/osm2gml/ComputePassableEdgesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..4c66c605cc7fc0480db0faa35777300aa0bb0473 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/ComputePassableEdgesStep.java @@ -0,0 +1,56 @@ +package maps.convert.osm2gml; + +import maps.convert.ConvertStep; + +import java.util.Collection; + +/** + This step computes which edges are passable and sets up neighbours accordingly. +*/ +public class ComputePassableEdgesStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a ComputePassableEdgesStep. + @param map The TemporaryMap to use. + */ + public ComputePassableEdgesStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Computing passable edges"; + } + + @Override + protected void step() { + setProgressLimit(map.getAllEdges().size()); + // For each edge see if it is shared by two road faces + // If so, make it passable. + int count = 0; + for (Edge next : map.getAllEdges()) { + int roadCount = 0; + Collection<TemporaryObject> attached = map.getAttachedObjects(next); + for (TemporaryObject o : attached) { + if (o instanceof TemporaryRoad || o instanceof TemporaryIntersection) { + ++roadCount; + } + } + if (roadCount > 1) { + // Edge is passable. Make the neighbours. + for (TemporaryObject o1 : attached) { + for (TemporaryObject o2 : attached) { + if (o1 == o2) { + continue; + } + o1.setNeighbour(next, o2); + } + } + ++count; + } + bumpProgress(); + } + setStatus("Made " + count + " edges passable"); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/Constants.java b/modules/maps/src/maps/convert/osm2gml/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..1efcce73d58b0ed811adfcbd058815d89a1b06cc --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/Constants.java @@ -0,0 +1,88 @@ +package maps.convert.osm2gml; + +import java.awt.Color; + +/** Useful OSM to GML constants. */ +public final class Constants { + /** The width of roads in m. */ + public static final float ROAD_WIDTH = 7; + + // CHECKSTYLE:OFF:JavadocVariable + public static final Color BLACK = new Color(0, 0, 0); + public static final Color WHITE = new Color(255, 255, 255); + public static final Color RED = new Color(255, 0, 0); + public static final Color MAROON = new Color(128, 0, 0); + public static final Color LIME = new Color(0, 255, 0); + public static final Color GREEN = new Color(0, 128, 0); + public static final Color BLUE = new Color(0, 0, 255); + public static final Color NAVY = new Color(0, 0, 128); + public static final Color FUSCHIA = new Color(255, 0, 255); + public static final Color GRAY = new Color(128, 128, 128); + public static final Color OLIVE = new Color(128, 128, 0); + public static final Color PURPLE = new Color(128, 0, 128); + public static final Color SILVER = new Color(192, 192, 192); + public static final Color TEAL = new Color(0, 128, 128); + public static final Color YELLOW = new Color(255, 255, 0); + public static final Color AQUA = new Color(0, 255, 255); + public static final Color ORANGE = new Color(255, 140, 0); + + public static final Color TRANSPARENT_BLACK = new Color(0, 0, 0, 128); + public static final Color TRANSPARENT_WHITE = new Color(255, 255, 255, 128); + public static final Color TRANSPARENT_RED = new Color(255, 0, 0, 128); + public static final Color TRANSPARENT_MAROON = new Color(128, 0, 0, 128); + public static final Color TRANSPARENT_LIME = new Color(0, 255, 0, 128); + public static final Color TRANSPARENT_GREEN = new Color(0, 128, 0, 128); + public static final Color TRANSPARENT_BLUE = new Color(0, 0, 255, 128); + public static final Color TRANSPARENT_NAVY = new Color(0, 0, 128, 128); + public static final Color TRANSPARENT_FUSCHIA = new Color(255, 0, 255, 128); + public static final Color TRANSPARENT_GRAY = new Color(128, 128, 128, 128); + public static final Color TRANSPARENT_OLIVE = new Color(128, 128, 0, 128); + public static final Color TRANSPARENT_PURPLE = new Color(128, 0, 128, 128); + public static final Color TRANSPARENT_SILVER = new Color(192, 192, 192, 128); + public static final Color TRANSPARENT_TEAL = new Color(0, 128, 128, 128); + public static final Color TRANSPARENT_YELLOW = new Color(255, 255, 0, 128); + public static final Color TRANSPARENT_AQUA = new Color(0, 255, 255, 128); + public static final Color TRANSPARENT_ORANGE = new Color(255, 140, 0, 128); + + public static final Color[] COLOURS = {RED, + GREEN, + BLUE, + MAROON, + LIME, + NAVY, + OLIVE, + PURPLE, + TEAL, + GRAY, + SILVER, + FUSCHIA, + YELLOW, + AQUA, + ORANGE, + BLACK, + WHITE + }; + + public static final Color[] TRANSPARENT_COLOURS = {TRANSPARENT_RED, + TRANSPARENT_GREEN, + TRANSPARENT_BLUE, + TRANSPARENT_MAROON, + TRANSPARENT_LIME, + TRANSPARENT_NAVY, + TRANSPARENT_OLIVE, + TRANSPARENT_PURPLE, + TRANSPARENT_TEAL, + TRANSPARENT_GRAY, + TRANSPARENT_SILVER, + TRANSPARENT_FUSCHIA, + TRANSPARENT_YELLOW, + TRANSPARENT_AQUA, + TRANSPARENT_ORANGE, + TRANSPARENT_BLACK, + TRANSPARENT_WHITE + }; + // CHECKSTYLE:ON:JavadocVariable + + private Constants() { + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/ConvertTools.java b/modules/maps/src/maps/convert/osm2gml/ConvertTools.java new file mode 100644 index 0000000000000000000000000000000000000000..665cee2d77363396b28c35ae2ff0d48bed61ee2d --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/ConvertTools.java @@ -0,0 +1,459 @@ +package maps.convert.osm2gml; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.gui.ShapeDebugFrame; +//import rescuecore2.log.Logger; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.debug.GMLNodeShapeInfo; +import maps.gml.GMLEdge; +import maps.gml.debug.GMLEdgeShapeInfo; +//import maps.gml.GMLFace; +//import maps.gml.debug.GMLFaceShapeInfo; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLObject; +import maps.gml.GMLRoad; +import maps.gml.GMLBuilding; +import maps.gml.GMLSpace; +import maps.gml.GMLShape; +import maps.gml.debug.GMLShapeInfo; +import maps.osm.OSMMap; +import maps.osm.OSMNode; +import maps.osm.OSMBuilding; +import maps.MapTools; + +import java.util.Set; +import java.util.List; +import java.util.Collection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import java.awt.Color; + +/** + Useful tools for converting OSM to GML. + */ +public final class ConvertTools { + private static final Color BACKGROUND_BUILDING_COLOUR = new Color(0, 255, 0, 32); // Transparent lime + private static final Color BACKGROUND_INTERSECTION_COLOUR = new Color(192, 192, 192, 32); // Transparent silver + private static final Color BACKGROUND_ROAD_COLOUR = new Color(128, 128, 128, 32); // Transparent gray + private static final Color BACKGROUND_SPACE_COLOUR = new Color(0, 128, 0, 32); // Transparent green + + private static final double CLOCKWISE_SUM = -360; + private static final double THRESHOLD = 0.0001; + + private ConvertTools() {} + + /** + Compute the size of one metre in latitude/longitude for an OSMMap. + @param map The map to look up. + @return The size of one metre on the given map. + */ + public static double sizeOf1Metre(OSMMap map) { + return MapTools.sizeOf1Metre(map.getCentreLatitude(), map.getCentreLongitude()); + } + + /** + Compute the nearby-node threshold for an OSMMap in degrees. + @param map The map to look up. + @param thresholdM The desired threshold in meters. + @return The size of the nearby-node threshold for the map in degrees. + */ + public static double nearbyThreshold(OSMMap map, double thresholdM) { + return sizeOf1Metre(map) * thresholdM; + } + + /** + Convert a GMLEdge to a Line2D. + @param edge The edge to convert. + @return A new Line2D. + */ + public static Line2D gmlEdgeToLine(GMLEdge edge) { + GMLNode start = edge.getStart(); + GMLNode end = edge.getEnd(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } + + /** + Convert a GMLEdge to a Line2D. + @param edge The edge to convert. + @param start The node to start from. This must be one of the endpoints of the edge. + @return A new Line2D. + */ + public static Line2D gmlEdgeToLine(GMLEdge edge, GMLNode start) { + if (!start.equals(edge.getStart()) && !start.equals(edge.getEnd())) { + throw new IllegalArgumentException("'start' must be one of the endpoints of 'edge'"); + } + GMLNode end = start.equals(edge.getStart()) ? edge.getEnd() : edge.getStart(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } + + /** + Convert a GMLDirectedEdge to a Line2D. + @param edge The edge to convert. + @return A new Line2D. + */ + public static Line2D gmlDirectedEdgeToLine(GMLDirectedEdge edge) { + GMLNode start = edge.getStartNode(); + GMLNode end = edge.getEndNode(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } + + /** + Find the "leftmost" turn in a particular direction, i.e. the one with the highest left angle, or the lowest right angle if there are no left turns possible. + @param from The edge we're turning from. + @param candidates The set of edges we could turn into. + @return The leftmost turn. + */ + public static Edge findLeftTurn(DirectedEdge from, Set<? extends Edge> candidates) { + return findBestTurn(from, candidates, true); + } + + /** + Find the "rightmost" turn in a particular direction, i.e. the one with the highest right angle, or the lowest left angle if there are no right turns possible. + @param from The edge we're turning from. + @param candidates The set of edges we could turn into. + @return The rightmost turn. + */ + public static Edge findRightTurn(DirectedEdge from, Set<? extends Edge> candidates) { + return findBestTurn(from, candidates, false); + } + + /** + Find the "best" turn in a particular direction. If left turns are preferred then the "best" turn is the one with the highest left angle, or the lowest right angle. For right turns, the "best" is the one with the highest right angle or the lowest left angle. + @param from The edge we're turning from. + @param candidates The set of edges we could turn into. + @param preferLeft Whether to prefer left turns or not. + @return The best turn. + */ + public static Edge findBestTurn(DirectedEdge from, Set<? extends Edge> candidates, boolean preferLeft) { + Edge mostRight = null; + Edge mostLeft = null; + Edge leastRight = null; + Edge leastLeft = null; + double mostRightAngle = 0; + double mostLeftAngle = 0; + double leastRightAngle = 0; + double leastLeftAngle = 0; + Line2D fromLine = from.getLine(); + for (Edge next : candidates) { + if (next.equals(from.getEdge())) { + continue; + } + Line2D nextLine = next.getLine(); + if (!next.getStart().equals(from.getEndNode())) { + nextLine = new Line2D(nextLine.getEndPoint(), nextLine.getOrigin()); + } + if (GeometryTools2D.isRightTurn(fromLine, nextLine)) { + double angle = GeometryTools2D.getRightAngleBetweenLines(fromLine, nextLine); + if (mostRight == null || angle > mostRightAngle) { + mostRight = next; + mostRightAngle = angle; + } + if (leastRight == null || angle < leastRightAngle) { + leastRight = next; + leastRightAngle = angle; + } + } + else { + double angle = GeometryTools2D.getLeftAngleBetweenLines(fromLine, nextLine); + if (mostLeft == null || angle > mostLeftAngle) { + mostLeft = next; + mostLeftAngle = angle; + } + if (leastLeft == null || angle < leastLeftAngle) { + leastLeft = next; + leastLeftAngle = angle; + } + } + } + if (preferLeft) { + if (mostLeft != null) { + return mostLeft; + } + return leastRight; + } + else { + if (mostRight != null) { + return mostRight; + } + return leastLeft; + } + } + + /** + Create ShapeInfo objects for all GMLShapes in a map. + @param map The map to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> getAllDebugShapes(GMLMap map) { + return createGMLShapeDebug(map.getAllShapes()); + } + + /** + Create ShapeInfo objects for all TemporaryObjects in a map. + @param map The map to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> getAllDebugShapes(TemporaryMap map) { + return createTemporaryObjectDebug(map.getAllObjects()); + } + + /** + Create ShapeInfo objects for a set of GMLShapes. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createGMLShapeDebug(GMLShape... objects) { + return createGMLShapeDebug(Arrays.asList(objects)); + } + + /** + Create ShapeInfo objects for a set of GMLShapes. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createGMLShapeDebug(Collection<? extends GMLShape> objects) { + List<ShapeDebugFrame.ShapeInfo> allShapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + for (GMLShape next : objects) { + Color c = Constants.TRANSPARENT_RED; + String name = "Unknown"; + if (next instanceof GMLRoad) { + c = BACKGROUND_ROAD_COLOUR; + name = "Roads"; + } + if (next instanceof GMLBuilding) { + c = BACKGROUND_BUILDING_COLOUR; + name = "Buildings"; + } + if (next instanceof GMLSpace) { + c = BACKGROUND_SPACE_COLOUR; + name = "Spaces"; + } + allShapes.add(new GMLShapeInfo(next, name, Color.BLACK, c)); + } + return allShapes; + } + + /** + Create ShapeInfo objects for a set of GMLObjects. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createGMLObjectDebug(GMLObject... objects) { + return createGMLObjectDebug(Arrays.asList(objects)); + } + + /** + Create ShapeInfo objects for a set of GMLObjects. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createGMLObjectDebug(Collection<? extends GMLObject> objects) { + List<ShapeDebugFrame.ShapeInfo> allShapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + for (GMLObject object : objects) { + if (object instanceof GMLNode) { + allShapes.add(new GMLNodeShapeInfo((GMLNode)object, "Nodes", Constants.BLACK, true)); + } + if (object instanceof GMLEdge) { + allShapes.add(new GMLEdgeShapeInfo((GMLEdge)object, "Edges", Constants.BLACK, false)); + } + /* + if (object instanceof GMLFace) { + GMLFace face = (GMLFace)object; + Color c = Constants.TRANSPARENT_RED; + String name = "Unknown"; + allShapes.add(new GMLFaceShapeInfo(face, "Faces", Constants.BLACK, Constants.TRANSPARENT_AQUA, false)); + } + */ + } + return allShapes; + } + + /** + Create ShapeInfo objects for a set of TemporaryObjects. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createTemporaryObjectDebug(TemporaryObject... objects) { + return createTemporaryObjectDebug(Arrays.asList(objects)); + } + + /** + Create ShapeInfo objects for a set of TemporaryObjects. + @param objects The objects to debug. + @return A list of ShapeInfo objects. + */ + public static List<ShapeDebugFrame.ShapeInfo> createTemporaryObjectDebug(Collection<? extends TemporaryObject> objects) { + List<ShapeDebugFrame.ShapeInfo> allShapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + for (TemporaryObject next : objects) { + Color c = Constants.TRANSPARENT_RED; + String name = "Unknown"; + if (next instanceof TemporaryRoad) { + c = BACKGROUND_ROAD_COLOUR; + name = "Roads"; + } + if (next instanceof TemporaryBuilding) { + c = BACKGROUND_BUILDING_COLOUR; + name = "Buildings"; + } + if (next instanceof TemporaryIntersection) { + c = BACKGROUND_INTERSECTION_COLOUR; + name = "Intersections"; + } + allShapes.add(new TemporaryObjectInfo(next, name, Color.BLACK, c)); + } + return allShapes; + } + + /** + Is a number approximately equal to another number? + @param n The number to test. + @param expected The expected value. + @param threshold The threshold. + @return If n is within [expected - threshold, expected + threshold]. + */ + public static boolean nearlyEqual(double n, double expected, double threshold) { + return (n >= expected - threshold + && n <= expected + threshold); + } + + /** + Find out if a set of GMLDirectedEdges is convex. + @param edges The set of edges to test. + @return True iff the face is convex. + */ + public static boolean isConvex(List<DirectedEdge> edges) { + Iterator<DirectedEdge> it = edges.iterator(); + Line2D first = it.next().getLine(); + Line2D a = first; + Line2D b = it.next().getLine(); + boolean rightTurn = GeometryTools2D.isRightTurn(a, b); + while (it.hasNext()) { + a = b; + b = it.next().getLine(); + if (rightTurn != GeometryTools2D.isRightTurn(a, b)) { + return false; + } + } + if (rightTurn != GeometryTools2D.isRightTurn(b, first)) { + return false; + } + return true; + } + + /** + Sum the angles of all turns in a GMLFace. + @param face The face to check. + @return The sum of angles in the face. + */ + /* + public static double getAnglesSum(GMLFace face) { + double sum = 0; + Iterator<GMLDirectedEdge> it = face.getEdges().iterator(); + GMLDirectedEdge first = it.next(); + GMLDirectedEdge a = first; + while (it.hasNext()) { + GMLDirectedEdge b = it.next(); + double d = getAngle(a, b); + if (!Double.isNaN(d)) { + sum += d; + } + a = b; + } + double d = getAngle(a, first); + if (!Double.isNaN(d)) { + sum += d; + } + return sum; + } + */ + + /** + Sum the angles of all turns in an OSMBuilding. + @param building The building to check. + @param map The OSMMap the building is part of. + @return The sum of angles in the building. + */ + public static double getAnglesSum(OSMBuilding building, OSMMap map) { + double sum = 0; + Iterator<Long> it = building.getNodeIDs().iterator(); + long first = it.next(); + long second = it.next(); + long a = first; + long b = second; + while (it.hasNext()) { + long c = it.next(); + double d = getAngle(a, b, c, map); + // Logger.debug("Angle from " + a + ":" + b + ":" + c + " = " + d); + if (!Double.isNaN(d)) { + sum += d; + } + a = b; + b = c; + } + double d = getAngle(a, first, second, map); + // Logger.debug("Angle from " + a + ":" + first + ":" + second + " = " + d); + if (!Double.isNaN(d)) { + sum += d; + } + return sum; + } + + /** + Find out if a GMLFace is defined clockwise or not. + @param face The GMLFace to check. + @return True if the face is defined clockwise, false if anti-clockwise. + */ + /* + public static boolean isClockwise(GMLFace face) { + return nearlyEqual(getAnglesSum(face), CLOCKWISE_SUM, THRESHOLD); + } + */ + + /** + Find out if an OSMBuilding is defined clockwise or not. + @param building The OSMBuilding to check. + @param map The OSM map. + @return True if the building is defined clockwise, false if anti-clockwise. + */ + public static boolean isClockwise(OSMBuilding building, OSMMap map) { + return nearlyEqual(getAnglesSum(building, map), CLOCKWISE_SUM, THRESHOLD); + } + + /* + private static double getAngle(GMLDirectedEdge a, GMLDirectedEdge b) { + Vector2D v1 = new Vector2D(a.getEndNode().getX() - a.getStartNode().getX(), a.getEndNode().getY() - a.getStartNode().getY()); + Vector2D v2 = new Vector2D(b.getEndNode().getX() - b.getStartNode().getX(), b.getEndNode().getY() - b.getStartNode().getY()); + double d = GeometryTools2D.getAngleBetweenVectors(v1, v2); + if (GeometryTools2D.isRightTurn(v1, v2)) { + return -d; + } + return d; + } + */ + + private static double getAngle(long first, long second, long third, OSMMap map) { + OSMNode n1 = map.getNode(first); + OSMNode n2 = map.getNode(second); + OSMNode n3 = map.getNode(third); + Vector2D v1 = new Vector2D(n2.getLongitude() - n1.getLongitude(), n2.getLatitude() - n1.getLatitude()); + Vector2D v2 = new Vector2D(n3.getLongitude() - n2.getLongitude(), n3.getLatitude() - n2.getLatitude()); + double d = GeometryTools2D.getAngleBetweenVectors(v1, v2); + if (GeometryTools2D.isRightTurn(v1, v2)) { + return -d; + } + return d; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/Convertor.java b/modules/maps/src/maps/convert/osm2gml/Convertor.java new file mode 100644 index 0000000000000000000000000000000000000000..6beeb1b5263d84634da5608c40c0c7e942bb1a9d --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/Convertor.java @@ -0,0 +1,115 @@ +package maps.convert.osm2gml; + +import maps.osm.OSMMap; +import maps.gml.GMLMap; +import maps.convert.ConvertStep; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JComponent; +import javax.swing.JProgressBar; +import javax.swing.Box; +import java.awt.BorderLayout; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Dimension; +import java.awt.Insets; + +import java.util.List; +import java.util.ArrayList; + +/** + This class converts OSMMaps to GMLMaps. +*/ +public class Convertor { + private static final int PROGRESS_WIDTH = 200; + private static final int PROGRESS_HEIGHT = 10; + private static final int STATUS_WIDTH = 500; + private static final int STATUS_HEIGHT = 10; + private static final int MARGIN = 4; + + /** + Convert an OSMMap to a GMLMap. + @param map The OSMMap to convert. + @return A new GMLMap. + */ + public GMLMap convert(OSMMap map) { + GMLMap gmlMap = new GMLMap(); + + JFrame frame = new JFrame("OSM to GML converter"); + JPanel main = new JPanel(new BorderLayout()); + JComponent top = Box.createVerticalBox(); + top.add(new JLabel("Converting OSM map with " + map.getRoads().size() + " roads and " + map.getBuildings().size() + " buildings")); + top.add(new JLabel("Map size: " + (map.getMaxLongitude() - map.getMinLongitude()) + " x " + (map.getMaxLatitude() - map.getMinLatitude()))); + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 1; + c.weighty = 1; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.CENTER; + c.insets = new Insets(MARGIN, MARGIN, MARGIN, MARGIN); + JPanel progress = new JPanel(layout); + + // Random random = new Random(); + + TemporaryMap temp = new TemporaryMap(map); + + List<ConvertStep> steps = new ArrayList<ConvertStep>(); + addStep(new CleanOSMStep(temp), steps, progress, layout, c); + addStep(new ScanOSMStep(temp), steps, progress, layout, c); + addStep(new MakeTempObjectsStep(temp), steps, progress, layout, c); + addStep(new SplitIntersectingEdgesStep(temp), steps, progress, layout, c); + addStep(new SplitShapesStep(temp), steps, progress, layout, c); + addStep(new RemoveShapesStep(temp), steps, progress, layout, c); + addStep(new MergeShapesStep(temp), steps, progress, layout, c); + addStep(new ComputePassableEdgesStep(temp), steps, progress, layout, c); + /* + addStep(new CreateBuildingsStep(temp, ConvertTools.sizeOf1Metre(osmMap), random), steps, progress, layout, c); + addStep(new CreateEntrancesStep(temp), steps, progress, layout, c); + addStep(new PruneStep(temp), steps, progress, layout, c); + */ + addStep(new MakeObjectsStep(temp, gmlMap), steps, progress, layout, c); + + main.add(top); + main.add(progress); + + frame.setContentPane(main); + frame.pack(); + frame.setVisible(true); + + for (ConvertStep next : steps) { + next.doStep(); + } + + return gmlMap; + } + + private void addStep(ConvertStep step, List<ConvertStep> steps, JComponent panel, GridBagLayout layout, GridBagConstraints c) { + JLabel title = new JLabel(step.getDescription()); + JProgressBar progress = step.getProgressBar(); + JComponent status = step.getStatusComponent(); + + c.gridx = 0; + c.weightx = 1; + layout.setConstraints(title, c); + panel.add(title); + c.gridx = 1; + c.weightx = 0; + layout.setConstraints(progress, c); + panel.add(progress); + c.gridx = 2; + c.weightx = 1; + layout.setConstraints(status, c); + panel.add(status); + ++c.gridy; + progress.setPreferredSize(new Dimension(PROGRESS_WIDTH, PROGRESS_HEIGHT)); + status.setPreferredSize(new Dimension(STATUS_WIDTH, STATUS_HEIGHT)); + + steps.add(step); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/CreateBuildingsStep.java b/modules/maps/src/maps/convert/osm2gml/CreateBuildingsStep.java new file mode 100644 index 0000000000000000000000000000000000000000..39d1dd45dff929938bc388f6a71aa0f6c37890fe --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/CreateBuildingsStep.java @@ -0,0 +1,189 @@ +package maps.convert.osm2gml; + +import maps.gml.GMLMap; + +import maps.convert.ConvertStep; + +import java.util.Random; + +/** + This class populates a GMLMap with random buildings. +*/ +public class CreateBuildingsStep extends ConvertStep { + private static final double SIMILAR_LENGTH_THRESHOLD = 0.1; + private static final double NEARLY_PARALLEL_THRESHOLD = 0.0001; + + // private GMLMap gmlMap; + // private double sizeOf1m; + // private Random random; + + /** + Construct a CreateBuildingsStep. + @param gmlMap The GMLMap to use. + @param sizeOf1m The size of 1m in GMLMap units. + @param random The random number generator to use. + */ + public CreateBuildingsStep(GMLMap gmlMap, double sizeOf1m, Random random) { + // this.gmlMap = gmlMap; + // this.sizeOf1m = sizeOf1m; + // this.random = random; + } + + @Override + public String getDescription() { + return "Creating buildings"; + } + + @Override + protected void step() { + /* + debug.setBackground(ConvertTools.getAllGMLShapes(gmlMap)); + // Find open spaces with no buildings + setProgressLimit(gmlMap.getEdges().size()); + Set<GMLEdge> seenLeft = new HashSet<GMLEdge>(); + Set<GMLEdge> seenRight = new HashSet<GMLEdge>(); + for (GMLEdge edge : gmlMap.getEdges()) { + if (seenLeft.contains(edge) && seenRight.contains(edge)) { + bumpProgress(); + continue; + } + // Try walking from this edge looking for space on the left + if (!seenLeft.contains(edge)) { + List<GMLDirectedEdge> edges = walk(edge, true); + if (edges != null) { + processOpenSpace(edges); + for (GMLDirectedEdge dEdge : edges) { + seenLeft.add(dEdge.getEdge()); + } + } + } + if (!seenRight.contains(edge)) { + List<GMLDirectedEdge> edges = walk(edge, false); + if (edges != null) { + processOpenSpace(reverseEdgeList(edges)); + for (GMLDirectedEdge dEdge : edges) { + seenRight.add(dEdge.getEdge()); + } + } + } + bumpProgress(); + } + */ + } + + /* + private List<GMLDirectedEdge> walk(GMLEdge edge, boolean left) { + if (hasConnectedFace(edge, left)) { + return null; + } + GMLNode start = edge.getStart(); + GMLNode current = edge.getEnd(); + GMLDirectedEdge dEdge = new GMLDirectedEdge(edge, start); + List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(); + result.add(dEdge); + GMLEdge last = edge; + while (current != start) { + GMLEdge next = ConvertTools.findBestTurn(dEdge, gmlMap.getAttachedEdges(current), left); + boolean forward = current == next.getStart(); + boolean lookOnLeft = (left && forward) || (!left && !forward); +// debug.show("Walking outside " + (left ? "left" : "right"), +// new GMLEdgeShapeInfo(last, "From edge", Constants.RED, true), +// new GMLEdgeShapeInfo(next, "To edge", Constants.BLUE, true)); + // See if any faces are connected on the side we care about + if (hasConnectedFace(next, lookOnLeft)) { + // There's a connected face so this walk isn't going to result in an open space. + return null; + } + dEdge = new GMLDirectedEdge(next, current); + current = dEdge.getEndNode(); + last = next; + result.add(dEdge); + } + return result; + } + + private boolean hasConnectedFace(GMLEdge edge, boolean left) { + Set<GMLFace> faces = gmlMap.getAttachedFaces(edge); +// List<ShapeDebugFrame.ShapeInfo> shapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); +// shapes.add(new GMLEdgeShapeInfo(edge, "Test edge", Constants.BLUE, true)); +// shapes.add(new GMLNodeShapeInfo(edge.getStart(), "Start node", Constants.RED, true)); +// shapes.add(new GMLNodeShapeInfo(edge.getEnd(), "End node", Constants.NAVY, true)); +// for (GMLFace face : faces) { +// shapes.add(new GMLFaceShapeInfo(face, "Attached face", Constants.BLACK, Constants.TRANSPARENT_ORANGE, true)); +// } +// debug.show("Checking for connected faces", shapes); + for (GMLFace face : faces) { + if (FaceType.BUILDING.equals(face.getFaceType())) { + // Always exclude edges on buildings even if they're on the opposite side + return true; + } + if (left) { + if (face.isConnectedLeft(edge)) { + return true; + } + } + else { + if (face.isConnectedRight(edge)) { + return true; + } + } + } + return false; + } + + private List<GMLDirectedEdge> reverseEdgeList(List<GMLDirectedEdge> edges) { + List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(edges.size()); + for (GMLDirectedEdge edge : edges) { + result.add(new GMLDirectedEdge(edge.getEdge(), !edge.isForward())); + } + Collections.reverse(result); + return result; + } + + private void processOpenSpace(List<GMLDirectedEdge> edges) { + GMLFace face = gmlMap.createFace(edges, FaceType.BUILDING); + gmlMap.removeFace(face); + if (!ConvertTools.isClockwise(face)) { + List<GMLDirectedEdgeShapeInfo> e = new ArrayList<GMLDirectedEdgeShapeInfo>(edges.size()); + for (GMLDirectedEdge next : edges) { + e.add(new GMLDirectedEdgeShapeInfo(next, "Open space edge", Constants.OLIVE, true, true)); + } + // Split into "nice" shapes + // if (isNearlyRectangular(face)) { + debug.show("Open space", e); + RowHousingBuildingSpaceFiller filler = new RowHousingBuildingSpaceFiller(sizeOf1m, random, debug); + filler.createBuildings(face, gmlMap); + // } + } + } + + private boolean isNearlyRectangular(GMLFace face) { + if (face.getEdges().size() != 4) { + return false; + } + // Check if the opposing faces are approximately parallel + Iterator<GMLDirectedEdge> it = face.getEdges().iterator(); + GMLDirectedEdge e1 = it.next(); + GMLDirectedEdge e2 = it.next(); + GMLDirectedEdge e3 = it.next(); + GMLDirectedEdge e4 = it.next(); + if (nearlyParallel(e1, e3) && nearlyParallel(e2, e4) && similarLength(e1, e3) && similarLength(e2, e4)) { + return true; + } + return false; + } + + private boolean nearlyParallel(GMLDirectedEdge e1, GMLDirectedEdge e2) { + Line2D l1 = ConvertTools.gmlDirectedEdgeToLine(e1); + Line2D l2 = ConvertTools.gmlDirectedEdgeToLine(e2); + double d = (l1.getDirection().getX() * l2.getDirection().getY()) - (l1.getDirection().getY() * l2.getDirection().getX()); + return ConvertTools.nearlyEqual(d, 0, NEARLY_PARALLEL_THRESHOLD); + } + + private boolean similarLength(GMLDirectedEdge e1, GMLDirectedEdge e2) { + double l1 = ConvertTools.gmlDirectedEdgeToLine(e1).getDirection().getLength(); + double l2 = ConvertTools.gmlDirectedEdgeToLine(e1).getDirection().getLength(); + return ConvertTools.nearlyEqual(l1 - l2, 0, SIMILAR_LENGTH_THRESHOLD); + } + */ +} diff --git a/modules/maps/src/maps/convert/osm2gml/CreateEntrancesStep.java b/modules/maps/src/maps/convert/osm2gml/CreateEntrancesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..fb8df525cc45082e428e0cf08e4dbd20c1f67230 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/CreateEntrancesStep.java @@ -0,0 +1,81 @@ +package maps.convert.osm2gml; + +import maps.gml.GMLMap; + +import maps.convert.ConvertStep; + +/** + This class computes the entrances for buildings. +*/ +public class CreateEntrancesStep extends ConvertStep { + // private GMLMap gmlMap; + + /** + Construct a CreateEntrancesStep. + @param gmlMap The GMLMap to use. + */ + public CreateEntrancesStep(GMLMap gmlMap) { + super(); + // this.gmlMap = gmlMap; + } + + @Override + public String getDescription() { + return "Creating building entrances"; + } + + @Override + protected void step() { + /* + setProgressLimit(gmlMap.getFaces().size()); + int sharedCount = 0; + int corridorCount = 0; + for (GMLFace face : gmlMap.getFaces()) { + if (FaceType.BUILDING.equals(face.getFaceType())) { + // Look to see if we have any edges shared with a road + boolean found = false; + for (GMLDirectedEdge directedEdge : face.getEdges()) { + GMLEdge edge = directedEdge.getEdge(); + if (isSharedWithRoad(edge)) { + // Make the edge passable + // TO DO: Make part of the edge passable + // TO DO: Make more edges passable if this edge is too short + edge.setPassable(true); + found = true; + ++sharedCount; + break; + } + } + // If we couldn't find a shared edge then we need to create a corridor that connects an edge to a road. + if (!found) { + makeCorrider(face); + ++corridorCount; + } + } + bumpProgress(); + } + setStatus("Made " + sharedCount + " shared edges passable and created " + corridorCount + " corridors"); + */ + } + + /* + private boolean isSharedWithRoad(GMLEdge edge) { + for (GMLFace face : gmlMap.getAttachedFaces(edge)) { + if (FaceType.ROAD.equals(face.getFaceType())) { + return true; + } + } + return false; + } + + private void makeCorrider(GMLFace face) { + // Find an edge that is close to a road or intersection + GMLEdge bestBuildingEdge = null; + GMLEdge bestRoadEdge = null; + for (GMLDirectedEdge next : face.getEdges()) { + GMLEdge buildingEdge = next.getEdge(); + // Look for the nearest road or intersection edge + } + } + */ +} diff --git a/modules/maps/src/maps/convert/osm2gml/DirectedEdge.java b/modules/maps/src/maps/convert/osm2gml/DirectedEdge.java new file mode 100644 index 0000000000000000000000000000000000000000..0bfcf0b2cac156b28adad68413110467b057a5f4 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/DirectedEdge.java @@ -0,0 +1,126 @@ +package maps.convert.osm2gml; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +/** + A DirectedEdge is an edge with an orientation. + */ +public class DirectedEdge { + private Edge edge; + private boolean forward; + private Line2D line; + + /** + Construct a directed edge. + @param edge The underlying edge. + @param forward True if this directed edge is aligned with the underlying edge direction, false otherwise. + */ + public DirectedEdge(Edge edge, boolean forward) { + this.edge = edge; + this.forward = forward; + this.line = edge.getLine(); + if (!forward) { + line = new Line2D(line.getEndPoint(), line.getOrigin()); + } + } + + /** + Construct a directed edge. + @param edge The underlying edge. + @param start The start node. + */ + public DirectedEdge(Edge edge, Node start) { + this.edge = edge; + this.forward = start.equals(edge.getStart()); + this.line = edge.getLine(); + if (!forward) { + line = new Line2D(line.getEndPoint(), line.getOrigin()); + } + } + + /** + Get the underlying edge. + @return The underlying edge. + */ + public Edge getEdge() { + return edge; + } + + /** + Get the line represented by this edge. + @return The line. + */ + public Line2D getLine() { + return line; + } + + /** + Is this directed edge in the direction of the underlying edge? + @return True if this directed edge is aligned with the underlying edge direction, false otherwise. + */ + public boolean isForward() { + return forward; + } + + /** + Get the node at the start of the underlying edge. + @return The start node. + */ + public Node getStartNode() { + return forward ? edge.getStart() : edge.getEnd(); + } + + /** + Get the node at the end of the underlying edge. + @return The end node. + */ + public Node getEndNode() { + return forward ? edge.getEnd() : edge.getStart(); + } + + /** + Get the coordinates of the start of this edge. + @return The coordinates of the start of this edge. + */ + public Point2D getStartCoordinates() { + if (forward) { + return edge.getStart().getCoordinates(); + } + else { + return edge.getEnd().getCoordinates(); + } + } + + /** + Get the coordinates of the end of this edge. + @return The coordinates of the end of this edge. + */ + public Point2D getEndCoordinates() { + if (forward) { + return edge.getEnd().getCoordinates(); + } + else { + return edge.getStart().getCoordinates(); + } + } + + @Override + public String toString() { + return "DirectedEdge" + (forward ? "" : " backwards") + " along " + edge; + } + + @Override + public int hashCode() { + return edge.hashCode() ^ (forward ? 1 : 0); + } + + @Override + public boolean equals(Object o) { + if (o instanceof DirectedEdge) { + DirectedEdge e = (DirectedEdge)o; + return this.forward == e.forward && this.edge.equals(e.edge); + } + return false; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/DirectedEdgeShapeInfo.java b/modules/maps/src/maps/convert/osm2gml/DirectedEdgeShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..09bbd95b846eb8ebe2414a86231a9410b6aed770 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/DirectedEdgeShapeInfo.java @@ -0,0 +1,57 @@ +package maps.convert.osm2gml; + +import java.awt.Color; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.misc.geometry.Line2D; + +/** + A ShapeInfo that knows how to draw DirectedEdges. +*/ +public class DirectedEdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo { + private Collection<DirectedEdge> edges; + + /** + Create a new DirectedEdgeShapeInfo. + @param edge The edge to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + */ + public DirectedEdgeShapeInfo(DirectedEdge edge, String name, Color colour, boolean thick) { + this(Collections.singleton(edge), name, colour, thick); + } + + /** + Create a new DirectedEdgeShapeInfo. + @param edges The edges to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + */ + public DirectedEdgeShapeInfo(Collection<DirectedEdge> edges, String name, Color colour, boolean thick) { + super(makeLines(edges), name, colour, thick, true); + this.edges = edges; + } + + @Override + public Object getObject() { + return edges; + } + + private static Collection<Line2D> makeLines(Collection<DirectedEdge> edges) { + if (edges == null) { + return null; + } + Collection<Line2D> result = new ArrayList<Line2D>(); + for (DirectedEdge next : edges) { + result.add(next.getLine()); + } + return result; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/Edge.java b/modules/maps/src/maps/convert/osm2gml/Edge.java new file mode 100644 index 0000000000000000000000000000000000000000..9c1175e42fb5d36be7af06da9afb953155e7d97e --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/Edge.java @@ -0,0 +1,61 @@ +package maps.convert.osm2gml; + +import rescuecore2.misc.geometry.Line2D; + +/** + An edge. An edge is a line between two nodes. + */ +public class Edge extends ManagedObject { + private Node start; + private Node end; + private Line2D line; + + /** + Construct a new Edge. + @param id The ID of this object. + @param start The start node. + @param end The end node. + */ + public Edge(long id, Node start, Node end) { + super(id); + this.start = start; + this.end = end; + line = new Line2D(start.getCoordinates(), end.getCoordinates()); + } + + /** + Get the start node. + @return The start node. + */ + public Node getStart() { + return start; + } + + /** + Get the end node. + @return The end node. + */ + public Node getEnd() { + return end; + } + + /** + Get the line represented by this edge. + @return The line. + */ + public Line2D getLine() { + return line; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("Edge "); + result.append(getID()); + result.append(" from "); + result.append(start); + result.append(" to "); + result.append(end); + return result.toString(); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/EdgeShapeInfo.java b/modules/maps/src/maps/convert/osm2gml/EdgeShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..96c987cf482fe576f7260b8ceffb648fa5fae1a7 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/EdgeShapeInfo.java @@ -0,0 +1,59 @@ +package maps.convert.osm2gml; + +import java.awt.Color; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.misc.geometry.Line2D; + +/** + A ShapeInfo that knows how to draw Edges. +*/ +public class EdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo { + private Collection<Edge> edges; + + /** + Create a new EdgeShapeInfo. + @param edge The edge to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + @param arrow Whether to draw the edge's direction or not. + */ + public EdgeShapeInfo(Edge edge, String name, Color colour, boolean thick, boolean arrow) { + this(Collections.singleton(edge), name, colour, thick, arrow); + } + + /** + Create a new EdgeShapeInfo. + @param edges The edges to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + @param arrow Whether to draw the edge's direction or not. + */ + public EdgeShapeInfo(Collection<Edge> edges, String name, Color colour, boolean thick, boolean arrow) { + super(makeLines(edges), name, colour, thick, arrow); + this.edges = edges; + } + + @Override + public Object getObject() { + return edges; + } + + private static Collection<Line2D> makeLines(Collection<Edge> edges) { + if (edges == null) { + return null; + } + Collection<Line2D> result = new ArrayList<Line2D>(); + for (Edge next : edges) { + result.add(next.getLine()); + } + return result; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/MakeObjectsStep.java b/modules/maps/src/maps/convert/osm2gml/MakeObjectsStep.java new file mode 100644 index 0000000000000000000000000000000000000000..7ffe31fdfa008500e67bdfa3528f37a7e3de5ef2 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/MakeObjectsStep.java @@ -0,0 +1,103 @@ +package maps.convert.osm2gml; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLShape; +import maps.convert.ConvertStep; +import maps.ScaleConversion; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; + +/** + This step creates the final GML objects. +*/ +public class MakeObjectsStep extends ConvertStep { + private TemporaryMap map; + private GMLMap gmlMap; + + /** + Construct a MakeObjectsStep. + @param map The TemporaryMap to read. + @param gmlMap The GMLMap to populate. + */ + public MakeObjectsStep(TemporaryMap map, GMLMap gmlMap) { + super(); + this.map = map; + this.gmlMap = gmlMap; + } + + @Override + public String getDescription() { + return "Generating GML objects"; + } + + @Override + protected void step() { + double xMin = Double.POSITIVE_INFINITY; + double yMin = Double.POSITIVE_INFINITY; + for (Node next : map.getAllNodes()) { + xMin = Math.min(xMin, next.getX()); + yMin = Math.min(yMin, next.getY()); + } + double sizeOf1m = ConvertTools.sizeOf1Metre(map.getOSMMap()); + double scale = 1.0 / sizeOf1m; + ScaleConversion conversion = new ScaleConversion(xMin, yMin, scale, scale); + Collection<Node> nodes = map.getAllNodes(); + Collection<Edge> edges = map.getAllEdges(); + setProgressLimit(nodes.size() + edges.size() + (map.getAllObjects().size() * 2)); + Map<Node, GMLNode> nodeMap = new HashMap<Node, GMLNode>(); + Map<Edge, GMLEdge> edgeMap = new HashMap<Edge, GMLEdge>(); + Map<TemporaryObject, GMLShape> shapeMap = new HashMap<TemporaryObject, GMLShape>(); + for (Node n : nodes) { + GMLNode node = gmlMap.createNode(conversion.convertX(n.getX()), conversion.convertY(n.getY())); + nodeMap.put(n, node); + bumpProgress(); + } + for (Edge e : edges) { + GMLNode first = nodeMap.get(e.getStart()); + GMLNode second = nodeMap.get(e.getEnd()); + GMLEdge edge = gmlMap.createEdge(first, second); + edgeMap.put(e, edge); + bumpProgress(); + } + for (TemporaryBuilding b : map.getBuildings()) { + shapeMap.put(b, gmlMap.createBuilding(makeEdges(b, edgeMap))); + bumpProgress(); + } + for (TemporaryRoad r : map.getRoads()) { + shapeMap.put(r, gmlMap.createRoad(makeEdges(r, edgeMap))); + bumpProgress(); + } + for (TemporaryIntersection i : map.getIntersections()) { + shapeMap.put(i, gmlMap.createRoad(makeEdges(i, edgeMap))); + bumpProgress(); + } + // Generate neighbour information + for (TemporaryObject o : map.getAllObjects()) { + GMLShape s = shapeMap.get(o); + for (DirectedEdge e : o.getEdges()) { + TemporaryObject neighbour = o.getNeighbour(e); + if (neighbour != null) { + s.setNeighbour(edgeMap.get(e.getEdge()), shapeMap.get(neighbour).getID()); + } + } + bumpProgress(); + } + setStatus("Created " + gmlMap.getRoads().size() + " roads and " + gmlMap.getBuildings().size() + " buildings"); + } + + private List<GMLDirectedEdge> makeEdges(TemporaryObject o, Map<Edge, GMLEdge> edgeMap) { + List<DirectedEdge> oldEdges = o.getEdges(); + List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(oldEdges.size()); + for (DirectedEdge dEdge : oldEdges) { + result.add(new GMLDirectedEdge(edgeMap.get(dEdge.getEdge()), dEdge.isForward())); + } + return result; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/MakeTempObjectsStep.java b/modules/maps/src/maps/convert/osm2gml/MakeTempObjectsStep.java new file mode 100644 index 0000000000000000000000000000000000000000..5ebe1d631f7cfe51b109e04ad8ab0f137a57d3c5 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/MakeTempObjectsStep.java @@ -0,0 +1,97 @@ +package maps.convert.osm2gml; + +import maps.convert.ConvertStep; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +import rescuecore2.misc.geometry.Point2D; + +/** + This step creates TemporaryObjects from the OSM data. +*/ +public class MakeTempObjectsStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a MakeTempObjectsStep. + @param map The TemporaryMap to populate. + */ + public MakeTempObjectsStep(TemporaryMap map) { + super(); + this.map = map; + } + + @Override + public String getDescription() { + return "Generating temporary objects"; + } + + @Override + protected void step() { + Collection<OSMRoadInfo> roads = map.getOSMRoadInfo(); + Collection<OSMIntersectionInfo> intersections = map.getOSMIntersectionInfo(); + Collection<OSMBuildingInfo> buildings = map.getOSMBuildingInfo(); + setProgressLimit(roads.size() + intersections.size() + buildings.size()); + generateRoadObjects(roads); + generateIntersectionObjects(intersections); + generateBuildingObjects(buildings); + setStatus("Created " + map.getRoads().size() + " roads, " + map.getIntersections().size() + " intersections, " + map.getBuildings().size() + " buildings"); + } + + private void generateRoadObjects(Collection<OSMRoadInfo> roads) { + for (OSMRoadInfo road : roads) { + if (road.getArea() != null) { + List<DirectedEdge> edges = generateEdges(road); + if (edges.size() > 2) { + map.addRoad(new TemporaryRoad(edges)); + } + } + bumpProgress(); + } + } + + private void generateIntersectionObjects(Collection<OSMIntersectionInfo> intersections) { + for (OSMIntersectionInfo intersection : intersections) { + if (intersection.getArea() != null) { + List<DirectedEdge> edges = generateEdges(intersection); + if (edges.size() > 2) { + map.addIntersection(new TemporaryIntersection(edges)); + } + } + bumpProgress(); + } + } + + private void generateBuildingObjects(Collection<OSMBuildingInfo> buildings) { + for (OSMBuildingInfo building : buildings) { + if (building.getArea() != null) { + List<DirectedEdge> edges = generateEdges(building); + if (edges.size() > 2) { + map.addBuilding(new TemporaryBuilding(edges, building.getBuildingID())); + } + } + bumpProgress(); + } + } + + private List<DirectedEdge> generateEdges(OSMShape s) { + List<DirectedEdge> result = new ArrayList<DirectedEdge>(); + Iterator<Point2D> it = s.getVertices().iterator(); + Node first = map.getNode(it.next()); + Node previous = first; + while (it.hasNext()) { + Node n = map.getNode(it.next()); + if (!n.equals(previous)) { + result.add(map.getDirectedEdge(previous, n)); + previous = n; + } + } + if (!previous.equals(first)) { + result.add(map.getDirectedEdge(previous, first)); + } + return result; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/ManagedObject.java b/modules/maps/src/maps/convert/osm2gml/ManagedObject.java new file mode 100644 index 0000000000000000000000000000000000000000..19caeb99f6492b9a4270f6efbf56d176830dd10d --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/ManagedObject.java @@ -0,0 +1,37 @@ +package maps.convert.osm2gml; + +/** + A managed map object. +*/ +public abstract class ManagedObject { + private long id; + + /** + Construct a managed object. + @param id The id of the object. + */ + protected ManagedObject(long id) { + this.id = id; + } + + /** + Get this object's ID. + @return The object ID. + */ + public long getID() { + return id; + } + + @Override + public int hashCode() { + return (int)id; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ManagedObject) { + return this.id == ((ManagedObject)o).id; + } + return false; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/MergeShapesStep.java b/modules/maps/src/maps/convert/osm2gml/MergeShapesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..82c14aea5ab4f7c4781d131d3545d2655204a91f --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/MergeShapesStep.java @@ -0,0 +1,159 @@ +package maps.convert.osm2gml; + +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; + +import java.awt.Color; + +import maps.convert.ConvertStep; + +/** + This class merges adjacent shapes of the same type. +*/ +public class MergeShapesStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a MergeShapesStep. + @param map The TemporaryMap to use. + */ + public MergeShapesStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Merging adjacent shapes"; + } + + @Override + protected void step() { + debug.setBackground(ConvertTools.getAllDebugShapes(map)); + Collection<TemporaryBuilding> buildings = map.getBuildings(); + Collection<TemporaryRoad> roads = map.getRoads(); + setProgressLimit(buildings.size() + roads.size()); + // Merge any buildings with the same ID that got split earlier. + int buildingCount = 0; + int roadCount = 0; + for (TemporaryBuilding next : buildings) { + if (tryToMerge(next)) { + ++buildingCount; + } + bumpProgress(); + } + // Try merging adjacent roads + for (TemporaryRoad next : roads) { + if (tryToMerge(next)) { + ++roadCount; + } + bumpProgress(); + } + setStatus("Merged " + buildingCount + " building shapes and " + roadCount + " road shapes"); + } + + private boolean tryToMerge(TemporaryBuilding b) { + if (!map.getBuildings().contains(b)) { + return false; + } + Collection<TemporaryBuilding> others = map.getBuildings(); + for (TemporaryBuilding other : others) { + if (b == other) { + continue; + } + if (other.getBuildingID() == b.getBuildingID()) { + List<DirectedEdge> boundary = mergeShapes(b, other); + if (boundary == null) { + continue; + } + TemporaryBuilding newBuilding = new TemporaryBuilding(boundary, b.getBuildingID()); + map.addBuilding(newBuilding); + map.removeBuilding(b); + map.removeBuilding(other); + debug.show("Merged buildings", new TemporaryObjectInfo(b, "First", Color.BLACK, Color.GREEN), + new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE), + new TemporaryObjectInfo(newBuilding, "New building", Color.BLUE, null)); + return true; + } + } + return false; + } + + private boolean tryToMerge(TemporaryRoad r) { + if (!map.getRoads().contains(r)) { + return false; + } + Collection<TemporaryRoad> others = map.getRoads(); + for (TemporaryRoad other : others) { + if (r == other) { + continue; + } + List<DirectedEdge> boundary = mergeShapes(r, other); + if (boundary == null) { + continue; + } + // Check for convexity + if (ConvertTools.isConvex(boundary)) { + TemporaryRoad newRoad = new TemporaryRoad(boundary); + map.addRoad(newRoad); + map.removeRoad(r); + map.removeRoad(other); + debug.show("Merged roads", new TemporaryObjectInfo(r, "First", Color.BLACK, Color.GREEN), + new TemporaryObjectInfo(other, "Second", Color.BLACK, Color.WHITE), + new TemporaryObjectInfo(newRoad, "New road", Color.BLUE, null)); + return true; + } + } + return false; + } + + private List<DirectedEdge> mergeShapes(TemporaryObject first, TemporaryObject second) { + Map<Edge, DirectedEdge> edges1 = new HashMap<Edge, DirectedEdge>(); + Map<Edge, DirectedEdge> edges2 = new HashMap<Edge, DirectedEdge>(); + for (DirectedEdge e : first.getEdges()) { + edges1.put(e.getEdge(), e); + } + for (DirectedEdge e : second.getEdges()) { + edges2.put(e.getEdge(), e); + } + if (Collections.disjoint(edges1.keySet(), edges2.keySet())) { + return null; + } + Set<DirectedEdge> boundary = new HashSet<DirectedEdge>(); + for (Map.Entry<Edge, DirectedEdge> next : edges1.entrySet()) { + if (!edges2.containsKey(next.getKey())) { + boundary.add(next.getValue()); + } + } + for (Map.Entry<Edge, DirectedEdge> next : edges2.entrySet()) { + if (!edges1.containsKey(next.getKey())) { + boundary.add(next.getValue()); + } + } + // Walk the boundary + DirectedEdge start = boundary.iterator().next(); + List<DirectedEdge> result = new ArrayList<DirectedEdge>(); + result.add(start); + while (!boundary.isEmpty()) { + start = findNextEdge(start, boundary); + boundary.remove(start); + result.add(start); + } + return result; + } + + private DirectedEdge findNextEdge(DirectedEdge from, Set<DirectedEdge> candidates) { + Node n = from.getEndNode(); + for (DirectedEdge next : candidates) { + if (next.getStartNode().equals(n)) { + return next; + } + } + throw new IllegalArgumentException("No candidate edge starting from " + n); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/Node.java b/modules/maps/src/maps/convert/osm2gml/Node.java new file mode 100644 index 0000000000000000000000000000000000000000..2bdcd22610635d6f0e97d4bb10b2f35fa4685164 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/Node.java @@ -0,0 +1,59 @@ +package maps.convert.osm2gml; + +import rescuecore2.misc.geometry.Point2D; + +/** + A node object. + */ +public class Node extends ManagedObject { + private Point2D coordinates; + + /** + Construct a new node. + @param id The ID of this node. + @param x The x coordinate of this node. + @param y The y coordinate of this node. + */ + public Node(long id, double x, double y) { + this(id, new Point2D(x, y)); + } + + /** + Construct a new node. + @param id The ID of this node. + @param coordinates The coordinates of this node. + */ + public Node(long id, Point2D coordinates) { + super(id); + this.coordinates = coordinates; + } + + /** + Get the coordinates of this node. + @return The node coordinates. + */ + public Point2D getCoordinates() { + return coordinates; + } + + /** + Get the X coordinate. + @return The X coordinate. + */ + public double getX() { + return coordinates.getX(); + } + + /** + Get the Y coordinate. + @return The Y coordinate. + */ + public double getY() { + return coordinates.getY(); + } + + @Override + public String toString() { + return "Node " + getID() + " at " + coordinates; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/OSMBuildingInfo.java b/modules/maps/src/maps/convert/osm2gml/OSMBuildingInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..08e749c644b97a1add17084bea54b55a91f4b203 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/OSMBuildingInfo.java @@ -0,0 +1,80 @@ +package maps.convert.osm2gml; + +import maps.osm.OSMBuilding; +import maps.osm.OSMMap; +import maps.osm.OSMNode; + +import rescuecore2.misc.geometry.Point2D; + +import java.awt.geom.Area; +import java.awt.geom.Path2D; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + Information about an OSM building. +*/ +public class OSMBuildingInfo implements OSMShape { + private List<Point2D> vertices; + private Area area; + private long buildingID; + + /** + Construct a new OSMBuildingInfo. + @param building The building. + @param map The map. + */ + public OSMBuildingInfo(OSMBuilding building, OSMMap map) { + buildingID = building.getID(); + vertices = new ArrayList<Point2D>(); + for (Long next : building.getNodeIDs()) { + OSMNode node = map.getNode(next); + vertices.add(new Point2D(node.getLongitude(), node.getLatitude())); + } + // Compute the area + Path2D.Double path = new Path2D.Double(); + Iterator<Point2D> it = vertices.iterator(); + Point2D point = it.next(); + path.moveTo(point.getX(), point.getY()); + while (it.hasNext()) { + point = it.next(); + path.lineTo(point.getX(), point.getY()); + } + path.closePath(); + area = new Area(path.createTransformedShape(null)); + } + + @Override + public List<Point2D> getVertices() { + return vertices; + } + + @Override + public Area getArea() { + return area; + } + + /** + Get the ID of the building. + @return The building ID. + */ + public long getBuildingID() { + return buildingID; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("BuildingInfo ["); + for (Iterator<Point2D> it = vertices.iterator(); it.hasNext();) { + result.append(it.next().toString()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/OSMIntersectionInfo.java b/modules/maps/src/maps/convert/osm2gml/OSMIntersectionInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..0a3174cdb11088b8eb6476e79a74c47dbf05f5bd --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/OSMIntersectionInfo.java @@ -0,0 +1,322 @@ +package maps.convert.osm2gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Collections; +import java.util.Comparator; + +import java.awt.geom.Area; +import java.awt.geom.Path2D; + +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +//import rescuecore2.misc.gui.ShapeDebugFrame; +//import java.awt.Color; + +import maps.osm.OSMNode; + +/** + Information about an OSM intersection. +*/ +public class OSMIntersectionInfo implements OSMShape { + // private static ShapeDebugFrame debug = new ShapeDebugFrame(); + + private OSMNode centre; + private List<RoadAspect> roads; + private List<Point2D> vertices; + private Area area; + + /** + Create an IntersectionInfo. + @param centre The OSMNode at the centre of the intersection. + */ + public OSMIntersectionInfo(OSMNode centre) { + this.centre = centre; + roads = new ArrayList<RoadAspect>(); + } + + /** + Add an incoming road. + @param road The incoming road. + */ + public void addRoadSegment(OSMRoadInfo road) { + if (road.getFrom() == centre && road.getTo() == centre) { + System.out.println("Degenerate road found"); + } + else { + roads.add(new RoadAspect(road, centre)); + } + } + + /** + Process this intersection and determine the vertices and area it covers. + @param sizeOf1m The size of 1m in latitude/longitude. + */ + public void process(double sizeOf1m) { + vertices = new ArrayList<Point2D>(); + if (roads.size() > 1) { + processRoads(sizeOf1m); + } + else { + processSingleRoad(sizeOf1m); + area = null; + } + } + + /** + Get the OSMNode at the centre of this intersection. + @return The OSMNode at the centre. + */ + public OSMNode getCentre() { + return centre; + } + + @Override + public Area getArea() { + return area; + } + + @Override + public List<Point2D> getVertices() { + return vertices; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("IntersectionInfo (centre "); + result.append(centre); + result.append(") ["); + for (Iterator<Point2D> it = vertices.iterator(); it.hasNext();) { + result.append(it.next().toString()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("]"); + if (area == null) { + result.append(" (degenerate)"); + } + return result.toString(); + } + + private void processRoads(double sizeOf1m) { + // Sort incoming roads counterclockwise about the centre + Point2D centrePoint = new Point2D(centre.getLongitude(), centre.getLatitude()); + CounterClockwiseSort sort = new CounterClockwiseSort(centrePoint); + Collections.sort(roads, sort); + // Go through each pair of adjacent incoming roads and compute the two intersection points + Iterator<RoadAspect> it = roads.iterator(); + RoadAspect first = it.next(); + RoadAspect previous = first; + while (it.hasNext()) { + RoadAspect next = it.next(); + Point2D p = findIncomingRoadIntersection(previous, next, centrePoint, sizeOf1m); + vertices.add(p); + previous = next; + } + Point2D p = findIncomingRoadIntersection(previous, first, centrePoint, sizeOf1m); + vertices.add(p); + // If there are multiple vertices then compute the area + if (vertices.size() > 2) { + Iterator<Point2D> ix = vertices.iterator(); + Point2D point = ix.next(); + Path2D.Double path = new Path2D.Double(); + path.moveTo(point.getX(), point.getY()); + while (ix.hasNext()) { + point = ix.next(); + path.lineTo(point.getX(), point.getY()); + } + path.closePath(); + area = new Area(path.createTransformedShape(null)); + } + else { + area = null; + } + } + + /** + Process two incoming roads and find the intersection of the right edge of the first road and the left edge of the second road. + @param first The road to check the right edge of. + @param second The road the check the left edge of. + @param centrePoint The centre of the intersection. + @return The intersection of the two roads. + */ + private Point2D findIncomingRoadIntersection(RoadAspect first, RoadAspect second, Point2D centrePoint, double sizeOf1m) { + OSMNode firstNode = first.getFarNode(); + OSMNode secondNode = second.getFarNode(); + // Find the intersection of the incoming road edges + Point2D firstPoint = new Point2D(firstNode.getLongitude(), firstNode.getLatitude()); + Point2D secondPoint = new Point2D(secondNode.getLongitude(), secondNode.getLatitude()); + Vector2D firstVector = centrePoint.minus(firstPoint); + Vector2D secondVector = centrePoint.minus(secondPoint); + Vector2D firstNormal = firstVector.getNormal().normalised().scale(-Constants.ROAD_WIDTH * sizeOf1m / 2); + Vector2D secondNormal = secondVector.getNormal().normalised().scale(Constants.ROAD_WIDTH * sizeOf1m / 2); + Point2D start1Point = firstPoint.plus(firstNormal); + Point2D start2Point = secondPoint.plus(secondNormal); + Line2D line1 = new Line2D(start1Point, firstVector); + Line2D line2 = new Line2D(start2Point, secondVector); + Point2D intersection = GeometryTools2D.getIntersectionPoint(line1, line2); + if (intersection == null) { + // Lines are parallel + // This means the normals are parallel, so we can just add a normal to the centre point to generate an intersection point + intersection = centrePoint.plus(firstNormal); + } + first.setRightEnd(intersection); + second.setLeftEnd(intersection); + + /* + List<ShapeDebugFrame.ShapeInfo> shapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(firstPoint, centrePoint), "First road", Color.BLUE, false, false)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(firstPoint, firstNormal), "First road offset", Color.YELLOW, false, false)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start1Point, "Left start", Color.BLUE, true)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line1, "Left edge", Color.BLUE, true, false)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(secondPoint, centrePoint), "Second road", Color.WHITE, false, false)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(secondPoint, secondNormal), "Second road offset", Color.CYAN, false, false)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start2Point, "Right start", Color.WHITE, true)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line2, "Right edge", Color.WHITE, true, false)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.ORANGE, true)); + debug.show("Intersection", shapes); + */ + + return intersection; + } + + /** + This "intersection" has a single incoming road. Set the incoming road's left and right edges. + */ + private void processSingleRoad(double sizeOf1m) { + Point2D centrePoint = new Point2D(centre.getLongitude(), centre.getLatitude()); + RoadAspect road = roads.iterator().next(); + OSMNode node = road.getFarNode(); + Point2D nodePoint = new Point2D(node.getLongitude(), node.getLatitude()); + Vector2D nodeVector = centrePoint.minus(nodePoint); + Vector2D nodeNormal = nodeVector.getNormal().normalised().scale(-Constants.ROAD_WIDTH * sizeOf1m / 2); + Vector2D nodeNormal2 = nodeNormal.scale(-1); + Point2D start1Point = nodePoint.plus(nodeNormal); + Point2D start2Point = nodePoint.plus(nodeNormal2); + Line2D line1 = new Line2D(start1Point, nodeVector); + Line2D line2 = new Line2D(start2Point, nodeVector); + Point2D end1 = line1.getPoint(1); + Point2D end2 = line2.getPoint(1); + road.setRightEnd(end1); + road.setLeftEnd(end2); + + /* + List<ShapeDebugFrame.ShapeInfo> shapes = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, centrePoint), "Single road", Color.BLUE, false)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, nodeNormal), "Offset 1", Color.YELLOW, false)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(new Line2D(nodePoint, nodeNormal2), "Offset 2", Color.CYAN, false)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start1Point, "Left start", Color.BLUE, true)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line1, "Left edge", Color.BLUE, true)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(start2Point, "Right start", Color.WHITE, true)); + shapes.add(new ShapeDebugFrame.Line2DShapeInfo(line2, "Right edge", Color.WHITE, true)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(end1, "Endpoint 1", Color.ORANGE, true)); + shapes.add(new ShapeDebugFrame.Point2DShapeInfo(end2, "Endpoint 2", Color.PINK, true)); + debug.show(shapes); + */ + } + + private static class RoadAspect { + private boolean forward; + private OSMRoadInfo road; + + RoadAspect(OSMRoadInfo road, OSMNode intersection) { + this.road = road; + forward = intersection == road.getTo(); + } + + OSMRoadInfo getRoad() { + return road; + } + + OSMNode getFarNode() { + return forward ? road.getFrom() : road.getTo(); + } + + void setLeftEnd(Point2D p) { + if (forward) { + road.setToLeft(p); + } + else { + road.setFromRight(p); + } + } + + void setRightEnd(Point2D p) { + if (forward) { + road.setToRight(p); + } + else { + road.setFromLeft(p); + } + } + } + + private static class CounterClockwiseSort implements Comparator<RoadAspect> { + private Point2D centre; + + /** + Construct a CounterClockwiseSort with a reference point. + @param centre The reference point. + */ + public CounterClockwiseSort(Point2D centre) { + this.centre = centre; + } + + @Override + public int compare(RoadAspect first, RoadAspect second) { + double d1 = score(first); + double d2 = score(second); + if (d1 < d2) { + return 1; + } + else if (d1 > d2) { + return -1; + } + else { + return 0; + } + } + + /** + Compute the score for a RoadAspect - the amount of clockwiseness from 12 o'clock. + @param aspect The RoadAspect. + @return The amount of clockwiseness. This will be in the range [0..4) with 0 representing 12 o'clock, 1 representing 3 o'clock and so on. + */ + public double score(RoadAspect aspect) { + OSMNode node = aspect.getFarNode(); + Point2D point = new Point2D(node.getLongitude(), node.getLatitude()); + Vector2D v = point.minus(centre); + double sin = v.getX() / v.getLength(); + double cos = v.getY() / v.getLength(); + if (Double.isNaN(sin) || Double.isNaN(cos)) { + System.out.println(v); + System.out.println(v.getLength()); + } + return convert(sin, cos); + } + + // CHECKSTYLE:OFF:MagicNumber + private double convert(double sin, double cos) { + if (sin >= 0 && cos >= 0) { + return sin; + } + if (sin >= 0 && cos < 0) { + return 2 - sin; + } + if (sin < 0 && cos < 0) { + return 2 - sin; + } + if (sin < 0 && cos >= 0) { + return 4 + sin; + } + throw new IllegalArgumentException("This should be impossible! What's going on? sin=" + sin + ", cos=" + cos); + } + // CHECKSTYLE:ON:MagicNumber + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/OSMRoadInfo.java b/modules/maps/src/maps/convert/osm2gml/OSMRoadInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..2a0c98846ccc02ca2e401b0674a18de0643e12db --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/OSMRoadInfo.java @@ -0,0 +1,162 @@ +package maps.convert.osm2gml; + +import rescuecore2.misc.geometry.Point2D; + +import maps.osm.OSMNode; + +import java.awt.geom.Area; +import java.awt.geom.Path2D; + +import java.util.List; +import java.util.ArrayList; + +/** + Information about an OSM road. +*/ +public class OSMRoadInfo implements OSMShape { + private OSMNode from; + private OSMNode to; + private Point2D fromLeft; + private Point2D toLeft; + private Point2D fromRight; + private Point2D toRight; + private Area area; + + /** + Create an OSMRoadInfo between two nodes. + @param from The first OSMNode. + @param to The second OSMNode. + */ + public OSMRoadInfo(OSMNode from, OSMNode to) { + this.from = from; + this.to = to; + area = null; + } + + /** + Get the "from" node. + @return The "from" node. + */ + public OSMNode getFrom() { + return from; + } + + /** + Get the "to" node. + @return The "to" node. + */ + public OSMNode getTo() { + return to; + } + + /** + Set the point that is at the left side of this road at the "from" end. + @param p The from-left corner point. + */ + public void setFromLeft(Point2D p) { + fromLeft = p; + area = null; + } + + /** + Set the point that is at the right side of this road at the "from" end. + @param p The from-right corner point. + */ + public void setFromRight(Point2D p) { + fromRight = p; + area = null; + } + + /** + Set the point that is at the left side of this road at the "to" end. + @param p The to-left corner point. + */ + public void setToLeft(Point2D p) { + toLeft = p; + area = null; + } + + /** + Set the point that is at the right side of this road at the "to" end. + @param p The to-right corner point. + */ + public void setToRight(Point2D p) { + toRight = p; + area = null; + } + + /** + Get the point that is at the left side of this road at the "from" end. + @return The from-left corner point. + */ + public Point2D getFromLeft() { + return fromLeft; + } + + /** + Get the point that is at the right side of this road at the "from" end. + @return The from-right corner point. + */ + public Point2D getFromRight() { + return fromRight; + } + + /** + Get the point that is at the left side of this road at the "to" end. + @return The to-left corner point. + */ + public Point2D getToLeft() { + return toLeft; + } + + /** + Get the point that is at the right side of this road at the "to" end. + @return The to-right corner point. + */ + public Point2D getToRight() { + return toRight; + } + + @Override + public Area getArea() { + if (area == null) { + if (fromLeft == null || fromRight == null || toLeft == null || toRight == null) { + return null; + } + Path2D.Double path = new Path2D.Double(); + path.moveTo(fromLeft.getX(), fromLeft.getY()); + path.lineTo(fromRight.getX(), fromRight.getY()); + path.lineTo(toRight.getX(), toRight.getY()); + path.lineTo(toLeft.getX(), toLeft.getY()); + path.closePath(); + area = new Area(path.createTransformedShape(null)); + } + return area; + } + + @Override + public List<Point2D> getVertices() { + List<Point2D> result = new ArrayList<Point2D>(); + result.add(fromLeft); + result.add(fromRight); + result.add(toRight); + result.add(toLeft); + return result; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("RoadInfo ["); + result.append(fromLeft); + result.append(", "); + result.append(fromRight); + result.append(", "); + result.append(toRight); + result.append(", "); + result.append(toLeft); + result.append("]"); + return result.toString(); + } +} + diff --git a/modules/maps/src/maps/convert/osm2gml/OSMShape.java b/modules/maps/src/maps/convert/osm2gml/OSMShape.java new file mode 100644 index 0000000000000000000000000000000000000000..c61efd3af7e36bcd83db237cf1d63dd3ce1cf640 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/OSMShape.java @@ -0,0 +1,23 @@ +package maps.convert.osm2gml; + +import java.util.List; +import java.awt.geom.Area; + +import rescuecore2.misc.geometry.Point2D; + +/** + Interface for OSM object shapes. +*/ +public interface OSMShape { + /** + Get the vertices of this shape in clockwise order. + @return The vertices of this shape. + */ + List<Point2D> getVertices(); + + /** + Get the area covered by this shape. + @return The area of this shape. + */ + Area getArea(); +} diff --git a/modules/maps/src/maps/convert/osm2gml/PruneStep.java b/modules/maps/src/maps/convert/osm2gml/PruneStep.java new file mode 100644 index 0000000000000000000000000000000000000000..4f99e17f9fa93bd66cd686a2870410e26018c1a7 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/PruneStep.java @@ -0,0 +1,54 @@ +package maps.convert.osm2gml; + +import maps.gml.GMLMap; + +import maps.convert.ConvertStep; + +/** + This step removes extra nodes and edges. +*/ +public class PruneStep extends ConvertStep { + // private GMLMap gmlMap; + + /** + Construct a PruneStep. + @param gmlMap The GMLMap to use. + */ + public PruneStep(GMLMap gmlMap) { + super(); + // this.gmlMap = gmlMap; + } + + @Override + public String getDescription() { + return "Pruning nodes and edges"; + } + + @Override + protected void step() { + /* + setProgressLimit(gmlMap.getEdges().size() + gmlMap.getNodes().size()); + int edgeCount = 0; + int nodeCount = 0; + // Any edge that is not part of a face can be pruned + setStatus("Pruning edges"); + for (GMLEdge next : gmlMap.getEdges()) { + if (gmlMap.getAttachedFaces(next).isEmpty()) { + gmlMap.removeEdge(next); + ++edgeCount; + } + bumpProgress(); + } + // Any node that is not part of an edge can be pruned + setStatus("Pruning nodes"); + for (GMLNode next : gmlMap.getNodes()) { + if (gmlMap.getAttachedEdges(next).isEmpty()) { + gmlMap.removeNode(next); + ++nodeCount; + } + bumpProgress(); + } + setStatus("Removed " + edgeCount + " edges and " + nodeCount + " nodes"); + */ + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/RemoveShapesStep.java b/modules/maps/src/maps/convert/osm2gml/RemoveShapesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..c2ca7004ba662cdedc9cc4a3e51a046e13b8184c --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/RemoveShapesStep.java @@ -0,0 +1,128 @@ +package maps.convert.osm2gml; + +import java.awt.Color; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; + +import maps.convert.ConvertStep; + +import rescuecore2.log.Logger; + +/** + This step removes shapes that are duplicates or contained entirely inside another shape. +*/ +public class RemoveShapesStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a RemoveFacesStep. + @param map The TemporaryMap to use. + */ + public RemoveShapesStep(TemporaryMap map) { + super(); + this.map = map; + } + + @Override + public String getDescription() { + return "Removing extraneous shapes"; + } + + @Override + protected void step() { + debug.setBackground(ConvertTools.getAllDebugShapes(map)); + debug.setAutozoomEnabled(false); + Collection<TemporaryObject> allObjects = map.getAllObjects(); + setProgressLimit(allObjects.size() * 2); + Set<TemporaryObject> removed = new HashSet<TemporaryObject>(); + setStatus("Removing duplicate shapes"); + int duplicateCount = 0; + int interiorCount = 0; + Logger.debug("Removing building duplicates"); + duplicateCount += removeDuplicates(map.getBuildings(), removed, allObjects); + Logger.debug("Removing intersection duplicates"); + duplicateCount += removeDuplicates(map.getIntersections(), removed, allObjects); + Logger.debug("Removing road duplicates"); + duplicateCount += removeDuplicates(map.getRoads(), removed, allObjects); + Logger.debug("Removing interior faces"); + setStatus("Removing interior faces"); + interiorCount += removeInterior(map.getRoads(), removed, allObjects); + interiorCount += removeInterior(map.getIntersections(), removed, allObjects); + interiorCount += removeInterior(map.getBuildings(), removed, allObjects); + setStatus("Removed " + removed.size() + " faces: " + duplicateCount + " duplicates and " + interiorCount + " interior"); + debug.clearBackground(); + debug.activate(); + debug.show("Result", ConvertTools.getAllDebugShapes(map)); + } + + /** + Remove all shapes that are duplicates of a test set. + @param test The set of objects to test against. + @param removed The set of removed objects. + @param toCheck The set of shapes to check. + @return The number of objects removed. + */ + private int removeDuplicates(Collection<? extends TemporaryObject> test, Set<TemporaryObject> removed, Collection<TemporaryObject> toCheck) { + int count = 0; + Logger.debug(test.size() + " test objects, " + toCheck.size() + " to check, " + removed.size() + " already removed"); + for (TemporaryObject first : test) { + bumpProgress(); + if (removed.contains(first)) { + continue; + } + Logger.debug("Next test object: " + first); + for (TemporaryObject second : toCheck) { + if (removed.contains(second)) { + continue; + } + if (first == second) { + continue; + } + Logger.debug("Next check object: " + second); + if (first.isDuplicate(second)) { + map.removeTemporaryObject(second); + removed.add(second); + ++count; + Logger.debug("Removed duplicate object: " + second + " is same as " + first); + } + debug.show("Checking for duplicates", + new TemporaryObjectInfo(first, "First", Color.WHITE, Constants.TRANSPARENT_LIME), + new TemporaryObjectInfo(second, "Second", Color.WHITE, Constants.TRANSPARENT_BLUE)); + } + } + return count; + } + + /** + Remove any shapes that are entirely inside another shapes. + @param toCheck The set of objects to check. + @param removed The set of removed objects. + @param allObjects All objects. + @return The number of removed objects. + */ + private int removeInterior(Collection<? extends TemporaryObject> toCheck, Set<TemporaryObject> removed, Collection<TemporaryObject> allObjects) { + int count = 0; + for (TemporaryObject first : toCheck) { + bumpProgress(); + if (removed.contains(first)) { + continue; + } + for (TemporaryObject second : allObjects) { + if (removed.contains(second)) { + continue; + } + if (first == second) { + continue; + } + if (first.isEntirelyInside(second)) { + map.removeTemporaryObject(first); + removed.add(first); + ++count; + } + } + } + return count; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/ScanOSMStep.java b/modules/maps/src/maps/convert/osm2gml/ScanOSMStep.java new file mode 100644 index 0000000000000000000000000000000000000000..5cceb45521ff6f7f8f2da92f704ec85ba7405623 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/ScanOSMStep.java @@ -0,0 +1,102 @@ +package maps.convert.osm2gml; + +import maps.osm.OSMMap; +import maps.osm.OSMNode; +import maps.osm.OSMRoad; +import maps.osm.OSMBuilding; + +import maps.convert.ConvertStep; + +import java.util.Map; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Iterator; + +/** + This step scans the OpenStreetMap data and generates information about roads, intersections and buildings. +*/ +public class ScanOSMStep extends ConvertStep { + private TemporaryMap map; + private Map<OSMNode, OSMIntersectionInfo> nodeToIntersection; + private List<OSMIntersectionInfo> intersections; + private List<OSMRoadInfo> roads; + private List<OSMBuildingInfo> buildings; + + /** + Construct a ScanOSMStep. + @param map The OSMMap to scan. + */ + public ScanOSMStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Scanning OpenStreetMap data"; + } + + @Override + protected void step() { + nodeToIntersection = new HashMap<OSMNode, OSMIntersectionInfo>(); + intersections = new ArrayList<OSMIntersectionInfo>(); + roads = new ArrayList<OSMRoadInfo>(); + buildings = new ArrayList<OSMBuildingInfo>(); + OSMMap osm = map.getOSMMap(); + setProgressLimit(osm.getRoads().size() + osm.getBuildings().size()); + setStatus("Scanning roads and buildings"); + scanRoads(); + scanBuildings(); + double sizeOf1m = ConvertTools.sizeOf1Metre(osm); + setStatus("Generating intersections"); + setProgressLimit(intersections.size()); + setProgress(0); + for (OSMIntersectionInfo next : intersections) { + next.process(sizeOf1m); + bumpProgress(); + } + setStatus("Created " + roads.size() + " roads, " + intersections.size() + " intersections, " + buildings.size() + " buildings"); + map.setOSMInfo(intersections, roads, buildings); + } + + private void scanRoads() { + OSMMap osm = map.getOSMMap(); + for (OSMRoad road : osm.getRoads()) { + Iterator<Long> it = road.getNodeIDs().iterator(); + OSMNode start = osm.getNode(it.next()); + while (it.hasNext()) { + OSMNode end = osm.getNode(it.next()); + if (start == end) { + System.out.println("Degenerate road: " + road.getID()); + continue; + } + OSMIntersectionInfo from = nodeToIntersection.get(start); + OSMIntersectionInfo to = nodeToIntersection.get(end); + if (from == null) { + from = new OSMIntersectionInfo(start); + nodeToIntersection.put(start, from); + intersections.add(from); + } + if (to == null) { + to = new OSMIntersectionInfo(end); + nodeToIntersection.put(end, to); + intersections.add(to); + } + OSMRoadInfo roadInfo = new OSMRoadInfo(start, end); + from.addRoadSegment(roadInfo); + to.addRoadSegment(roadInfo); + start = end; + roads.add(roadInfo); + } + bumpProgress(); + } + } + + private void scanBuildings() { + OSMMap osm = map.getOSMMap(); + for (OSMBuilding building : osm.getBuildings()) { + buildings.add(new OSMBuildingInfo(building, osm)); + bumpProgress(); + } + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/SplitIntersectingEdgesStep.java b/modules/maps/src/maps/convert/osm2gml/SplitIntersectingEdgesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..720f7aba3b04fa6cc9c44f07cb5014fb742132cf --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/SplitIntersectingEdgesStep.java @@ -0,0 +1,283 @@ +package maps.convert.osm2gml; + +import java.util.List; +import java.util.Deque; +import java.util.ArrayDeque; +import java.util.Set; +import java.util.HashSet; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; +//import rescuecore2.log.Logger; + +import maps.convert.ConvertStep; + +/** + This step splits any edges that intersect. +*/ +public class SplitIntersectingEdgesStep extends ConvertStep { + private TemporaryMap map; + private int splitCount; + private int inspectedCount; + private Deque<Edge> toCheck; + private Set<Edge> seen; + + /** + Construct a SplitIntersectingEdgesStep. + @param map The TemporaryMap to use. + */ + public SplitIntersectingEdgesStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Splitting intersecting edges"; + } + + @Override + protected void step() { + debug.setBackground(ConvertTools.getAllDebugShapes(map)); + toCheck = new ArrayDeque<Edge>(map.getAllEdges()); + seen = new HashSet<Edge>(); + setProgressLimit(toCheck.size()); + splitCount = 0; + inspectedCount = 0; + while (!toCheck.isEmpty()) { + Edge next = toCheck.pop(); + check(next); + ++inspectedCount; + setProgressLimit(toCheck.size() + inspectedCount); + bumpProgress(); + } + setStatus("Inspected " + inspectedCount + " edges and split " + splitCount); + } + + private void check(Edge e) { + if (!map.getAllEdges().contains(e)) { + // Logger.debug("Skipped edge " + e); + // debug.show("Skipped edge", new EdgeShapeInfo(e, "Skipped edge", Color.BLUE, true, false)); + return; + } + if (seen.contains(e)) { + return; + } + seen.add(e); + Line2D l1 = e.getLine(); + Set<Edge> edges = new HashSet<Edge>(map.getAllEdges()); + for (Edge test : edges) { + if (test.equals(e)) { + continue; + } + Line2D l2 = test.getLine(); + if (GeometryTools2D.parallel(l1, l2)) { + if (processParallelLines(e, test)) { + break; + } + } + else { + if (checkForIntersection(e, test)) { + break; + } + } + } + } + + /** + @return True if e1 was split. + */ + private boolean processParallelLines(Edge e1, Edge e2) { + // Possible cases: + // Shorter line entirely inside longer + // Shorter line overlaps longer at longer start + // Shorter line overlaps longer at longer end + // Shorter line start point is same as longer start and end point is inside + // Shorter line start point is same as longer end and end point is inside + // Shorter line end point is same as longer start and start point is inside + // Shorter line end point is same as longer end and start point is inside + Edge shorterEdge = e1; + Edge longerEdge = e2; + if (e1.getLine().getDirection().getLength() > e2.getLine().getDirection().getLength()) { + shorterEdge = e2; + longerEdge = e1; + } + Line2D shorter = shorterEdge.getLine(); + Line2D longer = longerEdge.getLine(); + boolean shortStartLongStart = shorterEdge.getStart() == longerEdge.getStart(); + boolean shortStartLongEnd = shorterEdge.getStart() == longerEdge.getEnd(); + boolean shortEndLongStart = shorterEdge.getEnd() == longerEdge.getStart(); + boolean shortEndLongEnd = shorterEdge.getEnd() == longerEdge.getEnd(); + boolean startInside = !shortStartLongStart && !shortStartLongEnd && GeometryTools2D.contains(longer, shorter.getOrigin()); + boolean endInside = !shortEndLongStart && !shortEndLongEnd && GeometryTools2D.contains(longer, shorter.getEndPoint()); + /* + if (startInside || endInside) { + ++overlapCount; + } + */ + if (startInside && endInside) { + processInternalEdge(shorterEdge, longerEdge); + return longerEdge == e1; + } + else if (startInside && !endInside) { + // Either full overlap or coincident end point + if (shortEndLongStart) { + processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getEnd()); + return longerEdge == e1; + } + else if (shortEndLongEnd) { + processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getEnd()); + return longerEdge == e1; + } + else { + // Full overlap + processOverlap(shorterEdge, longerEdge); + return true; + } + } + else if (endInside && !startInside) { + // Either full overlap or coincident end point + if (shortStartLongStart) { + processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getStart()); + return longerEdge == e1; + } + else if (shortStartLongEnd) { + processCoincidentNode(shorterEdge, longerEdge, shorterEdge.getStart()); + return longerEdge == e1; + } + else { + // Full overlap + processOverlap(shorterEdge, longerEdge); + return true; + } + } + return false; + } + + /** + @return true if first is split. + */ + private boolean checkForIntersection(Edge first, Edge second) { + Point2D intersection = GeometryTools2D.getSegmentIntersectionPoint(first.getLine(), second.getLine()); + // System.out.println(intersection); + if (intersection == null) { + // Maybe the intersection is within the map's "nearby" tolerance? + intersection = GeometryTools2D.getIntersectionPoint(first.getLine(), second.getLine()); + /* + debug.show("Split intersection", + new ShapeDebugFrame.Line2DShapeInfo(first, "Line 1", Color.ORANGE, true, false), + new ShapeDebugFrame.Line2DShapeInfo(second, "Line 2", Color.BLUE, true, false), + new ShapeDebugFrame.Point2DShapeInfo(intersection, "Near-miss intersection", Color.BLACK, false), + new ShapeDebugFrame.Line2DShapeInfo(firstObject.getLines(), "Object 1", Color.GREEN, false, false), + new ShapeDebugFrame.Line2DShapeInfo(secondObject.getLines(), "Object 2", Color.GRAY, false, false) + ); + */ + // Was this a near miss? + if (map.isNear(intersection, first.getStart().getCoordinates()) || map.isNear(intersection, first.getEnd().getCoordinates())) { + // Check that the intersection is actually somewhere on the second segment + double d = second.getLine().getIntersection(first.getLine()); + if (d < 0 || d > 1) { + // Nope. Ignore it. + return false; + } + } + else if (map.isNear(intersection, second.getStart().getCoordinates()) || map.isNear(intersection, second.getEnd().getCoordinates())) { + // Check that the intersection is actually somewhere on the first line segment + double d = first.getLine().getIntersection(second.getLine()); + if (d < 0 || d > 1) { + // Nope. Ignore it. + return false; + } + } + else { + // Not a near miss. + return false; + } + } + Node n = map.getNode(intersection); + // Split the two edges into 4 (maybe) + // Was the first edge split? + boolean splitFirst = !n.equals(first.getStart()) && !n.equals(first.getEnd()); + boolean splitSecond = !n.equals(second.getStart()) && !n.equals(second.getEnd()); + Set<Edge> newEdges = new HashSet<Edge>(); + if (splitFirst) { + List<Edge> e = map.splitEdge(first, n); + toCheck.addAll(e); + newEdges.addAll(e); + ++splitCount; + /* + Logger.debug("Split edge " + first); + debug.show("Split first line", + new ShapeDebugFrame.Line2DShapeInfo(first.getLine(), "Line 1", Color.ORANGE, true, false), + new EdgeShapeInfo(e, "New edges", Color.WHITE, false, true), + new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true) + ); + */ + } + if (splitSecond) { + List<Edge> e = map.splitEdge(second, n); + toCheck.addAll(e); + newEdges.addAll(e); + ++splitCount; + /* + Logger.debug("Split edge " + second); + debug.show("Split second line", + new ShapeDebugFrame.Line2DShapeInfo(second.getLine(), "Line 2", Color.BLUE, true, false), + new EdgeShapeInfo(e, "New edges", Color.WHITE, false, true), + new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true) + ); + */ + } + // if (splitFirst || splitSecond) { + /* + Logger.debug("First line: " + first + " -> " + first.getLine()); + Logger.debug("Second line: " + second + " -> " + second.getLine()); + Logger.debug("Intersection: " + intersection); + Logger.debug("New edges"); + for (Edge next : newEdges) { + Logger.debug(" " + next + " -> " + next.getLine()); + } + */ + // debug.show("Split intersection", + // new ShapeDebugFrame.Line2DShapeInfo(first.getLine(), "Line 1", Color.ORANGE, true, false), + // new ShapeDebugFrame.Line2DShapeInfo(second.getLine(), "Line 2", Color.BLUE, true, false), + // new EdgeShapeInfo(newEdges, "New edges", Color.WHITE, false, true), + // new ShapeDebugFrame.Point2DShapeInfo(intersection, "Intersection", Color.BLACK, true) + // ); + // } + return splitFirst; + } + + private void processInternalEdge(Edge shorter, Edge longer) { + // Split longer into (up to) three chunks + double t1 = GeometryTools2D.positionOnLine(longer.getLine(), shorter.getLine().getOrigin()); + double t2 = GeometryTools2D.positionOnLine(longer.getLine(), shorter.getLine().getEndPoint()); + Node first; + Node second; + if (t1 < t2) { + first = shorter.getStart(); + second = shorter.getEnd(); + } + else { + first = shorter.getEnd(); + second = shorter.getStart(); + } + toCheck.addAll(map.splitEdge(longer, first, second)); + ++splitCount; + } + + private void processCoincidentNode(Edge shorter, Edge longer, Node coincidentPoint) { + // Split the long edge at the non-coincident point + Node cutPoint = coincidentPoint.equals(shorter.getStart()) ? shorter.getEnd() : shorter.getStart(); + toCheck.addAll(map.splitEdge(longer, cutPoint)); + ++splitCount; + } + + private void processOverlap(Edge shorter, Edge longer) { + Node shortSplit = GeometryTools2D.contains(shorter.getLine(), longer.getLine().getOrigin()) ? longer.getStart() : longer.getEnd(); + Node longSplit = GeometryTools2D.contains(longer.getLine(), shorter.getLine().getOrigin()) ? shorter.getStart() : shorter.getEnd(); + toCheck.addAll(map.splitEdge(shorter, shortSplit)); + toCheck.addAll(map.splitEdge(longer, longSplit)); + ++splitCount; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/SplitShapesStep.java b/modules/maps/src/maps/convert/osm2gml/SplitShapesStep.java new file mode 100644 index 0000000000000000000000000000000000000000..e03d014bf1b80c3bae8d355e50d52cb5f5ff96bb --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/SplitShapesStep.java @@ -0,0 +1,115 @@ +package maps.convert.osm2gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; + +import maps.convert.ConvertStep; + +import rescuecore2.log.Logger; + +/** + This step splits any shapes that overlap. +*/ +public class SplitShapesStep extends ConvertStep { + private TemporaryMap map; + + /** + Construct a SplitFacesStep. + @param map The map to use. + */ + public SplitShapesStep(TemporaryMap map) { + this.map = map; + } + + @Override + public String getDescription() { + return "Splitting overlapping shapes"; + } + + @Override + protected void step() { + Collection<TemporaryObject> all = map.getAllObjects(); + setProgressLimit(all.size()); + int count = 0; + debug.setBackground(ConvertTools.getAllDebugShapes(map)); + for (TemporaryObject shape : all) { + count += splitShapeIfRequired(shape); + bumpProgress(); + } + setStatus("Added " + count + " new shapes"); + } + + private int splitShapeIfRequired(TemporaryObject shape) { + Set<DirectedEdge> edgesRemaining = new HashSet<DirectedEdge>(shape.getEdges()); + boolean firstShape = true; + int newShapeCount = 0; + // debug.show("Splitting shapes", new TemporaryObjectInfo(shape, "Shape", Constants.BLACK, Constants.TRANSPARENT_ORANGE)); + // Logger.debug("Splitting shape " + shape); + // Logger.debug("Edges: "); + // for (DirectedEdge e : edgesRemaining) { + // Logger.debug(" " + e); + // } + while (!edgesRemaining.isEmpty()) { + // Logger.debug(edgesRemaining.size() + " edges remaining"); + DirectedEdge dEdge = edgesRemaining.iterator().next(); + edgesRemaining.remove(dEdge); + Node start = dEdge.getStartNode(); + Node end = dEdge.getEndNode(); + List<DirectedEdge> result = new ArrayList<DirectedEdge>(); + result.add(dEdge); + // Now walk around + Logger.debug("Starting walk from " + dEdge); + Logger.debug("Start: " + start); + Logger.debug("End: " + end); + while (!end.equals(start)) { + Set<Edge> candidates = map.getAttachedEdges(end); + // Logger.debug("From edge: " + dEdge); + // Logger.debug("Candidates: "); + // for (Edge e : candidates) { + // Logger.debug(" " + e); + // } + Edge turn = ConvertTools.findLeftTurn(dEdge, candidates); + Logger.debug("Best turn: " + turn); + DirectedEdge newDEdge = new DirectedEdge(turn, end); + // debug.show("Splitting shapes", + // new TemporaryObjectInfo(shape, "Shape", Constants.BLACK, Constants.TRANSPARENT_ORANGE), + // new ShapeDebugFrame.Line2DShapeInfo(dEdge.getLine(), "Edge", Constants.BLUE, true, true), + // new ShapeDebugFrame.Line2DShapeInfo(newDEdge.getLine(), "Turn", Constants.RED, true, true)); + dEdge = newDEdge; + end = dEdge.getEndNode(); + edgesRemaining.remove(dEdge); + result.add(dEdge); + Logger.debug("Added " + dEdge); + Logger.debug("New end: " + end); + } + if (!firstShape || !edgesRemaining.isEmpty()) { + // Didn't cover all edges so new shapes are needed. + if (firstShape) { + map.removeTemporaryObject(shape); + firstShape = false; + } + else { + ++newShapeCount; + } + TemporaryObject newObject = null; + if (shape instanceof TemporaryRoad) { + newObject = new TemporaryRoad(result); + } + if (shape instanceof TemporaryIntersection) { + newObject = new TemporaryIntersection(result); + } + if (shape instanceof TemporaryBuilding) { + newObject = new TemporaryBuilding(result, ((TemporaryBuilding)shape).getBuildingID()); + } + map.addTemporaryObject(newObject); + // debug.show("Splitting shapes", + // new TemporaryObjectInfo(shape, "Original shape", Constants.BLACK, null), + // new TemporaryObjectInfo(newObject, "New shape", Constants.RED, Constants.TRANSPARENT_RED)); + } + } + return newShapeCount; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryBuilding.java b/modules/maps/src/maps/convert/osm2gml/TemporaryBuilding.java new file mode 100644 index 0000000000000000000000000000000000000000..f3b59738a7df71bdbec5bed0edf30a39f7fe041b --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryBuilding.java @@ -0,0 +1,28 @@ +package maps.convert.osm2gml; + +import java.util.List; + +/** + A temporary building during conversion. +*/ +public class TemporaryBuilding extends TemporaryObject { + private long id; + + /** + Construct a new TemporaryBuilding. + @param edges The edges of the building in counter-clockwise order. + @param id The ID of the OSM building that generated this data. + */ + public TemporaryBuilding(List<DirectedEdge> edges, long id) { + super(edges); + this.id = id; + } + + /** + Get the ID of the original OSM building. + @return The OSM building ID. + */ + public long getBuildingID() { + return id; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryIntersection.java b/modules/maps/src/maps/convert/osm2gml/TemporaryIntersection.java new file mode 100644 index 0000000000000000000000000000000000000000..c4839a1ed80427e49022b02224058f00dc30f404 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryIntersection.java @@ -0,0 +1,16 @@ +package maps.convert.osm2gml; + +import java.util.List; + +/** + A temporary intersection during conversion. +*/ +public class TemporaryIntersection extends TemporaryObject { + /** + Construct a new TemporaryIntersection. + @param edges The edges of the intersection in counter-clockwise order. + */ + public TemporaryIntersection(List<DirectedEdge> edges) { + super(edges); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryMap.java b/modules/maps/src/maps/convert/osm2gml/TemporaryMap.java new file mode 100644 index 0000000000000000000000000000000000000000..307b4d3bbf06f96271f3315be3ebed6eece2bb5d --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryMap.java @@ -0,0 +1,444 @@ +package maps.convert.osm2gml; + +import java.util.Set; +import java.util.Map; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; + +import maps.osm.OSMMap; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.collections.LazyMap; +//import rescuecore2.log.Logger; + +/** + This class holds all temporary information during map conversion. +*/ +public class TemporaryMap { + /** + The threshold for determining if nodes are co-located in metres. + */ + private static final double NEARBY_THRESHOLD_M = 1; + + private double threshold; + + private Set<Node> nodes; + private Set<Edge> edges; + private Map<Node, Set<Edge>> edgesAtNode; + private Map<Edge, Set<TemporaryObject>> objectsAtEdge; + private Set<TemporaryRoad> tempRoads; + private Set<TemporaryIntersection> tempIntersections; + private Set<TemporaryBuilding> tempBuildings; + private Set<TemporaryObject> allObjects; + + private OSMMap osmMap; + private Collection<OSMIntersectionInfo> osmIntersections; + private Collection<OSMRoadInfo> osmRoads; + private Collection<OSMBuildingInfo> osmBuildings; + + private int nextID; + + /** + Construct a TemporaryMap. + @param osmMap The OpenStreetMap data this map is generated from. + */ + public TemporaryMap(OSMMap osmMap) { + this.osmMap = osmMap; + nextID = 0; + nodes = new HashSet<Node>(); + edges = new HashSet<Edge>(); + threshold = ConvertTools.nearbyThreshold(osmMap, NEARBY_THRESHOLD_M); + tempRoads = new HashSet<TemporaryRoad>(); + tempIntersections = new HashSet<TemporaryIntersection>(); + tempBuildings = new HashSet<TemporaryBuilding>(); + allObjects = new HashSet<TemporaryObject>(); + edgesAtNode = new LazyMap<Node, Set<Edge>>() { + @Override + public Set<Edge> createValue() { + return new HashSet<Edge>(); + } + }; + objectsAtEdge = new LazyMap<Edge, Set<TemporaryObject>>() { + @Override + public Set<TemporaryObject> createValue() { + return new HashSet<TemporaryObject>(); + } + }; + } + + /** + Get the OSMMap. + @return The OSMMap. + */ + public OSMMap getOSMMap() { + return osmMap; + } + + /** + Set the OSMMap information. + @param intersections The set of intersections. + @param roads The set of roads. + @param buildings The set of buildings. + */ + public void setOSMInfo(Collection<OSMIntersectionInfo> intersections, Collection<OSMRoadInfo> roads, Collection<OSMBuildingInfo> buildings) { + osmIntersections = new HashSet<OSMIntersectionInfo>(intersections); + osmRoads = new HashSet<OSMRoadInfo>(roads); + osmBuildings = new HashSet<OSMBuildingInfo>(buildings); + } + + /** + Get the OSM intersection info. + @return The OSM intersection info. + */ + public Collection<OSMIntersectionInfo> getOSMIntersectionInfo() { + return Collections.unmodifiableCollection(osmIntersections); + } + + /** + Get the OSM road info. + @return The OSM road info. + */ + public Collection<OSMRoadInfo> getOSMRoadInfo() { + return Collections.unmodifiableCollection(osmRoads); + } + + /** + Get the OSM building info. + @return The OSM building info. + */ + public Collection<OSMBuildingInfo> getOSMBuildingInfo() { + return Collections.unmodifiableCollection(osmBuildings); + } + + /** + Add a road. + @param road The road to add. + */ + public void addRoad(TemporaryRoad road) { + tempRoads.add(road); + addObject(road); + } + + /** + Remove a road. + @param road The road to remove. + */ + public void removeRoad(TemporaryRoad road) { + tempRoads.remove(road); + removeObject(road); + } + + /** + Add an intersection. + @param intersection The intersection to add. + */ + public void addIntersection(TemporaryIntersection intersection) { + tempIntersections.add(intersection); + addObject(intersection); + } + + /** + Remove an intersection. + @param intersection The intersection to remove. + */ + public void removeIntersection(TemporaryIntersection intersection) { + tempIntersections.remove(intersection); + removeObject(intersection); + } + + /** + Add a building. + @param building The building to add. + */ + public void addBuilding(TemporaryBuilding building) { + tempBuildings.add(building); + addObject(building); + } + + /** + Remove a building. + @param building The building to remove. + */ + public void removeBuilding(TemporaryBuilding building) { + tempBuildings.remove(building); + removeObject(building); + } + + /** + Add an object. + @param object The object to add. + */ + public void addTemporaryObject(TemporaryObject object) { + if (object instanceof TemporaryRoad) { + addRoad((TemporaryRoad)object); + } + if (object instanceof TemporaryIntersection) { + addIntersection((TemporaryIntersection)object); + } + if (object instanceof TemporaryBuilding) { + addBuilding((TemporaryBuilding)object); + } + } + + /** + Remove an object. + @param object The object to remove. + */ + public void removeTemporaryObject(TemporaryObject object) { + if (object instanceof TemporaryRoad) { + removeRoad((TemporaryRoad)object); + } + if (object instanceof TemporaryIntersection) { + removeIntersection((TemporaryIntersection)object); + } + if (object instanceof TemporaryBuilding) { + removeBuilding((TemporaryBuilding)object); + } + } + + /** + Get all roads in the map. + @return All roads. + */ + public Collection<TemporaryRoad> getRoads() { + return new HashSet<TemporaryRoad>(tempRoads); + } + + /** + Get all intersections in the map. + @return All intersections. + */ + public Collection<TemporaryIntersection> getIntersections() { + return new HashSet<TemporaryIntersection>(tempIntersections); + } + + /** + Get all buildings in the map. + @return All buildings. + */ + public Collection<TemporaryBuilding> getBuildings() { + return new HashSet<TemporaryBuilding>(tempBuildings); + } + + /** + Get all objects in the map. + @return All objects. + */ + public Collection<TemporaryObject> getAllObjects() { + return new HashSet<TemporaryObject>(allObjects); + } + + /** + Get all nodes in the map. + @return All nodes. + */ + public Collection<Node> getAllNodes() { + return new HashSet<Node>(nodes); + } + + /** + Get all edges in the map. + @return All edges. + */ + public Collection<Edge> getAllEdges() { + return new HashSet<Edge>(edges); + } + + /** + Get all objects attached to an Edge. + @param e The Edge. + @return All attached TemporaryObjects. + */ + public Set<TemporaryObject> getAttachedObjects(Edge e) { + return new HashSet<TemporaryObject>(objectsAtEdge.get(e)); + } + + /** + Get all edges attached to a Node. + @param n The Node. + @return All attached edges. + */ + public Set<Edge> getAttachedEdges(Node n) { + return new HashSet<Edge>(edgesAtNode.get(n)); + } + + /** + Set the threshold for deciding if two points are the same. The {@link #isNear(Point2D, Point2D)} method uses this value to check if a new point needs to be registered. + @param t The new threshold. + */ + public void setNearbyThreshold(double t) { + threshold = t; + } + + /** + Get the threshold for deciding if two points are the same. The {@link #isNear(Point2D, Point2D)} method uses this value to check if a new point needs to be registered. + @return The nearby threshold. + */ + public double getNearbyThreshold() { + return threshold; + } + + /** + Find out if two points are nearby. + @param point1 The first point. + @param point2 The second point. + @return True iff the two points are within the nearby threshold. + */ + public boolean isNear(Point2D point1, Point2D point2) { + return isNear(point1.getX(), point1.getY(), point2.getX(), point2.getY()); + } + + /** + Find out if two points are nearby. + @param x1 The x coordinate of the first point. + @param y1 The y coordinate of the first point. + @param x2 The x coordinate of the second point. + @param y2 The y coordinate of the second point. + @return True iff the two points are within the nearby threshold. + */ + public boolean isNear(double x1, double y1, double x2, double y2) { + double dx = x2 - x1; + double dy = y2 - y1; + return (dx >= -threshold + && dx <= threshold + && dy >= -threshold + && dy <= threshold); + } + + /** + Get a Node near a point. If a Node already exists nearby then it will be returned, otherwise a new Node will be created. + @param p The node coordinates. + @return A Node. + */ + public Node getNode(Point2D p) { + return getNode(p.getX(), p.getY()); + } + + /** + Get a Node near a point. If a Node already exists nearby then it will be returned, otherwise a new Node will be created. + @param x The X coordinate. + @param y The Y coordinate. + @return A Node. + */ + public Node getNode(double x, double y) { + for (Node next : nodes) { + if (isNear(x, y, next.getX(), next.getY())) { + return next; + } + } + return createNode(x, y); + } + + /** + Get an Edge between two nodes. This will return either a new Edge or a shared instance if one already exists. + @param from The from node. + @param to The to node. + @return An Edge. + */ + public Edge getEdge(Node from, Node to) { + for (Edge next : edges) { + if (next.getStart().equals(from) && next.getEnd().equals(to) + || next.getStart().equals(to) && next.getEnd().equals(from)) { + return next; + } + } + return createEdge(from, to); + } + + /** + Get a DirectedEdge between two nodes. + @param from The from node. + @param to The to node. + @return A new DirectedEdge. + */ + public DirectedEdge getDirectedEdge(Node from, Node to) { + Edge e = getEdge(from, to); + return new DirectedEdge(e, from); + } + + /** + Replace an existing edge with a set of new edges. + @param edge The old edge. + @param newEdges The new edges. + */ + public void replaceEdge(Edge edge, Collection<Edge> newEdges) { + for (TemporaryObject next : getAttachedObjects(edge)) { + next.replaceEdge(edge, newEdges); + for (Edge nextEdge : newEdges) { + objectsAtEdge.get(nextEdge).add(next); + } + } + removeEdge(edge); + } + + /** + Split an edge into chunks. + @param edge The edge to split. + @param splitPoints The points to split the line. These must be in order along the line. + @return The replacement edges. + */ + public List<Edge> splitEdge(Edge edge, Node... splitPoints) { + List<Edge> replacements = new ArrayList<Edge>(); + Edge current = edge; + for (Node n : splitPoints) { + if (n.equals(current.getStart()) || n.equals(current.getEnd())) { + // Don't bother if the split point is at the origin or endpoint + continue; + } + replacements.add(getEdge(current.getStart(), n)); + current = getEdge(n, current.getEnd()); + } + if (!edge.equals(current)) { + replacements.add(current); + } + if (!replacements.isEmpty()) { + replaceEdge(edge, replacements); + } + return replacements; + } + + private Node createNode(double x, double y) { + Node result = new Node(nextID++, x, y); + nodes.add(result); + return result; + } + + private Edge createEdge(Node from, Node to) { + Edge result = new Edge(nextID++, from, to); + edges.add(result); + edgesAtNode.get(from).add(result); + edgesAtNode.get(to).add(result); + // Logger.debug("Created edge " + result); + return result; + } + + private void addObject(TemporaryObject object) { + allObjects.add(object); + for (DirectedEdge next : object.getEdges()) { + objectsAtEdge.get(next.getEdge()).add(object); + } + } + + private void removeNode(Node n) { + nodes.remove(n); + edgesAtNode.remove(n); + } + + private void removeEdge(Edge e) { + edges.remove(e); + edgesAtNode.get(e.getStart()).remove(e); + edgesAtNode.get(e.getEnd()).remove(e); + objectsAtEdge.remove(e); + // Logger.debug("Removed edge " + e); + } + + private void removeObject(TemporaryObject object) { + allObjects.remove(object); + for (DirectedEdge next : object.getEdges()) { + objectsAtEdge.get(next.getEdge()).remove(object); + } + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryObject.java b/modules/maps/src/maps/convert/osm2gml/TemporaryObject.java new file mode 100644 index 0000000000000000000000000000000000000000..307a43ed765e23887c8fbf7de89e3d7fc8ff5f7f --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryObject.java @@ -0,0 +1,246 @@ +package maps.convert.osm2gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Path2D; +import java.awt.geom.Area; +import java.awt.Shape; + +import rescuecore2.misc.geometry.Point2D; +//import rescuecore2.log.Logger; + +import maps.gml.GMLCoordinates; +import maps.gml.GMLTools; + +/** + Abstract base class for temporary data structures during conversion. +*/ +public abstract class TemporaryObject { + private List<DirectedEdge> edges; + private Map<DirectedEdge, TemporaryObject> neighbours; + private Path2D path; + private Rectangle2D bounds; + + /** + Construct a new TemporaryObject. + @param edges The edges of the object in counter-clockwise order. + */ + protected TemporaryObject(List<DirectedEdge> edges) { + this.edges = new ArrayList<DirectedEdge>(edges); + neighbours = new HashMap<DirectedEdge, TemporaryObject>(); + } + + /** + Get the edges of this object. + @return The edges. + */ + public List<DirectedEdge> getEdges() { + return Collections.unmodifiableList(edges); + } + + /** + Get the neighbour through a particular edge. + @param edge The edge to look up. + @return The neighbour through that edge or null. + */ + public TemporaryObject getNeighbour(DirectedEdge edge) { + return neighbours.get(edge); + } + + /** + Set the neighbour through a particular edge. + @param edge The edge to set the neighbour of. + @param neighbour The new neighbour for that edge. + */ + public void setNeighbour(DirectedEdge edge, TemporaryObject neighbour) { + neighbours.put(edge, neighbour); + } + + /** + Set the neighbour through a particular edge. + @param edge The edge to set the neighbour of. + @param neighbour The new neighbour for that edge. + */ + public void setNeighbour(Edge edge, TemporaryObject neighbour) { + neighbours.put(findDirectedEdge(edge), neighbour); + } + + /** + Turn the edges into a list of coordinates. + @return A list of GMLCoordinates. + */ + public List<GMLCoordinates> makeGMLCoordinates() { + List<GMLCoordinates> result = new ArrayList<GMLCoordinates>(); + for (DirectedEdge next : edges) { + Point2D p = next.getStartCoordinates(); + result.add(new GMLCoordinates(p.getX(), p.getY())); + } + return result; + } + + /** + Get the bounds of this object. + @return The bounds. + */ + public Rectangle2D getBounds() { + if (bounds == null) { + bounds = GMLTools.getBounds(makeGMLCoordinates()); + } + return bounds; + } + + /** + Get the Shape of this object. + @return The shape. + */ + public Shape getShape() { + if (path == null) { + path = new Path2D.Double(); + Iterator<DirectedEdge> it = edges.iterator(); + DirectedEdge d = it.next(); + path.moveTo(d.getStartCoordinates().getX(), d.getStartCoordinates().getY()); + path.lineTo(d.getEndCoordinates().getX(), d.getEndCoordinates().getY()); + while (it.hasNext()) { + d = it.next(); + path.lineTo(d.getEndCoordinates().getX(), d.getEndCoordinates().getY()); + } + } + return path; + } + + /** + Check if this object is a duplicate of another. Objects are duplicates if they contain the same list of directed edges, possibly offset. + @param other The other object to check against. + @return True if this object is a duplicate of other, false otherwise. + */ + public boolean isDuplicate(TemporaryObject other) { + List<DirectedEdge> myEdges = getEdges(); + List<DirectedEdge> otherEdges = other.getEdges(); + if (myEdges.size() != otherEdges.size()) { + return false; + } + Iterator<DirectedEdge> it = myEdges.iterator(); + DirectedEdge start = it.next(); + // See if we can find an equivalent edge in other + Iterator<DirectedEdge> ix = otherEdges.iterator(); + DirectedEdge otherStart = null; + while (ix.hasNext()) { + DirectedEdge test = ix.next(); + if (test.equals(start)) { + // Found! + otherStart = test; + break; + } + } + if (otherStart == null) { + // Edge not found in other so can't be a duplicate + return false; + } + // Check that edges are equivalent + // Walk through the edge lists starting at the beginning for me and at the equivalent edge in other. When we reach the end of other go back to the start. + while (ix.hasNext()) { + DirectedEdge a = it.next(); + DirectedEdge b = ix.next(); + if (!a.equals(b)) { + return false; + } + } + ix = otherEdges.iterator(); + while (it.hasNext()) { + DirectedEdge a = it.next(); + DirectedEdge b = ix.next(); + if (!a.equals(b)) { + return false; + } + } + return true; + } + + /** + Check if this object is a entirely inside another. + @param other The other object to check against. + @return True if this object is entirely inside the other, false otherwise. + */ + public boolean isEntirelyInside(TemporaryObject other) { + if (!this.getBounds().intersects(other.getBounds())) { + return false; + } + Area a = new Area(getShape()); + Area b = new Area(other.getShape()); + Area intersection = new Area(a); + intersection.intersect(b); + return a.equals(intersection); + } + + /** + Replace an edge with a set of replacement edges. + @param edge The edge to replace. + @param replacements The set of replacement edges. These can be in any order. + */ + protected void replaceEdge(Edge edge, Collection<Edge> replacements) { + // Logger.debug(this + " replacing edge " + edge + " with " + replacements); + // Logger.debug("Old edge list: " + edges); + if (replacements.isEmpty()) { + // Just remove the edge + for (Iterator<DirectedEdge> it = edges.iterator(); it.hasNext();) { + DirectedEdge next = it.next(); + if (next.getEdge().equals(edge)) { + it.remove(); + } + } + } + else { + for (ListIterator<DirectedEdge> it = edges.listIterator(); it.hasNext();) { + DirectedEdge next = it.next(); + if (next.getEdge().equals(edge)) { + it.remove(); + Set<Edge> replacementsSet = new HashSet<Edge>(replacements); + // Create directed edges for the replacements + Node start = next.getStartNode(); + Node end = next.getEndNode(); + while (!start.equals(end)) { + DirectedEdge newEdge = findNewEdge(start, replacementsSet); + replacementsSet.remove(newEdge.getEdge()); + it.add(newEdge); + start = newEdge.getEndNode(); + } + break; + } + } + } + // Logger.debug("New edge list: " + edges); + bounds = null; + path = null; + } + + private DirectedEdge findNewEdge(Node from, Set<Edge> candidates) { + for (Edge next : candidates) { + if (next.getStart().equals(from)) { + return new DirectedEdge(next, true); + } + if (next.getEnd().equals(from)) { + return new DirectedEdge(next, false); + } + } + return null; + } + + private DirectedEdge findDirectedEdge(Edge e) { + for (DirectedEdge next : edges) { + if (next.getEdge().equals(e)) { + return next; + } + } + throw new IllegalArgumentException("Edge " + e + " not found"); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryObjectInfo.java b/modules/maps/src/maps/convert/osm2gml/TemporaryObjectInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..961aca090ad7889e55d3a5cbf766bd5a0ac061b8 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryObjectInfo.java @@ -0,0 +1,90 @@ +package maps.convert.osm2gml; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.Polygon; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import java.util.List; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.ShapeDebugFrame; + +import maps.gml.GMLCoordinates; + +/** + A ShapeInfo that knows how to draw TemporaryObjects. +*/ +public class TemporaryObjectInfo extends ShapeDebugFrame.ShapeInfo { + private TemporaryObject shape; + private Color outlineColour; + private Color fillColour; + private Rectangle2D bounds; + + /** + Create a new TemporaryObjectInfo. + @param shape The shape to draw. + @param name The name of the shape. + @param outlineColour The colour to draw the outline of the shape. This may be null to indicate that the outline should not be painted. + @param fillColour The colour to draw the interior of the shape. This may be null to indicate that the interior should not be painted. + */ + public TemporaryObjectInfo(TemporaryObject shape, String name, Color outlineColour, Color fillColour) { + super(shape, name); + this.shape = shape; + this.outlineColour = outlineColour; + this.fillColour = fillColour; + if (shape != null) { + bounds = shape.getBounds(); + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (shape == null) { + return null; + } + List<GMLCoordinates> coordinates = shape.makeGMLCoordinates(); + int n = coordinates.size(); + int[] xs = new int[n]; + int[] ys = new int[n]; + int i = 0; + for (GMLCoordinates next : coordinates) { + xs[i] = transform.xToScreen(next.getX()); + ys[i] = transform.yToScreen(next.getY()); + ++i; + } + Polygon p = new Polygon(xs, ys, n); + if (fillColour != null) { + g.setColor(fillColour); + g.fill(p); + } + if (outlineColour != null) { + g.setColor(outlineColour); + g.draw(p); + } + return p; + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (outlineColour != null) { + g.setColor(outlineColour); + g.drawRect(0, 0, width - 1, height - 1); + } + if (fillColour != null) { + g.setColor(fillColour); + g.fillRect(0, 0, width, height); + } + } + + @Override + public Rectangle2D getBoundsShape() { + return bounds; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return null; + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/TemporaryRoad.java b/modules/maps/src/maps/convert/osm2gml/TemporaryRoad.java new file mode 100644 index 0000000000000000000000000000000000000000..239136df96d89048f0eb2d9d66cdc19581a43eae --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/TemporaryRoad.java @@ -0,0 +1,16 @@ +package maps.convert.osm2gml; + +import java.util.List; + +/** + A temporary road during conversion. +*/ +public class TemporaryRoad extends TemporaryObject { + /** + Construct a new TemporaryRoad. + @param edges The edges of the road in counter-clockwise order. + */ + public TemporaryRoad(List<DirectedEdge> edges) { + super(edges); + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/BuildingSpaceFiller.java b/modules/maps/src/maps/convert/osm2gml/buildings/BuildingSpaceFiller.java new file mode 100644 index 0000000000000000000000000000000000000000..af6ade4ed92243eaa20b23a7d6da1d20b57ab4f1 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/BuildingSpaceFiller.java @@ -0,0 +1,16 @@ +package maps.convert.osm2gml.buildings; + +import maps.gml.GMLShape; +import maps.gml.GMLMap; + +/** + Interface for objects that know how to fill spaces with buildings. +*/ +public interface BuildingSpaceFiller { + /** + Populate a space with buildings. + @param space The space to fill. + @param map The GMLMap to populate. + */ + void createBuildings(GMLShape space, GMLMap map); +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingBuildingSpaceFiller.java b/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingBuildingSpaceFiller.java new file mode 100644 index 0000000000000000000000000000000000000000..e4b918582c292b6c5f6426c10fceaaa806b45470 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingBuildingSpaceFiller.java @@ -0,0 +1,102 @@ +package maps.convert.osm2gml.buildings; + +import maps.gml.GMLShape; +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLDirectedEdge; +import maps.convert.osm2gml.buildings.row.RowFiller; +import maps.convert.osm2gml.buildings.row.RectangularDuplexRowFiller; + +import java.util.Random; +import java.util.Comparator; + +//import rescuecore2.misc.gui.ShapeDebugFrame; + +/** + A BuildingSpaceFiller that fills a space with row housing. +*/ +public class RowHousingBuildingSpaceFiller implements BuildingSpaceFiller { + // private ShapeDebugFrame debug; + private double sizeOf1m; + private Random random; + + /** + Construct a new RowHousingBuildingSpaceFiller. + @param sizeOf1m The size of 1m in GMLMap units. + @param random The random number generator to use. + */ + public RowHousingBuildingSpaceFiller(double sizeOf1m, Random random/*, ShapeDebugFrame debug*/) { + // this.debug = debug; + this.sizeOf1m = sizeOf1m; + this.random = random; + } + + @Override + public void createBuildings(GMLShape space, GMLMap map) { + // Sort the edges of the space by length + /* + List<GMLDirectedEdge> allEdges = space.getEdges(); + Collections.sort(allEdges, new EdgeLengthComparator()); + Set<GMLFace> newFaces = new HashSet<GMLFace>(); + RowFiller filler = createRandomFiller(); + for (GMLDirectedEdge next : allEdges) { + Set<GMLFace> edgeFaces = filler.fillRow(next, map); + // debug.show("Next row faces", ConvertTools.createGMLDebugShapes(edgeFaces)); + // Remove new faces that overlap with existing ones + for (Iterator<GMLFace> it = edgeFaces.iterator(); it.hasNext();) { + GMLFace newFace = it.next(); + boolean good = true; + for (GMLFace testFace : map.getFaces()) { + if (testFace == newFace) { + continue; + } + if (newFace.intersects(testFace)) { + good = false; + break; + } + } + if (good) { + newFaces.add(newFace); + } + else { + map.removeFace(newFace); + it.remove(); + } + } + // debug.show("Pruned next row faces", ConvertTools.createGMLDebugShapes(edgeFaces)); + } + debug.show("All new faces", ConvertTools.createGMLDebugShapes(newFaces)); + */ + } + + private RowFiller createRandomFiller() { + if (random.nextBoolean()) { + return RectangularDuplexRowFiller.makeWideFiller(sizeOf1m, random); + } + else { + return RectangularDuplexRowFiller.makeLongFiller(sizeOf1m, random); + } + } + + private static final class EdgeLengthComparator implements Comparator<GMLDirectedEdge>, java.io.Serializable { + public int compare(GMLDirectedEdge e1, GMLDirectedEdge e2) { + GMLNode start1 = e1.getStartNode(); + GMLNode end1 = e1.getEndNode(); + GMLNode start2 = e2.getStartNode(); + GMLNode end2 = e2.getEndNode(); + double dx1 = end1.getX() - start1.getX(); + double dy1 = end1.getY() - start1.getY(); + double dx2 = end2.getX() - start2.getX(); + double dy2 = end2.getY() - start2.getY(); + double l1 = Math.hypot(dx1, dy1); + double l2 = Math.hypot(dx2, dy2); + if (l1 < l2) { + return 1; + } + if (l2 < l1) { + return -1; + } + return 0; + } + } +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingType.java b/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingType.java new file mode 100644 index 0000000000000000000000000000000000000000..a044e62222f9b472fae182ca1581c292adb4ca39 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/RowHousingType.java @@ -0,0 +1,12 @@ +package maps.convert.osm2gml.buildings; + +//CHECKSTYLE:OFF:JavadocType|JavadocVariable +public enum RowHousingType { + T_SHAPE_ROW, + T_SHAPE_DUPLEX, + RECTANGULAR_DUPLEX, // Done + THIN_DUPLEX, + SQUARE_DETACHED, + L_SHAPE_DETACHED, +//CHECKSTYLE:ON:JavadocType|JavadocVariable +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/row/RectangularDuplexRowFiller.java b/modules/maps/src/maps/convert/osm2gml/buildings/row/RectangularDuplexRowFiller.java new file mode 100644 index 0000000000000000000000000000000000000000..0c21dc8145e35dc59b45399382fb9e72ed2b6917 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/row/RectangularDuplexRowFiller.java @@ -0,0 +1,156 @@ +package maps.convert.osm2gml.buildings.row; + +import java.util.Set; +import java.util.HashSet; +import java.util.Random; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLShape; +import maps.gml.GMLMap; + +/** + A RowFiller that creates rectangular duplex units. +*/ +public final class RectangularDuplexRowFiller implements RowFiller { + private static final double WIDE_LOT_WIDTH_M = 25; + private static final double WIDE_BUILDING_WIDTH_M = 21; + private static final double WIDE_BUILDING_DEPTH_M = 7; + private static final double WIDE_MIN_OFFSET_M = 2; + private static final double WIDE_MAX_OFFSET_M = 10; + + private static final double LONG_LOT_WIDTH_M = 18; + private static final double LONG_BUILDING_WIDTH_M = 15; + private static final double LONG_BUILDING_DEPTH_M = 15; + private static final double LONG_MIN_OFFSET_M = 1; + private static final double LONG_MAX_OFFSET_M = 4; + + private static final int MIN_RUN_LENGTH = 1; + private static final int MAX_RUN_LENGTH = 5; + + private final double lotWidth; + private final double buildingWidth; + private final double buildingDepth; + private final double minOffset; + private final double maxOffset; + + private final Random random; + + private RectangularDuplexRowFiller(double sizeOf1m, Random random, double lotWidth, double buildingWidth, double buildingDepth, double minOffset, double maxOffset) { + this.lotWidth = lotWidth * sizeOf1m; + this.buildingWidth = buildingWidth * sizeOf1m; + this.buildingDepth = buildingDepth * sizeOf1m; + this.minOffset = minOffset * sizeOf1m; + this.maxOffset = maxOffset * sizeOf1m; + this.random = random; + } + + /** + Create a filler that creates wide buildings. + @param sizeOf1m The size of 1m in GML units. + @param random The random number generator to use. + @return A new RectangularDuplexRowFiller. + */ + public static RectangularDuplexRowFiller makeWideFiller(double sizeOf1m, Random random) { + return new RectangularDuplexRowFiller(sizeOf1m, random, WIDE_LOT_WIDTH_M, WIDE_BUILDING_WIDTH_M, WIDE_BUILDING_DEPTH_M, WIDE_MIN_OFFSET_M, WIDE_MAX_OFFSET_M); + } + + /** + Create a filler that creates longer, narrower buildings. + @param sizeOf1m The size of 1m in GML units. + @param random The random number generator to use. + @return A new RectangularDuplexRowFiller. + */ + public static RectangularDuplexRowFiller makeLongFiller(double sizeOf1m, Random random) { + return new RectangularDuplexRowFiller(sizeOf1m, random, LONG_LOT_WIDTH_M, LONG_BUILDING_WIDTH_M, LONG_BUILDING_DEPTH_M, LONG_MIN_OFFSET_M, LONG_MAX_OFFSET_M); + } + + @Override + public Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map) { + Set<GMLShape> result = new HashSet<GMLShape>(); + /* + Line2D edgeLine = ConvertTools.gmlDirectedEdgeToLine(edge); + Vector2D normal = edgeLine.getDirection().getNormal().normalised(); + // Create lots along the edge + double edgeLength = edgeLine.getDirection().getLength(); + int lots = (int)(edgeLength / LOT_WIDTH); + double trueLotWidth = edgeLength / lots; + System.out.println("Creating " + lots + " lots"); + double offset = getRandomOffset(); + int runLength = getRandomRunLength(); + for (int i = 0; i < lots; ++i) { + if (runLength-- == 0) { + offset = getRandomOffset(); + runLength = getRandomRunLength(); + } + double d1 = i; + double d2 = i + 1; + d1 /= lots; + d2 /= lots; + Point2D topRight = edgeLine.getPoint(d1); + Point2D topLeft = edgeLine.getPoint(d2); + Set<GMLFace> faces = createBuildingInLot(edgeLine, topRight, topLeft, normal, offset, map); + result.addAll(faces); + } + */ + return result; + } + + /* + private Set<GMLFace> createBuildingInLot(Line2D edgeLine, Point2D lotTopRight, Point2D lotTopLeft, Vector2D edgeNormal, double depthOffset, GMLMap map) { + // Create the building by moving in from the sides of the lot boundary + Line2D topLine = new Line2D(lotTopRight, lotTopLeft); + double lotWidth = topLine.getDirection().getLength(); + double widthSlack = ((lotWidth - BUILDING_WIDTH) / lotWidth) / 2; + Point2D topRight = topLine.getPoint(widthSlack); + Point2D topMiddle = topLine.getPoint(0.5); + Point2D topLeft = topLine.getPoint(1.0 - widthSlack); + // Offset from the top of the boundary + topRight = topRight.plus(edgeNormal.scale(depthOffset)); + topMiddle = topMiddle.plus(edgeNormal.scale(depthOffset)); + topLeft = topLeft.plus(edgeNormal.scale(depthOffset)); + // Find the other end of the building + Point2D bottomRight = topRight.plus(edgeNormal.scale(BUILDING_DEPTH)); + Point2D bottomMiddle = topMiddle.plus(edgeNormal.scale(BUILDING_DEPTH)); + Point2D bottomLeft = topLeft.plus(edgeNormal.scale(BUILDING_DEPTH)); + // Create new nodes and directed edges for the building + GMLNode n1 = map.ensureNodeNear(topRight); + GMLNode n2 = map.ensureNodeNear(topMiddle); + GMLNode n3 = map.ensureNodeNear(topLeft); + GMLNode n4 = map.ensureNodeNear(bottomLeft); + GMLNode n5 = map.ensureNodeNear(bottomMiddle); + GMLNode n6 = map.ensureNodeNear(bottomRight); + // Create two new buildings + List<GMLDirectedEdge> edges1 = new ArrayList<GMLDirectedEdge>(); + +// List<GMLDirectedEdge> edges2 = new ArrayList<GMLDirectedEdge>(); +// edges1.add(map.ensureDirectedEdge(topRight, topMiddle)); +// edges1.add(map.ensureDirectedEdge(topMiddle, bottomMiddle)); +// edges1.add(map.ensureDirectedEdge(bottomMiddle, bottomRight)); +// edges1.add(map.ensureDirectedEdge(bottomRight, topRight)); +// edges2.add(map.ensureDirectedEdge(topMiddle, topLeft)); +// edges2.add(map.ensureDirectedEdge(topLeft, bottomLeft)); +// edges2.add(map.ensureDirectedEdge(bottomLeft, bottomMiddle)); +// edges2.add(map.ensureDirectedEdge(bottomMiddle, topMiddle)); + + edges1.add(map.ensureDirectedEdge(topRight, topLeft)); + edges1.add(map.ensureDirectedEdge(topLeft, bottomLeft)); + edges1.add(map.ensureDirectedEdge(bottomLeft, bottomRight)); + edges1.add(map.ensureDirectedEdge(bottomRight, topRight)); + // TO DO: Make the entrance faces + Set<GMLFace> result = new HashSet<GMLFace>(); + result.add(map.createFace(edges1, FaceType.BUILDING)); + // result.add(map.createFace(edges2, FaceType.BUILDING)); + return result; + } + + private double getRandomOffset() { + double d = random.nextDouble(); + double range = MAX_OFFSET - MIN_OFFSET; + return MIN_OFFSET + (d * range); + } + + private int getRandomRunLength() { + return MIN_RUN_LENGTH + random.nextInt(MAX_RUN_LENGTH - MIN_RUN_LENGTH + 1); + } +*/ +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/row/RowFiller.java b/modules/maps/src/maps/convert/osm2gml/buildings/row/RowFiller.java new file mode 100644 index 0000000000000000000000000000000000000000..16eed507f17bb3d4c73f59c9c87f0f62533fd877 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/row/RowFiller.java @@ -0,0 +1,20 @@ +package maps.convert.osm2gml.buildings.row; + +import java.util.Set; + +import maps.gml.GMLShape; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLMap; + +/** + Interface for a building generator that works by row. +*/ +public interface RowFiller { + /** + Generate buildings along an edge. + @param edge The edge to populate. + @param map The map. + @return The set of new faces. + */ + Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map); +} diff --git a/modules/maps/src/maps/convert/osm2gml/buildings/row/ThinDuplexRowFiller.java b/modules/maps/src/maps/convert/osm2gml/buildings/row/ThinDuplexRowFiller.java new file mode 100644 index 0000000000000000000000000000000000000000..2b4fdcaf69b35e91fec5a16861f40da647a8d386 --- /dev/null +++ b/modules/maps/src/maps/convert/osm2gml/buildings/row/ThinDuplexRowFiller.java @@ -0,0 +1,108 @@ +package maps.convert.osm2gml.buildings.row; + +import java.util.Set; +import java.util.HashSet; +import java.util.Random; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLShape; +import maps.gml.GMLMap; + +/** + A RowFiller that creates long, thin duplex units. +*/ +public class ThinDuplexRowFiller implements RowFiller { + private static final double BUILDING_WIDTH_M = 10; + private static final double BUILDING_DEPTH_M = 20; + private static final double MIN_OFFSET_M = 2; + private static final double MAX_OFFSET_M = 3; + private static final int MIN_RUN_LENGTH = 1; + private static final int MAX_RUN_LENGTH = 5; + + private final double buildingWidth; + private final double buildingDepth; + private final double minOffset; + private final double maxOffset; + + private final Random random; + + /** + Construct a ThinDuplexRowFiller. + @param sizeOf1m The size of 1m. + @param random A random number generator. + */ + public ThinDuplexRowFiller(double sizeOf1m, Random random) { + buildingWidth = BUILDING_WIDTH_M * sizeOf1m; + buildingDepth = BUILDING_DEPTH_M * sizeOf1m; + minOffset = MIN_OFFSET_M * sizeOf1m; + maxOffset = MAX_OFFSET_M * sizeOf1m; + this.random = random; + } + + @Override + public Set<GMLShape> fillRow(GMLDirectedEdge edge, GMLMap map) { + Set<GMLShape> result = new HashSet<GMLShape>(); + /* + Line2D edgeLine = ConvertTools.gmlDirectedEdgeToLine(edge); + Vector2D normal = edgeLine.getDirection().getNormal().normalised(); + // Create buildings along the edge until we run out of room + double edgeLength = edgeLine.getDirection().getLength(); + double offset = getRandomOffset(); + int runLength = getRandomRunLength(); + double d = 0; + while (d < 1) { + if (runLength-- == 0) { + offset = getRandomOffset(); + runLength = getRandomRunLength(); + } + double d1 = d; + double d2 = d + (BUILDING_WIDTH / edgeLength); + Point2D topRight = edgeLine.getPoint(d1); + Point2D topLeft = edgeLine.getPoint(d2); + result.addAll(createBuildingInLot(edgeLine, topRight, topLeft, normal, offset, map)); + d = d2; + } + */ + return result; + } + + /* + private Set<GMLFace> createBuildingInLot(Line2D edgeLine, Point2D topRight, Point2D topLeft, Vector2D edgeNormal, double depthOffset, GMLMap map) { + // Offset from the top of the boundary + topRight = topRight.plus(edgeNormal.scale(depthOffset)); + topLeft = topLeft.plus(edgeNormal.scale(depthOffset)); + // Find the other end of the building + Point2D bottomRight = topRight.plus(edgeNormal.scale(BUILDING_DEPTH)); + Point2D bottomLeft = topLeft.plus(edgeNormal.scale(BUILDING_DEPTH)); + // Create new nodes and directed edges for the lot + GMLNode n1 = map.ensureNodeNear(topRight); + GMLNode n2 = map.ensureNodeNear(topLeft); + GMLNode n3 = map.ensureNodeNear(bottomLeft); + GMLNode n4 = map.ensureNodeNear(bottomRight); + List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>(); + GMLDirectedEdge e1 = map.ensureDirectedEdge(topRight, topLeft); + GMLDirectedEdge e2 = map.ensureDirectedEdge(topLeft, bottomLeft); + GMLDirectedEdge e3 = map.ensureDirectedEdge(bottomLeft, bottomRight); + GMLDirectedEdge e4 = map.ensureDirectedEdge(bottomRight, topRight); + edges.add(e1); + edges.add(e2); + edges.add(e3); + edges.add(e4); + GMLFace buildingFace = map.createFace(edges, FaceType.BUILDING); + // Make the entrance face + Set<GMLFace> result = new HashSet<GMLFace>(); + result.add(buildingFace); + return result; + } + + private double getRandomOffset() { + double d = random.nextDouble(); + double range = MAX_OFFSET - MIN_OFFSET; + return MIN_OFFSET + (d * range); + } + + private int getRandomRunLength() { + return MIN_RUN_LENGTH + random.nextInt(MAX_RUN_LENGTH - MIN_RUN_LENGTH + 1); + } + */ +} diff --git a/modules/maps/src/maps/gml/GMLBuilding.java b/modules/maps/src/maps/gml/GMLBuilding.java new file mode 100755 index 0000000000000000000000000000000000000000..e122bf00dfb763ed876f6ff3ff5f7420b8ddd773 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLBuilding.java @@ -0,0 +1,109 @@ +package maps.gml; + +import java.util.List; + +/** + A building in GML space. +*/ +public class GMLBuilding extends GMLShape { + private int floors; + private int code; + private int importance; + private int capacity; + + /** + Construct a GMLBuilding. + @param id The ID of the building. + */ + public GMLBuilding(int id) { + super(id); + } + + /** + Construct a GMLBuilding. + @param id The ID of the building. + @param edges The edges of the building. + */ + public GMLBuilding(int id, List<GMLDirectedEdge> edges) { + super(id, edges); + } + + /** + Construct a GMLBuilding. + @param id The ID of the building. + @param edges The edges of the building. + @param neighbours The neighbours of each edge. + */ + public GMLBuilding(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) { + super(id, edges, neighbours); + } + + @Override + public String toString() { + return "GMLBuilding " + getID(); + } + + /** + Set the capacity of this building. + @param newCapacity The new capacity of the building. + */ + public void setCapacity(int newCapacity) { + capacity = newCapacity; + } + + /** + Get the capacity of this building. + @return The the capacity of building. + */ + public int getCapacity() { + return capacity; + } + + /** + Set the number of floors in this building. + @param newFloors The new number of floors. + */ + public void setFloors(int newFloors) { + floors = newFloors; + } + + /** + Get the number of floors in this building. + @return The number of floors. + */ + public int getFloors() { + return floors; + } + + /** + Set the building code of this building. + @param newCode The new building code. + */ + public void setCode(int newCode) { + code = newCode; + } + + /** + Get the building code of this building. + @return The building code. + */ + public int getCode() { + return code; + } + + /** + Set the importance of this building. + @param newImportance The new importance. + */ + public void setImportance(int newImportance) { + importance = newImportance; + } + + /** + Get the importance of this building. + @return The importance. + */ + public int getImportance() { + return importance; + } +} diff --git a/modules/maps/src/maps/gml/GMLCoordinates.java b/modules/maps/src/maps/gml/GMLCoordinates.java new file mode 100644 index 0000000000000000000000000000000000000000..acef976ae515d026887077a818cac907a38a36eb --- /dev/null +++ b/modules/maps/src/maps/gml/GMLCoordinates.java @@ -0,0 +1,95 @@ +package maps.gml; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +/** + A set of GML coordinates. These coordinates are in m. +*/ +public class GMLCoordinates { + private static final NumberFormat FORMAT = new DecimalFormat("#0.000", DecimalFormatSymbols.getInstance(Locale.US)); + + private double x; + private double y; + + /** + Create a new GMLCoordinates object. + @param x The X coordinate. + @param y The Y coordinate. + */ + public GMLCoordinates(double x, double y) { + this.x = x; + this.y = y; + } + + /** + Copy constructor. + @param other The GMLCoordinates to copy. + */ + public GMLCoordinates(GMLCoordinates other) { + this.x = other.x; + this.y = other.y; + } + + /** + Create a new GMLCoordinates object from a String of the form "x,y". + @param s The String to read. + @throws IllegalArgumentException If the string is invalid. + */ + public GMLCoordinates(String s) { + int index = s.indexOf(","); + if (index == -1) { + throw new IllegalArgumentException("'" + s + "' is not of the form 'x,y'"); + } + try { + this.x = Double.parseDouble(s.substring(0, index)); + this.y = Double.parseDouble(s.substring(index + 1)); + } + catch (NumberFormatException e) { + throw new IllegalArgumentException("'" + s + "' is not of the form 'x,y'", e); + } + } + + /** + Get the X coordinate. + @return The X coordinate. + */ + public double getX() { + return x; + } + + /** + Get the Y coordinate. + @return The y coordinate. + */ + public double getY() { + return y; + } + + /** + Set the X coordinate. + @param newX The new X coordinate. + */ + public void setX(double newX) { + x = newX; + } + + /** + Set the Y coordinate. + @param newY The new Y coordinate. + */ + public void setY(double newY) { + y = newY; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(FORMAT.format(x)); + result.append(","); + result.append(FORMAT.format(y)); + return result.toString(); + } +} diff --git a/modules/maps/src/maps/gml/GMLDirectedEdge.java b/modules/maps/src/maps/gml/GMLDirectedEdge.java new file mode 100644 index 0000000000000000000000000000000000000000..18e559e895bdcd7bdcdb60da1bc5d51550e28033 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLDirectedEdge.java @@ -0,0 +1,127 @@ +package maps.gml; + +import java.util.List; +import java.util.ArrayList; + +/** + A GMLDirectedEdge is an edge with an orientation. + */ +public class GMLDirectedEdge { + private GMLEdge edge; + private boolean forward; + + /** + Construct a directed GML edge. + @param edge The underlying edge. + @param forward True if this directed edge is aligned with the underlying edge direction, false otherwise. + */ + public GMLDirectedEdge(GMLEdge edge, boolean forward) { + this.edge = edge; + this.forward = forward; + } + + /** + Construct a directed GML edge. + @param edge The underlying edge. + @param start The start node. + */ + public GMLDirectedEdge(GMLEdge edge, GMLNode start) { + this.edge = edge; + this.forward = start.equals(edge.getStart()); + } + + /** + Get the underlying edge. + @return The underlying edge. + */ + public GMLEdge getEdge() { + return edge; + } + + /** + Is this directed edge in the direction of the underlying edge? + @return True if this directed edge is aligned with the underlying edge direction, false otherwise. + */ + public boolean isForward() { + return forward; + } + + /** + Reverse the direction of this edge. + */ + public void reverse() { + forward = !forward; + } + + /** + Get the node at the start of the underlying edge. + @return The start node. + */ + public GMLNode getStartNode() { + return forward ? edge.getStart() : edge.getEnd(); + } + + /** + Get the node at the end of the underlying edge. + @return The end node. + */ + public GMLNode getEndNode() { + return forward ? edge.getEnd() : edge.getStart(); + } + + /** + Get the points of the underlying edge in the right order for this directed edge. + @return The points of the underlying edge in the right order. + */ + public List<GMLCoordinates> getPoints() { + List<GMLCoordinates> result = new ArrayList<GMLCoordinates>(); + result.add(getStartNode().getCoordinates()); + result.add(getEndNode().getCoordinates()); + return result; + } + + /** + Get the coordinates of the start of this edge. + @return The coordinates of the start of this edge. + */ + public GMLCoordinates getStartCoordinates() { + if (forward) { + return edge.getStart().getCoordinates(); + } + else { + return edge.getEnd().getCoordinates(); + } + } + + /** + Get the coordinates of the end of this edge. + @return The coordinates of the end of this edge. + */ + public GMLCoordinates getEndCoordinates() { + if (forward) { + return edge.getEnd().getCoordinates(); + } + else { + return edge.getStart().getCoordinates(); + } + } + + @Override + public String toString() { + return "GMLDirectedEdge" + (forward ? "" : " backwards") + " along " + edge; + } + + @Override + public int hashCode() { + return edge.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof GMLDirectedEdge) { + GMLDirectedEdge e = (GMLDirectedEdge)o; + return this.forward == e.forward && this.edge.equals(e.edge); + } + return false; + } +} diff --git a/modules/maps/src/maps/gml/GMLEdge.java b/modules/maps/src/maps/gml/GMLEdge.java new file mode 100644 index 0000000000000000000000000000000000000000..28ed9db548528813e3207486ec26d429383f4be9 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLEdge.java @@ -0,0 +1,112 @@ +package maps.gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +/** + A GML edge. An edge is a line between two nodes. + */ +public class GMLEdge extends GMLObject { + private GMLNode start; + private GMLNode end; + private boolean passable; + private List<GMLCoordinates> points; + + /** + Construct a new GMLEdge. + @param id The ID of this object. + @param start The start node. + @param end The end node. + @param passable True if this directed edge is passable. + */ + public GMLEdge(int id, GMLNode start, GMLNode end, boolean passable) { + super(id); + this.start = start; + this.end = end; + this.passable = passable; + points = new ArrayList<GMLCoordinates>(); + points.add(start.getCoordinates()); + points.add(end.getCoordinates()); + } + + /** + Get the points along the edge. + @return The coordinates along the edge. + */ + public List<GMLCoordinates> getPoints() { + return Collections.unmodifiableList(points); + } + + /** + Set the points along the edge. + @param newPoints The new coordinates along the edge. + */ + public void setPoints(List<GMLCoordinates> newPoints) { + points.clear(); + points.addAll(newPoints); + } + + /** + Get the start node. + @return The start node. + */ + public GMLNode getStart() { + return start; + } + + /** + Set the start node. + @param s The new start node. + */ + public void setStart(GMLNode s) { + start = s; + } + + /** + Get the end node. + @return The end node. + */ + public GMLNode getEnd() { + return end; + } + + /** + Set the end node. + @param e The new end node. + */ + public void setEnd(GMLNode e) { + end = e; + } + + /** + Find out if this edge is passable. + @return True iff the edge is passable. + */ + public boolean isPassable() { + return passable; + } + + /** + Set the passable flag on this edge. + @param b The new passable flag. + */ + public void setPassable(boolean b) { + passable = b; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("GMLEdge "); + result.append(getID()); + result.append(" from "); + result.append(start); + result.append(" to "); + result.append(end); + if (!passable) { + result.append(" (impassable)"); + } + return result.toString(); + } +} diff --git a/modules/maps/src/maps/gml/GMLException.java b/modules/maps/src/maps/gml/GMLException.java new file mode 100644 index 0000000000000000000000000000000000000000..681ef2f368cea6f46580d3d2bd8f3eddca7858f9 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLException.java @@ -0,0 +1,38 @@ +package maps.gml; + +/** + Exceptions related to GML. +*/ +public class GMLException extends Exception { + /** + Construct a GMLException with no error message. + */ + public GMLException() { + super(); + } + + /** + Construct a GMLException with an error message. + @param msg The error message. + */ + public GMLException(String msg) { + super(msg); + } + + /** + Construct a GMLException with an underlying cause. + @param cause The cause. + */ + public GMLException(Throwable cause) { + super(cause); + } + + /** + Construct a GMLException with an error message and underlying cause. + @param msg The error message. + @param cause The cause. + */ + public GMLException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/maps/src/maps/gml/GMLMap.java b/modules/maps/src/maps/gml/GMLMap.java new file mode 100755 index 0000000000000000000000000000000000000000..c68ceb8831b160cc2ecf533aaada331946293924 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLMap.java @@ -0,0 +1,1007 @@ +package maps.gml; + +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +import maps.CoordinateConversion; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +import rescuecore2.misc.collections.LazyMap; + +/** + A GML map. All coordinates are specified in m. +*/ +public class GMLMap implements maps.Map { + private double minX; + private double maxX; + private double minY; + private double maxY; + private boolean boundsKnown; + + private Map<Integer, GMLNode> nodes; + private Map<Integer, GMLEdge> edges; + private Map<Integer, GMLBuilding> buildings; + private Map<Integer, GMLRoad> roads; + private Map<Integer, GMLSpace> spaces; + private Set<GMLShape> allShapes; + private Set<GMLObject> allObjects; + + private Map<GMLNode, Collection<GMLEdge>> attachedEdges; + private Map<GMLEdge, Collection<GMLShape>> attachedShapes; + + private int nextID; + + /** + Construct an empty GML map. + */ + public GMLMap() { + nodes = new HashMap<Integer, GMLNode>(); + edges = new HashMap<Integer, GMLEdge>(); + buildings = new HashMap<Integer, GMLBuilding>(); + roads = new HashMap<Integer, GMLRoad>(); + spaces = new HashMap<Integer, GMLSpace>(); + allShapes = new HashSet<GMLShape>(); + allObjects = new HashSet<GMLObject>(); + attachedEdges = new LazyMap<GMLNode, Collection<GMLEdge>>() { + @Override + public Collection<GMLEdge> createValue() { + return new HashSet<GMLEdge>(); + } + }; + attachedShapes = new LazyMap<GMLEdge, Collection<GMLShape>>() { + @Override + public Collection<GMLShape> createValue() { + return new HashSet<GMLShape>(); + } + }; + boundsKnown = false; + nextID = 0; + } + + /** + Create a new GMLNode. + @param x The X coordinate of the node in m. + @param y The Y coordinate of the node in m. + @return A new GMLNode with a unique ID. + */ + public GMLNode createNode(double x, double y) { + GMLNode n = new GMLNode(nextID++, x, y); + addNode(n); + return n; + } + + /** + Create a new GMLNode. + @param coords The coordinates of the node. + @return A new GMLNode with a unique ID. + */ + public GMLNode createNode(GMLCoordinates coords) { + GMLNode n = new GMLNode(nextID++, coords); + addNode(n); + return n; + } + + /** + Create a set of new GMLNodes. + @param coords The coordinates of the new nodes. + @return A set of new GMLNodes with unique IDs. + */ + public List<GMLNode> createNodes(List<GMLCoordinates> coords) { + List<GMLNode> result = new ArrayList<GMLNode>(coords.size()); + for (GMLCoordinates c : coords) { + GMLNode n = new GMLNode(nextID++, c); + addNode(n); + result.add(n); + } + return result; + } + + /** + Create a set of new GMLNodes. + @param coords The coordinates of the new nodes. + @return A set of new GMLNodes with unique IDs. + */ + public List<GMLNode> createNodesFromPoints(List<Point2D> coords) { + List<GMLNode> result = new ArrayList<GMLNode>(coords.size()); + for (Point2D p : coords) { + GMLNode n = new GMLNode(nextID++, p.getX(), p.getY()); + addNode(n); + result.add(n); + } + return result; + } + + /** + Create a new GMLEdge between two nodes. + @param first The 'start' node. + @param second The 'end' node. + @return A new GMLEdge with a unique ID. + */ + public GMLEdge createEdge(GMLNode first, GMLNode second) { + GMLEdge e = new GMLEdge(nextID++, first, second, false); + addEdge(e); + return e; + } + + /** + Create new GMLBuilding. + @param apexes The apexes of the building. + @return A new GMLBuilding with a unique ID. + */ + public GMLBuilding createBuildingFromNodes(List<GMLNode> apexes) { + return createBuilding(apexesToEdges(apexes)); + } + + /** + Create new GMLBuilding. + @param bEdges The edges of the building. + @return A new GMLBuilding with a unique ID. + */ + public GMLBuilding createBuilding(List<GMLDirectedEdge> bEdges) { + GMLBuilding b = new GMLBuilding(nextID++, bEdges); + b.setFloors(1); + b.setCode(0); + b.setImportance(1); + b.setCapacity(0);//todo + addBuilding(b); + return b; + } + + /** + Create new GMLRoad. + @param apexes The apexes of the road. + @return A new GMLRoad with a unique ID. + */ + public GMLRoad createRoadFromNodes(List<GMLNode> apexes) { + return createRoad(apexesToEdges(apexes)); + } + + /** + Create new GMLRoad. + @param rEdges The edges of the road. + @return A new GMLRoad with a unique ID. + */ + public GMLRoad createRoad(List<GMLDirectedEdge> rEdges) { + GMLRoad r = new GMLRoad(nextID++, rEdges); + addRoad(r); + return r; + } + + /** + Create new GMLSpace. + @param apexes The apexes of the space. + @return A new GMLSpace with a unique ID. + */ + public GMLSpace createSpaceFromNodes(List<GMLNode> apexes) { + return createSpace(apexesToEdges(apexes)); + } + + /** + Create new GMLSpace. + @param sEdges The edges of the space. + @return A new GMLSpace with a unique ID. + */ + public GMLSpace createSpace(List<GMLDirectedEdge> sEdges) { + GMLSpace s = new GMLSpace(nextID++, sEdges); + addSpace(s); + return s; + } + + /** + Add a node. + @param n The node to add. + */ + public void addNode(GMLNode n) { + if (nodes.containsKey(n.getID())) { + return; + } + addObject(n); + nodes.put(n.getID(), n); + boundsKnown = false; + } + + /** + Add an edge. The edge's nodes will also be added if required. + @param e The edge to add. + */ + public void addEdge(GMLEdge e) { + if (edges.containsKey(e.getID())) { + return; + } + addObject(e); + edges.put(e.getID(), e); + addNode(e.getStart()); + addNode(e.getEnd()); + attachedEdges.get(e.getStart()).add(e); + attachedEdges.get(e.getEnd()).add(e); + } + + /** + Add a building. The building's edges will be added if required. + @param b The building to add. + */ + public void addBuilding(GMLBuilding b) { + if (buildings.containsKey(b.getID())) { + return; + } + addShape(b); + buildings.put(b.getID(), b); + } + + /** + Add a road. The road's edges will be added if required. + @param r The road to add. + */ + public void addRoad(GMLRoad r) { + if (roads.containsKey(r.getID())) { + return; + } + addShape(r); + roads.put(r.getID(), r); + } + + /** + Add a space. The space's edges will be added if required. + @param s The space to add. + */ + public void addSpace(GMLSpace s) { + if (spaces.containsKey(s.getID())) { + return; + } + addShape(s); + spaces.put(s.getID(), s); + } + + /** + Add an object. + @param object The object to add. + */ + public void add(GMLObject object) { + if (object instanceof GMLNode) { + addNode((GMLNode)object); + } + else if (object instanceof GMLEdge) { + addEdge((GMLEdge)object); + } + else if (object instanceof GMLRoad) { + addRoad((GMLRoad)object); + } + else if (object instanceof GMLBuilding) { + addBuilding((GMLBuilding)object); + } + else if (object instanceof GMLSpace) { + addSpace((GMLSpace)object); + } + else { + throw new IllegalArgumentException("Don't know how to add " + object + " (class: " + object.getClass().getName() + ")"); + } + } + + /** + Add a set of objects. + @param objects The objects to add. + */ + public void add(Collection<? extends GMLObject> objects) { + for (GMLObject next : objects) { + add(next); + } + } + + /** + Add a set of objects. + @param objects The objects to add. + */ + public void add(GMLObject... objects) { + for (GMLObject next : objects) { + add(next); + } + } + + /** + Remove a node, any edges attached to the node and any shapes attached to those edges. + @param n The node to remove. + @return All removed objects, not including n. + */ + public Collection<GMLObject> removeNode(GMLNode n) { + Collection<GMLObject> result = new HashSet<GMLObject>(); + if (nodes.containsKey(n.getID())) { + removeObject(n); + nodes.remove(n.getID()); + Collection<GMLEdge> attached = new HashSet<GMLEdge>(getAttachedEdges(n)); + for (GMLEdge next : attached) { + result.add(next); + result.addAll(removeEdge(next)); + } + boundsKnown = false; + } + return result; + } + + /** + Remove an edge and any attached shapes. + @param e The edge to remove. + @return All removed objects, not including e. + */ + public Collection<GMLObject> removeEdge(GMLEdge e) { + Collection<GMLObject> result = new HashSet<GMLObject>(); + if (edges.containsKey(e.getID())) { + removeObject(e); + edges.remove(e.getID()); + Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(e)); + for (GMLShape next : attached) { + result.add(next); + remove(next); + } + attachedEdges.get(e.getStart()).remove(e); + attachedEdges.get(e.getEnd()).remove(e); + } + return result; + } + + /** + Remove a building. + @param b The building to remove. + */ + public void removeBuilding(GMLBuilding b) { + if (buildings.containsKey(b.getID())) { + removeShape(b); + buildings.remove(b.getID()); + } + } + + /** + Remove a road. + @param r The road to remove. + */ + public void removeRoad(GMLRoad r) { + if (roads.containsKey(r.getID())) { + removeShape(r); + roads.remove(r.getID()); + } + } + + /** + Remove a space. + @param s The space to remove. + */ + public void removeSpace(GMLSpace s) { + if (spaces.containsKey(s.getID())) { + removeShape(s); + spaces.remove(s.getID()); + } + } + + /** + Remove an object. + @param object The object to remove. + */ + public void remove(GMLObject object) { + if (object instanceof GMLNode) { + removeNode((GMLNode)object); + } + else if (object instanceof GMLEdge) { + removeEdge((GMLEdge)object); + } + else if (object instanceof GMLRoad) { + removeRoad((GMLRoad)object); + } + else if (object instanceof GMLBuilding) { + removeBuilding((GMLBuilding)object); + } + else if (object instanceof GMLSpace) { + removeSpace((GMLSpace)object); + } + else { + throw new IllegalArgumentException("Don't know how to remove " + object + " (class: " + object.getClass().getName() + ")"); + } + } + + /** + Remove a set of objects. + @param objects The objects to remove. + */ + public void remove(Collection<? extends GMLObject> objects) { + for (GMLObject next : objects) { + remove(next); + } + } + + /** + Remove a set of objects. + @param objects The objects to remove. + */ + public void remove(GMLObject... objects) { + for (GMLObject next : objects) { + remove(next); + } + } + + /** + Remove all nodes, edges and shapes. + */ + public void removeAllNodes() { + nodes.clear(); + edges.clear(); + roads.clear(); + buildings.clear(); + spaces.clear(); + allShapes.clear(); + allObjects.clear(); + attachedEdges.clear(); + attachedShapes.clear(); + boundsKnown = false; + } + + /** + Remove all edges and shapes. + */ + public void removeAllEdges() { + edges.clear(); + roads.clear(); + buildings.clear(); + spaces.clear(); + allShapes.clear(); + allObjects.retainAll(nodes.values()); + attachedEdges.clear(); + attachedShapes.clear(); + } + + /** + Remove all buildings. + */ + public void removeAllBuildings() { + for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) { + entry.getValue().removeAll(buildings.values()); + } + allShapes.removeAll(buildings.values()); + allObjects.removeAll(buildings.values()); + buildings.clear(); + } + + /** + Remove all roads. + */ + public void removeAllRoads() { + for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) { + entry.getValue().removeAll(buildings.values()); + } + allShapes.removeAll(roads.values()); + allObjects.removeAll(roads.values()); + roads.clear(); + } + + /** + Remove all spaces. + */ + public void removeAllSpaces() { + for (Map.Entry<GMLEdge, Collection<GMLShape>> entry : attachedShapes.entrySet()) { + entry.getValue().removeAll(buildings.values()); + } + allShapes.removeAll(spaces.values()); + allObjects.removeAll(spaces.values()); + spaces.clear(); + } + + /** + Get a node by ID. + @param id The ID to look up. + @return The node with that ID or null if the ID is not found. + */ + public GMLNode getNode(int id) { + return nodes.get(id); + } + + /** + Get an edge by ID. + @param id The ID to look up. + @return The edge with that ID or null if the ID is not found. + */ + public GMLEdge getEdge(int id) { + return edges.get(id); + } + + /** + Get a building by ID. + @param id The ID to look up. + @return The building with that ID or null if the ID is not found. + */ + public GMLBuilding getBuilding(int id) { + return buildings.get(id); + } + + /** + Get a road by ID. + @param id The ID to look up. + @return The road with that ID or null if the ID is not found. + */ + public GMLRoad getRoad(int id) { + return roads.get(id); + } + + /** + Get a space by ID. + @param id The ID to look up. + @return The space with that ID or null if the ID is not found. + */ + public GMLSpace getSpace(int id) { + return spaces.get(id); + } + + /** + Get a shape by ID. + @param id The ID to look up. + @return The shape with that ID or null if the ID is not found. + */ + public GMLShape getShape(int id) { + GMLBuilding b = getBuilding(id); + if (b != null) { + return b; + } + GMLRoad r = getRoad(id); + if (r != null) { + return r; + } + GMLSpace s = getSpace(id); + if (s != null) { + return s; + } + return null; + } + + /** + Get an object by ID. + @param id The ID to look up. + @return The object with that ID or null if the ID is not found. + */ + public GMLObject getObject(int id) { + GMLNode n = getNode(id); + if (n != null) { + return n; + } + GMLEdge e = getEdge(id); + if (e != null) { + return e; + } + GMLBuilding b = getBuilding(id); + if (b != null) { + return b; + } + GMLRoad r = getRoad(id); + if (r != null) { + return r; + } + GMLSpace s = getSpace(id); + if (s != null) { + return s; + } + return null; + } + + /** + Get all nodes in the map. + @return All nodes. + */ + public Set<GMLNode> getNodes() { + return new HashSet<GMLNode>(nodes.values()); + } + + /** + Get all edges in the map. + @return All edges. + */ + public Set<GMLEdge> getEdges() { + return new HashSet<GMLEdge>(edges.values()); + } + + /** + Get all buildings in the map. + @return All buildings. + */ + public Set<GMLBuilding> getBuildings() { + return new HashSet<GMLBuilding>(buildings.values()); + } + + /** + Get all roads in the map. + @return All roads. + */ + public Set<GMLRoad> getRoads() { + return new HashSet<GMLRoad>(roads.values()); + } + + /** + Get all spaces in the map. + @return All spaces. + */ + public Set<GMLSpace> getSpaces() { + return new HashSet<GMLSpace>(spaces.values()); + } + + /** + Get all shapes in the map. + @return All shapes. + */ + public Set<GMLShape> getAllShapes() { + return Collections.unmodifiableSet(allShapes); + } + + /** + Get all objects in the map. + @return All objects. + */ + public Set<GMLObject> getAllObjects() { + return Collections.unmodifiableSet(allObjects); + } + + /** + Get the minimum x coordinate. + @return The minimum x coordinate. + */ + public double getMinX() { + calculateBounds(); + return minX; + } + + /** + Get the maximum x coordinate. + @return The maximum x coordinate. + */ + public double getMaxX() { + calculateBounds(); + return maxX; + } + + /** + Get the minimum y coordinate. + @return The minimum y coordinate. + */ + public double getMinY() { + calculateBounds(); + return minY; + } + + /** + Get the maximum y coordinate. + @return The maximum y coordinate. + */ + public double getMaxY() { + calculateBounds(); + return maxY; + } + + /** + Find out if this map has a real size or not. Maps with zero or one nodes do not have a real size. + @return True if this map has two or more nodes. + */ + public boolean hasSize() { + return nodes.size() > 1; + } + + /** + Rescale the map coordinates. + @param conversion The coordinate conversion to apply. + */ + public void convertCoordinates(CoordinateConversion conversion) { + for (GMLNode next : nodes.values()) { + next.convert(conversion); + } + boundsKnown = false; + } + + /** + Create or retrieve an existing edge between two nodes. + @param first The 'start' node. + @param second The 'end' node. + @return A new GMLEdge with a unique ID or an existing edge. The returned edge may be reversed with respect to first and second. + */ + public GMLEdge ensureEdge(GMLNode first, GMLNode second) { + for (GMLEdge next : edges.values()) { + if ((next.getStart().equals(first) && next.getEnd().equals(second)) + || (next.getStart().equals(second) && next.getEnd().equals(first)) + ) { + return next; + } + } + return createEdge(first, second); + } + + /** + Turn a list of apexes into a list of directed edges. + @param apexes The apexes to convert. + @return A list of directed edges. + */ + public List<GMLDirectedEdge> apexesToEdges(GMLNode... apexes) { + return apexesToEdges(Arrays.asList(apexes)); + } + + /** + Turn a list of apexes into a list of directed edges. + @param apexes The apexes to convert. + @return A list of directed edges. + */ + public List<GMLDirectedEdge> apexesToEdges(List<GMLNode> apexes) { + List<GMLDirectedEdge> edgeList = new ArrayList<GMLDirectedEdge>(apexes.size()); + Iterator<GMLNode> it = apexes.iterator(); + GMLNode first = it.next(); + GMLNode previous = first; + while (it.hasNext()) { + GMLNode next = it.next(); + GMLEdge edge = ensureEdge(previous, next); + edgeList.add(new GMLDirectedEdge(edge, previous)); + previous = next; + } + GMLEdge edge = ensureEdge(previous, first); + edgeList.add(new GMLDirectedEdge(edge, previous)); + return edgeList; + } + + /** + Get all GMLNodes inside a region. + @param xMin The lower X bound of the region. + @param yMin The lower Y bound of the region. + @param xMax The upper X bound of the region. + @param yMax The upper Y bound of the region. + @return All GMLNodes inside the region. + */ + public Collection<GMLNode> getNodesInRegion(double xMin, double yMin, double xMax, double yMax) { + Collection<GMLNode> result = new ArrayList<GMLNode>(); + for (GMLNode next : nodes.values()) { + double x = next.getX(); + double y = next.getY(); + if (x >= xMin && x <= xMax && y >= yMin && y <= yMax) { + result.add(next); + } + } + return result; + } + + /** + Find the GMLNode nearest a point. + @param x The X coordinate. + @param y The Y coordinate. + @return The nearest GMLNode. + */ + public GMLNode findNearestNode(double x, double y) { + GMLNode best = null; + double bestDistance = Double.NaN; + for (GMLNode next : nodes.values()) { + double dx = x - next.getX(); + double dy = y - next.getY(); + double d = (dx * dx) + (dy * dy); + if (best == null || d < bestDistance) { + best = next; + bestDistance = d; + } + } + return best; + } + + /** + Find the GMLEdge nearest a point. + @param x The X coordinate. + @param y The Y coordinate. + @return The nearest GMLEdge. + */ + public GMLEdge findNearestEdge(double x, double y) { + return findNearestEdge(x, y, edges.values()); + } + + /** + Find the GMLEdge nearest a point from a set of possible edges. + @param x The X coordinate. + @param y The Y coordinate. + @param possible The set of possible edges. + @return The nearest GMLEdge. + */ + public GMLEdge findNearestEdge(double x, double y, Collection<? extends GMLEdge> possible) { + GMLEdge best = null; + double bestDistance = Double.NaN; + Point2D test = new Point2D(x, y); + for (GMLEdge next : possible) { + Line2D line = GMLTools.toLine(next); + Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, test); + double d = GeometryTools2D.getDistance(test, closest); + if (best == null || d < bestDistance) { + best = next; + bestDistance = d; + } + } + return best; + } + + /** + Find the GMLShape under a point. + @param x The X coordinate. + @param y The Y coordinate. + @return The shape under the point or null if no shapes are found. + */ + public GMLShape findShapeUnder(double x, double y) { + for (GMLShape next : allShapes) { + if (GMLTools.coordsToShape(next.getUnderlyingCoordinates()).contains(x, y)) { + return next; + } + } + return null; + } + + /** + Get all GMLEdges that are attached to a GMLNode. + @param node The GMLNode to look up. + @return All attached GMLEdges. + */ + public Collection<GMLEdge> getAttachedEdges(GMLNode node) { + return Collections.unmodifiableCollection(attachedEdges.get(node)); + } + + /** + Get all GMLShapes that are attached to a GMLEdge. + @param edge The GMLEdge to look up. + @return All attached GMLShapes. + */ + public Collection<GMLShape> getAttachedShapes(GMLEdge edge) { + return Collections.unmodifiableCollection(attachedShapes.get(edge)); + } + + /** + Merge a pair of edges and form a new edge. The node and existing edges are not removed from the map. + @param edge1 The first edge. + @param edge2 The second edge. + @return The new edge. + */ + public GMLEdge mergeEdges(GMLEdge edge1, GMLEdge edge2) { + GMLNode commonNode = edge1.getStart(); + if (!commonNode.equals(edge2.getStart()) && !commonNode.equals(edge2.getEnd())) { + commonNode = edge1.getEnd(); + } + if (!commonNode.equals(edge2.getStart()) && !commonNode.equals(edge2.getEnd())) { + throw new IllegalArgumentException("Edges " + edge1 + " and " + edge2 + " do not have a common node"); + } + GMLNode start = commonNode.equals(edge1.getStart()) ? edge1.getEnd() : edge1.getStart(); + GMLNode end = commonNode.equals(edge2.getStart()) ? edge2.getEnd() : edge2.getStart(); + return ensureEdge(start, end); + } + + /** + Replace all references to a node with another node. This does not delete the old node from the map. + @param oldNode The node to replace. + @param newNode The new node. + */ + public void replaceNode(GMLNode oldNode, GMLNode newNode) { + List<GMLEdge> attached = new ArrayList<GMLEdge>(getAttachedEdges(oldNode)); + for (GMLEdge next : attached) { + if (next.getStart().equals(oldNode)) { + next.setStart(newNode); + attachedEdges.get(oldNode).remove(next); + attachedEdges.get(newNode).add(next); + } + if (next.getEnd().equals(oldNode)) { + next.setEnd(newNode); + attachedEdges.get(oldNode).remove(next); + attachedEdges.get(newNode).add(next); + } + } + } + + /** + Replace all references to an edge with another edge. This does not delete the old edge from the map. The two edges must have the same pair of start and end nodes but may be in different directions. + @param oldEdge The edge to replace. + @param newEdge The new edge. + */ + public void replaceEdge(GMLEdge oldEdge, GMLEdge newEdge) { + if ((oldEdge.getStart() != newEdge.getStart() && oldEdge.getStart() != newEdge.getEnd()) + || (oldEdge.getEnd() != newEdge.getStart() && oldEdge.getEnd() != newEdge.getEnd())) { + throw new IllegalArgumentException("oldEdge and newEdge do not share start and end nodes"); + } + Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(oldEdge)); + for (GMLShape next : attached) { + for (GMLDirectedEdge dEdge : next.getEdges()) { + if (dEdge.getEdge() == oldEdge) { + boolean forward; + if (oldEdge.getStart() == newEdge.getStart()) { + forward = dEdge.isForward(); + } + else { + forward = !dEdge.isForward(); + } + GMLDirectedEdge replacement = new GMLDirectedEdge(newEdge, forward); + next.replaceEdge(dEdge, replacement); + attachedShapes.get(oldEdge).remove(next); + attachedShapes.get(newEdge).add(next); + } + } + } + } + + /** + Insert a node into an edge. This method updates attached edges but does not delete the old edge. + @param edge The edge to split. + @param node The node to insert. + @return The two new edges. + */ + public Collection<GMLEdge> splitEdge(GMLEdge edge, GMLNode node) { + Collection<GMLEdge> result = new ArrayList<GMLEdge>(2); + GMLEdge first = ensureEdge(edge.getStart(), node); + GMLEdge second = ensureEdge(node, edge.getEnd()); + result.add(first); + result.add(second); + // Update any attached edges + Collection<GMLShape> attached = new HashSet<GMLShape>(getAttachedShapes(edge)); + for (GMLShape shape : attached) { + for (GMLDirectedEdge dEdge : shape.getEdges()) { + if (dEdge.getEdge() == edge) { + // Create two new directed edges + GMLDirectedEdge d1; + GMLDirectedEdge d2; + if (dEdge.isForward()) { + d1 = new GMLDirectedEdge(first, true); + d2 = new GMLDirectedEdge(second, true); + } + else { + d1 = new GMLDirectedEdge(second, false); + d2 = new GMLDirectedEdge(first, false); + } + shape.replaceEdge(dEdge, d1, d2); + attachedShapes.get(edge).remove(shape); + attachedShapes.get(first).add(shape); + attachedShapes.get(second).add(shape); + } + } + } + return result; + } + + private void addShape(GMLShape shape) { + addObject(shape); + allShapes.add(shape); + for (GMLDirectedEdge edge : shape.getEdges()) { + addEdge(edge.getEdge()); + attachedShapes.get(edge.getEdge()).add(shape); + } + } + + private void addObject(GMLObject object) { + allObjects.add(object); + nextID = Math.max(nextID, object.getID() + 1); + } + + private void removeShape(GMLShape shape) { + removeObject(shape); + allShapes.remove(shape); + for (GMLDirectedEdge edge : shape.getEdges()) { + attachedShapes.get(edge.getEdge()).remove(shape); + } + } + + private void removeObject(GMLObject object) { + allObjects.remove(object); + } + + private void calculateBounds() { + if (boundsKnown) { + return; + } + minX = Double.POSITIVE_INFINITY; + minY = Double.POSITIVE_INFINITY; + maxX = Double.NEGATIVE_INFINITY; + maxY = Double.NEGATIVE_INFINITY; + for (GMLNode n : nodes.values()) { + GMLCoordinates c = n.getCoordinates(); + minX = Math.min(minX, c.getX()); + maxX = Math.max(maxX, c.getX()); + minY = Math.min(minY, c.getY()); + maxY = Math.max(maxY, c.getY()); + } + boundsKnown = true; + } +} diff --git a/modules/maps/src/maps/gml/GMLMapFormat.java b/modules/maps/src/maps/gml/GMLMapFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..d8ad6b3ce4d249a97f82f8b8abe93dec130feeae --- /dev/null +++ b/modules/maps/src/maps/gml/GMLMapFormat.java @@ -0,0 +1,177 @@ +package maps.gml; + +import java.io.File; +import java.io.Reader; +import java.io.FileReader; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; + +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; +import org.dom4j.io.OutputFormat; + +import maps.MapFormat; +import maps.MapException; +import maps.Map; + +import rescuecore2.log.Logger; + +/** + Abstract base class for map formats that use GML. +*/ +public abstract class GMLMapFormat implements MapFormat { + @Override + public GMLMap read(File file) throws MapException { + FileReader r; + try { + r = new FileReader(file); + } + catch (FileNotFoundException e) { + throw new MapException(e); + } + try { + return read(r); + } + catch (DocumentException e) { + throw new MapException(e); + } + finally { + try { + r.close(); + } + catch (IOException e) { + Logger.warn("IOException while closing file reader", e); + } + } + } + + @Override + public void write(Map map, File file) throws MapException { + if (map == null) { + throw new IllegalArgumentException("Map must not be null"); + } + if (file == null) { + throw new IllegalArgumentException("File must not be null"); + } + if (!(map instanceof GMLMap)) { + throw new IllegalArgumentException("Map is not a GMLMap: " + map.getClass().getName()); + } + Document doc = write((GMLMap)map); + try { + if (!file.exists()) { + File parent = file.getParentFile(); + if (!parent.exists()) { + if (!file.getParentFile().mkdirs()) { + throw new MapException("Couldn't create file " + file.getPath()); + } + } + if (!file.createNewFile()) { + throw new MapException("Couldn't create file " + file.getPath()); + } + } + XMLWriter writer = new XMLWriter(new FileOutputStream(file), OutputFormat.createPrettyPrint()); + Element root = doc.getRootElement(); + for (java.util.Map.Entry<String, String> next : getNamespaces().entrySet()) { + root.addNamespace(next.getKey(), next.getValue()); + } + writer.write(doc); + writer.flush(); + writer.close(); + } + catch (IOException e) { + throw new MapException(e); + } + } + + @Override + public boolean canRead(File file) throws MapException { + if (file.isDirectory() || !file.exists()) { + return false; + } + if (!file.getName().endsWith(".gml")) { + return false; + } + // Check that the XML dialect is correct by looking at the root element. + FileReader r; + try { + r = new FileReader(file); + } + catch (FileNotFoundException e) { + throw new MapException(e); + } + try { + XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(r); + while (reader.hasNext()) { + if (reader.next() == XMLStreamConstants.START_ELEMENT) { + return isCorrectRootElement(reader.getNamespaceURI(), reader.getLocalName()); + } + } + } + catch (XMLStreamException e) { + Logger.debug("Exception while reading XML stream", e); + return false; + } + finally { + try { + r.close(); + } + catch (IOException e) { + Logger.warn("IOException while closing file reader", e); + } + } + return false; + } + + /** + Read a GMLMap from a Reader. + @param reader The Reader to read. + @return A new GMLMap. + @throws DocumentException If there is a problem parsing the XML. + @throws MapException If there is a problem reading the map. + */ + public GMLMap read(Reader reader) throws DocumentException, MapException { + Logger.debug("Parsing GML"); + SAXReader saxReader = new SAXReader(); + Document doc = saxReader.read(reader); + Logger.debug("Building map"); + return read(doc); + } + + /** + Find out if the root element is correct for this format type. + @param uri The URI of the root element. + @param localName The local name of the root element. + @return True if the uri and localName are correct for this format's root element, false otherwise. + */ + protected abstract boolean isCorrectRootElement(String uri, String localName); + + /** + Read a Document and return a GMLMap. + @param doc The document to read. + @return A new GMLMap. + @throws MapException If there is a problem reading the map. + */ + protected abstract GMLMap read(Document doc) throws MapException; + + /** + Turn a GMLMap into an xml document. + @param map The map to write. + @return A new document. + */ + protected abstract Document write(GMLMap map); + + /** + Get the uris and preferred prefixes for all namespaces this format cares about. + @return A map from prefix to uri for all relevant namespaces. + */ + protected abstract java.util.Map<String, String> getNamespaces(); +} diff --git a/modules/maps/src/maps/gml/GMLNode.java b/modules/maps/src/maps/gml/GMLNode.java new file mode 100644 index 0000000000000000000000000000000000000000..92e66dd05312744c1217ae1d1af24821e34cdb79 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLNode.java @@ -0,0 +1,82 @@ +package maps.gml; + +import maps.CoordinateConversion; + +/** + A GML node object. + */ +public class GMLNode extends GMLObject { + private GMLCoordinates coordinates; + + /** + Construct a new GML node. + @param id The ID of this node. + @param x The x coordinate of this node. + @param y The y coordinate of this node. + */ + public GMLNode(int id, double x, double y) { + this(id, new GMLCoordinates(x, y)); + } + + /** + Construct a new GML node. + @param id The ID of this node. + @param coordinates The coordinates of this node. + */ + public GMLNode(int id, GMLCoordinates coordinates) { + super(id); + this.coordinates = coordinates; + } + + /** + Get the coordinates of this node. + @return The node coordinates. + */ + public GMLCoordinates getCoordinates() { + return coordinates; + } + + /** + Set the coordinates of this node. + @param c The new coordinates. + */ + public void setCoordinates(GMLCoordinates c) { + if (c == null) { + throw new IllegalArgumentException("Coordinates cannot be null"); + } + this.coordinates = c; + } + + /** + Get the X coordinate. + @return The X coordinate. + */ + public double getX() { + return coordinates.getX(); + } + + /** + Get the Y coordinate. + @return The Y coordinate. + */ + public double getY() { + return coordinates.getY(); + } + + /** + Apply a CoordinateConversion to this node. + @param c The conversion to apply. + */ + public void convert(CoordinateConversion c) { + double oldX = coordinates.getX(); + double oldY = coordinates.getY(); + double newX = c.convertX(oldX); + double newY = c.convertY(oldY); + coordinates = new GMLCoordinates(newX, newY); + } + + @Override + public String toString() { + return "GMLNode " + getID() + " at " + coordinates; + } +} diff --git a/modules/maps/src/maps/gml/GMLObject.java b/modules/maps/src/maps/gml/GMLObject.java new file mode 100644 index 0000000000000000000000000000000000000000..92f5f93ef50eb37bff81d0190cadc007eecc50f2 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLObject.java @@ -0,0 +1,37 @@ +package maps.gml; + +/** + A GML map object. +*/ +public abstract class GMLObject { + private int id; + + /** + Construct a GML object. + @param id The id of the object. + */ + protected GMLObject(int id) { + this.id = id; + } + + /** + Get this object's ID. + @return The object ID. + */ + public int getID() { + return id; + } + + @Override + public int hashCode() { + return (int)id; + } + + @Override + public boolean equals(Object o) { + if (o instanceof GMLObject) { + return this.id == ((GMLObject)o).id; + } + return false; + } +} diff --git a/modules/maps/src/maps/gml/GMLRefuge.java b/modules/maps/src/maps/gml/GMLRefuge.java new file mode 100644 index 0000000000000000000000000000000000000000..6993f324b1f6ae8a18bb47663cad6dfa82085028 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLRefuge.java @@ -0,0 +1,97 @@ +package maps.gml; + +import java.util.List; + +public class GMLRefuge extends GMLBuilding { + + private int bedCapacity; + private int refillCapacity; + + + /** + * Construct a GMLRefuge. + * + * @param id + * The ID of the Refuge. + */ + public GMLRefuge( int id ) { + super( id ); + } + + + /** + * Construct a GMLRefuge. + * + * @param id + * The ID of the Refuge. + * @param edges + * The edges of the Refuge. + */ + public GMLRefuge( int id, List<GMLDirectedEdge> edges ) { + super( id, edges ); + } + + + /** + * Construct a GMLRefuge. + * + * @param id + * The ID of the Refuge. + * @param edges + * The edges of the Refuge. + * @param neighbours + * The neighbours of each edge. + */ + public GMLRefuge( int id, List<GMLDirectedEdge> edges, List<Integer> neighbours ) { + super( id, edges, neighbours ); + } + + + @Override + public String toString() { + return "GMLRefuge " + getID(); + } + + + /** + * Set the bed capacity of this Refuge. + * + * @param newCapacity + * The new bed capacity of the Refuge. + */ + public void setBedCapacity( int newCapacity ) { + bedCapacity = newCapacity; + } + + + /** + * Get the bed capacity of this Refuge. + * + * @return The the bed capacity of Refuge. + */ + public int getBedCapacity() { + return bedCapacity; + } + + + /** + * Set the refill capacity of this Refuge. + * + * @param newCapacity + * The new refill capacity of the Refuge. + */ + public void setRefillCapacity( int newCapacity ) { + refillCapacity = newCapacity; + } + + + /** + * Get the refill capacity of this Refuge. + * + * @return The the refill capacity of Refuge. + */ + public int getRefillCapacity() { + return refillCapacity; + } + +} diff --git a/modules/maps/src/maps/gml/GMLRoad.java b/modules/maps/src/maps/gml/GMLRoad.java new file mode 100644 index 0000000000000000000000000000000000000000..2b9ded80ca5a0271a131c027031d22c961c9dd5c --- /dev/null +++ b/modules/maps/src/maps/gml/GMLRoad.java @@ -0,0 +1,40 @@ +package maps.gml; + +import java.util.List; + +/** + A road in GML space. +*/ +public class GMLRoad extends GMLShape { + /** + Construct a GMLRoad. + @param id The ID of the road. + */ + public GMLRoad(int id) { + super(id); + } + + /** + Construct a GMLRoad. + @param id The ID of the road. + @param edges The edges of the road. + */ + public GMLRoad(int id, List<GMLDirectedEdge> edges) { + super(id, edges); + } + + /** + Construct a GMLRoad. + @param id The ID of the road. + @param edges The edges of the road. + @param neighbours The neighbours of each edge. + */ + public GMLRoad(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) { + super(id, edges, neighbours); + } + + @Override + public String toString() { + return "GMLRoad " + getID(); + } +} diff --git a/modules/maps/src/maps/gml/GMLShape.java b/modules/maps/src/maps/gml/GMLShape.java new file mode 100644 index 0000000000000000000000000000000000000000..7b6313efe0728c4c089347b56b86fa09ed1ad740 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLShape.java @@ -0,0 +1,283 @@ +package maps.gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Collections; + +import java.awt.geom.Rectangle2D; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + Abstract base class for shapes in GML space. +*/ +public abstract class GMLShape extends GMLObject { + private List<GMLDirectedEdge> edges; + private Map<GMLDirectedEdge, Integer> neighbours; + private List<GMLCoordinates> points; + private Rectangle2D bounds; + private Point2D centroid; + + /** + Construct a GMLShape. + @param id The ID of the shape. + */ + protected GMLShape(int id) { + super(id); + this.edges = new ArrayList<GMLDirectedEdge>(); + neighbours = new HashMap<GMLDirectedEdge, Integer>(); + bounds = null; + centroid = null; + } + + /** + Construct a GMLShape. + @param id The ID of the shape. + @param edges The edges of the shape. + */ + protected GMLShape(int id, List<GMLDirectedEdge> edges) { + this(id); + this.edges.addAll(edges); + points = getUnderlyingCoordinates(); + } + + /** + Construct a GMLShape. + @param id The ID of the shape. + @param edges The edges of the shape. + @param neighbours The neighbours of each edge. + */ + protected GMLShape(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) { + this(id, edges); + Iterator<GMLDirectedEdge> it = edges.iterator(); + Iterator<Integer> ix = neighbours.iterator(); + while (it.hasNext() && ix.hasNext()) { + setNeighbour(it.next(), ix.next()); + } + points = getUnderlyingCoordinates(); + } + + /** + Get the edges of this shape. + @return The edges. + */ + public List<GMLDirectedEdge> getEdges() { + return new ArrayList<GMLDirectedEdge>(edges); + } + + /** + Set the list of edges. + @param newEdges The new edge list. + */ + public void setEdges(List<GMLDirectedEdge> newEdges) { + edges.clear(); + neighbours.clear(); + edges.addAll(newEdges); + bounds = null; + centroid = null; + points = getUnderlyingCoordinates(); + } + + /** + Reorder the list of edges. This will not clear the neighbour map or the bounds. + @param newEdges The reordered edge list. + */ + public void reorderEdges(List<GMLDirectedEdge> newEdges) { + edges.clear(); + edges.addAll(newEdges); + points = getUnderlyingCoordinates(); + centroid = null; + neighbours.keySet().retainAll(newEdges); + } + + /** + Replace a GMLDirectedEdge with a set of new edges. The neighbour of the old edge will be set for each of the new edges. + @param oldEdge The edge to replace. + @param newEdges The new edges. + */ + public void replaceEdge(GMLDirectedEdge oldEdge, GMLDirectedEdge... newEdges) { + ListIterator<GMLDirectedEdge> it = edges.listIterator(); + while (it.hasNext()) { + if (it.next() == oldEdge) { + it.remove(); + for (GMLDirectedEdge e : newEdges) { + it.add(e); + } + } + } + bounds = null; + centroid = null; + points = getUnderlyingCoordinates(); + } + + /** + Remove an edge from this shape. + @param edge The underlying edge to remove. + */ + public void removeEdge(GMLEdge edge) { + for (Iterator<GMLDirectedEdge> it = edges.iterator(); it.hasNext();) { + GMLDirectedEdge dEdge = it.next(); + if (dEdge.getEdge().equals(edge)) { + it.remove(); + neighbours.remove(dEdge); + } + } + bounds = null; + centroid = null; + points = getUnderlyingCoordinates(); + } + + /** + Get the ID of the neighbour through a particular edge. + @param edge The edge to look up. + @return The ID of the neighbour through that edge or null. + */ + public Integer getNeighbour(GMLDirectedEdge edge) { + return neighbours.get(edge); + } + + /** + Set the ID of the neighbour through a particular edge. + @param edge The edge to set the neighbour of. + @param neighbour The new neighbour ID for that edge. This may be null. + */ + public void setNeighbour(GMLDirectedEdge edge, Integer neighbour) { + if (neighbour == null) { + neighbours.remove(edge); + } + else { + neighbours.put(edge, neighbour); + } + } + + /** + Find out if an edge has a neighbour. + @param edge The edge to look up. + @return True if there is a neighbour through that edge or false otherwise. + */ + public boolean hasNeighbour(GMLDirectedEdge edge) { + return neighbours.containsKey(edge); + } + + /** + Get the ID of the neighbour through a particular edge. + @param edge The edge to look up. + @return The ID of the neighbour through that edge or null. + */ + public Integer getNeighbour(GMLEdge edge) { + return getNeighbour(findDirectedEdge(edge)); + } + + /** + Set the ID of the neighbour through a particular edge. + @param edge The edge to set the neighbour of. + @param neighbour The new neighbour ID for that edge. This may be null. + */ + public void setNeighbour(GMLEdge edge, Integer neighbour) { + setNeighbour(findDirectedEdge(edge), neighbour); + } + + /** + Find out if an edge has a neighbour. + @param edge The edge to look up. + @return True if there is a neighbour through that edge or false otherwise. + */ + public boolean hasNeighbour(GMLEdge edge) { + return neighbours.containsKey(findDirectedEdge(edge)); + } + + /** + Get the coordinates of the edges that make up this shape. + @return The underlying edge coordinates. + */ + public List<GMLCoordinates> getUnderlyingCoordinates() { + List<GMLCoordinates> result = new ArrayList<GMLCoordinates>(); + for (GMLDirectedEdge next : edges) { + result.add(next.getStartCoordinates()); + } + return result; + } + + /** + Get the nodes of the edges that make up this shape. + @return The underlying nodes. + */ + public List<GMLNode> getUnderlyingNodes() { + List<GMLNode> result = new ArrayList<GMLNode>(); + for (GMLDirectedEdge next : edges) { + result.add(next.getStartNode()); + } + return result; + } + + /** + Get the coordinates of the apexes of this shape. + @return The apex coordinates. + */ + public List<GMLCoordinates> getCoordinates() { + return Collections.unmodifiableList(points); + } + + /** + Set the coordinates of the apexes of this shape. + @param newPoints The new apex coordinates. + */ + public void setCoordinates(List<GMLCoordinates> newPoints) { + points.clear(); + points.addAll(newPoints); + bounds = null; + centroid = null; + } + + /** + Get the x coordinate of the centroid of this shape. + @return The x coordinate of the centroid. + */ + public double getCentreX() { + return getCentroid().getX(); + } + + /** + Get the y coordinate of the centroid of this shape. + @return The y coordinate of the centroid. + */ + public double getCentreY() { + return getCentroid().getY(); + } + + /** + Get the bounds of this shape. + @return The bounds of the shape. + */ + public Rectangle2D getBounds() { + if (bounds == null) { + bounds = GMLTools.getBounds(getCoordinates()); + } + return bounds; + } + + /** + Get the centroid of this shape. + @return The centroid of the shape. + */ + public Point2D getCentroid() { + if (centroid == null) { + centroid = GeometryTools2D.computeCentroid(GMLTools.coordinatesAsPoints(getCoordinates())); + } + return centroid; + } + + private GMLDirectedEdge findDirectedEdge(GMLEdge e) { + for (GMLDirectedEdge next : edges) { + if (next.getEdge().equals(e)) { + return next; + } + } + throw new IllegalArgumentException(this + ": Edge " + e + " not found"); + } +} diff --git a/modules/maps/src/maps/gml/GMLSpace.java b/modules/maps/src/maps/gml/GMLSpace.java new file mode 100644 index 0000000000000000000000000000000000000000..a3b4be62bc7a2365328617c59cf0566d2b247dc8 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLSpace.java @@ -0,0 +1,35 @@ +package maps.gml; + +import java.util.List; + +/** + An open space in GML space. +*/ +public class GMLSpace extends GMLShape { + /** + Construct a GMLSpace. + @param id The ID of the space. + */ + public GMLSpace(int id) { + super(id); + } + + /** + Construct a GMLSpace. + @param id The ID of the space. + @param edges The edges of the space. + */ + public GMLSpace(int id, List<GMLDirectedEdge> edges) { + super(id, edges); + } + + /** + Construct a GMLSpace. + @param id The ID of the space. + @param edges The edges of the space. + @param neighbours The neighbours of each edge. + */ + public GMLSpace(int id, List<GMLDirectedEdge> edges, List<Integer> neighbours) { + super(id, edges, neighbours); + } +} diff --git a/modules/maps/src/maps/gml/GMLTools.java b/modules/maps/src/maps/gml/GMLTools.java new file mode 100644 index 0000000000000000000000000000000000000000..25ed148ad0f67ff3e3f95a5a8ea5519205d8e6f2 --- /dev/null +++ b/modules/maps/src/maps/gml/GMLTools.java @@ -0,0 +1,178 @@ +package maps.gml; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Path2D; +import java.awt.Shape; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +/** + Useful tools for manipulating GML. +*/ +public final class GMLTools { + private GMLTools() { + } + + /** + Turn a list of coordinates into a string suitable for putting into an XML document. + @param coords The coordinate list. + @return A string version of the list. + */ + public static String getCoordinatesString(List<GMLCoordinates> coords) { + StringBuilder result = new StringBuilder(); + for (Iterator<GMLCoordinates> it = coords.iterator(); it.hasNext();) { + GMLCoordinates next = it.next(); + result.append(String.valueOf(next.getX())); + result.append(","); + result.append(String.valueOf(next.getY())); + if (it.hasNext()) { + result.append(" "); + } + } + return result.toString(); + } + + /** + Turn a coordinates string into a list of GMLCoordinates. + @param coords The coordinates string. + @return A list of GMLCoordinates. + */ + public static List<GMLCoordinates> getCoordinatesList(String coords) { + List<GMLCoordinates> result = new ArrayList<GMLCoordinates>(); + StringTokenizer tokens = new StringTokenizer(coords, " "); + while (tokens.hasMoreTokens()) { + result.add(new GMLCoordinates(tokens.nextToken())); + } + return result; + } + + /** + Convert a list of GMLCoordinates to Point2D objects. + @param coords The GMLCoordinates to convert. + @return A list of Point2D objects. + */ + public static List<Point2D> coordinatesAsPoints(List<GMLCoordinates> coords) { + List<Point2D> result = new ArrayList<Point2D>(coords.size()); + for (GMLCoordinates next : coords) { + result.add(new Point2D(next.getX(), next.getY())); + } + return result; + } + + /** + Get the bounds of a set of coordinates. + @param coords The coordinate list. + @return The bounds of the coordinates. + */ + public static Rectangle2D getBounds(List<GMLCoordinates> coords) { + if (coords.isEmpty()) { + return null; + } + double minX = Double.POSITIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY; + double maxX = Double.NEGATIVE_INFINITY; + double maxY = Double.NEGATIVE_INFINITY; + for (GMLCoordinates next : coords) { + minX = Math.min(minX, next.getX()); + minY = Math.min(minY, next.getY()); + maxX = Math.max(maxX, next.getX()); + maxY = Math.max(maxY, next.getY()); + } + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } + + /** + Get the bounds of a set of gml objects. + @param objects The object list. + @return The bounds of the objects. + */ + public static Rectangle2D getObjectBounds(List<? extends GMLObject> objects) { + Rectangle2D result = null; + for (GMLObject next : objects) { + result = expand(result, next); + } + return result; + } + + /** + Turn a list of coordinates into a shape. + @param coords The coordinates. + @return A new shape. + */ + public static Shape coordsToShape(List<GMLCoordinates> coords) { + Path2D path = new Path2D.Double(); + Iterator<GMLCoordinates> it = coords.iterator(); + GMLCoordinates c = it.next(); + path.moveTo(c.getX(), c.getY()); + while (it.hasNext()) { + c = it.next(); + path.lineTo(c.getX(), c.getY()); + } + path.closePath(); + return path; + } + + /** + Turn a GMLNode into a Point2D. + @param node The node to convert. + @return A new Point2D. + */ + public static Point2D toPoint(GMLNode node) { + return new Point2D(node.getX(), node.getY()); + } + + /** + Turn a GMLEdge into a Line2D. + @param edge The edge to convert. + @return A new Line2D. + */ + public static Line2D toLine(GMLEdge edge) { + return new Line2D(toPoint(edge.getStart()), toPoint(edge.getEnd())); + } + + private static Rectangle2D expand(Rectangle2D rect, double x, double y) { + if (rect == null) { + return new Rectangle2D.Double(x, y, 0, 0); + } + double newMinX = Math.min(x, rect.getX()); + double newMaxX = Math.max(x, rect.getX() + rect.getWidth()); + double newMinY = Math.min(y, rect.getY()); + double newMaxY = Math.max(y, rect.getY() + rect.getHeight()); + rect.setRect(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY); + return rect; + } + + private static Rectangle2D expand(Rectangle2D rect, GMLNode node) { + return expand(rect, node.getX(), node.getY()); + } + + private static Rectangle2D expand(Rectangle2D rect, GMLEdge edge) { + return expand(expand(rect, edge.getStart()), edge.getEnd()); + } + + private static Rectangle2D expand(Rectangle2D rect, GMLShape shape) { + for (GMLDirectedEdge next : shape.getEdges()) { + rect = expand(rect, next.getEdge()); + } + return rect; + } + + private static Rectangle2D expand(Rectangle2D rect, GMLObject object) { + if (object instanceof GMLNode) { + return expand(rect, (GMLNode)object); + } + if (object instanceof GMLEdge) { + return expand(rect, (GMLEdge)object); + } + if (object instanceof GMLShape) { + return expand(rect, (GMLShape)object); + } + return rect; + } +} diff --git a/modules/maps/src/maps/gml/ViewGMLMap.java b/modules/maps/src/maps/gml/ViewGMLMap.java new file mode 100644 index 0000000000000000000000000000000000000000..e4485765b3d027c57a874e3c0057280031546578 --- /dev/null +++ b/modules/maps/src/maps/gml/ViewGMLMap.java @@ -0,0 +1,49 @@ +package maps.gml; + +import maps.MapReader; +import maps.gml.view.GMLMapViewer; + +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JFrame; + +/** + A GML map viewer. +*/ +public final class ViewGMLMap { + private static final int VIEWER_SIZE = 500; + + private ViewGMLMap() { + } + + /** + Start the viewer. + @param args Command-line arguments: mapname. + */ + public static void main(String[] args) { + if (args.length < 1) { + System.out.println("Usage: ViewGMLMap <mapname>"); + return; + } + try { + GMLMap map = (GMLMap)MapReader.readMap(args[0]); + GMLMapViewer gmlViewer = new GMLMapViewer(map); + JFrame frame = new JFrame("GML Map"); + gmlViewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + frame.setContentPane(gmlViewer); + frame.pack(); + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (Exception e) { + e.printStackTrace(); + } + // CHECKSTYLE:ON:IllegalCatch + } +} diff --git a/modules/maps/src/maps/gml/debug/GMLDirectedEdgeShapeInfo.java b/modules/maps/src/maps/gml/debug/GMLDirectedEdgeShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..246fb3522023377339c2f04881a4c82a7e112a27 --- /dev/null +++ b/modules/maps/src/maps/gml/debug/GMLDirectedEdgeShapeInfo.java @@ -0,0 +1,47 @@ +package maps.gml.debug; + +import java.awt.Color; + +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLNode; + +/** + A ShapeInfo that knows how to draw GMLDirectedEdges. +*/ +public class GMLDirectedEdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo { + private GMLDirectedEdge edge; + + /** + Create a new GMLDirectedEdgeShapeInfo. + @param edge The directed edge to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + @param arrow Whether to draw the direction arrow or not. + */ + public GMLDirectedEdgeShapeInfo(GMLDirectedEdge edge, String name, Color colour, boolean thick, boolean arrow) { + super(gmlDirectedEdgeToLine(edge), name, colour, thick, arrow); + this.edge = edge; + } + + @Override + public Object getObject() { + return edge; + } + + private static Line2D gmlDirectedEdgeToLine(GMLDirectedEdge edge) { + if (edge == null) { + return null; + } + GMLNode start = edge.getStartNode(); + GMLNode end = edge.getEndNode(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } +} diff --git a/modules/maps/src/maps/gml/debug/GMLEdgeShapeInfo.java b/modules/maps/src/maps/gml/debug/GMLEdgeShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..621d1fa977c07083310ec5b10ac1c07c8c25fb4f --- /dev/null +++ b/modules/maps/src/maps/gml/debug/GMLEdgeShapeInfo.java @@ -0,0 +1,46 @@ +package maps.gml.debug; + +import java.awt.Color; + +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +import maps.gml.GMLEdge; +import maps.gml.GMLNode; + +/** + A ShapeInfo that knows how to draw GMLEdges. +*/ +public class GMLEdgeShapeInfo extends ShapeDebugFrame.Line2DShapeInfo { + private GMLEdge edge; + + /** + Create a new GMLEdgeShapeInfo. + @param edge The edge to draw. + @param name The name of the edge. + @param colour The colour to draw the edge. + @param thick Whether to draw the edge thick or not. + */ + public GMLEdgeShapeInfo(GMLEdge edge, String name, Color colour, boolean thick) { + super(gmlEdgeToLine(edge), name, colour, thick, false); + this.edge = edge; + } + + @Override + public Object getObject() { + return edge; + } + + private static Line2D gmlEdgeToLine(GMLEdge edge) { + if (edge == null) { + return null; + } + GMLNode start = edge.getStart(); + GMLNode end = edge.getEnd(); + Point2D origin = new Point2D(start.getX(), start.getY()); + Point2D endPoint = new Point2D(end.getX(), end.getY()); + return new Line2D(origin, endPoint); + } +} diff --git a/modules/maps/src/maps/gml/debug/GMLNodeShapeInfo.java b/modules/maps/src/maps/gml/debug/GMLNodeShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..31fd623e33da8e929cd550100fc49a356992ab2a --- /dev/null +++ b/modules/maps/src/maps/gml/debug/GMLNodeShapeInfo.java @@ -0,0 +1,37 @@ +package maps.gml.debug; + +import java.awt.Color; + +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.misc.geometry.Point2D; + +import maps.gml.GMLNode; + +/** + A ShapeInfo that knows how to draw GMLNodes. +*/ +public class GMLNodeShapeInfo extends ShapeDebugFrame.Point2DShapeInfo { + private GMLNode node; + + /** + Create a new GMLNodeShapeInfo. + @param node The node to draw. + @param name The name of the node. + @param colour The colour to draw the node. + @param square Whether to draw the node with a square or not. + */ + public GMLNodeShapeInfo(GMLNode node, String name, Color colour, boolean square) { + super(gmlNodeToPoint(node), name, colour, square); + this.node = node; + } + + @Override + public Object getObject() { + return node; + } + + private static Point2D gmlNodeToPoint(GMLNode node) { + return new Point2D(node.getX(), node.getY()); + } +} diff --git a/modules/maps/src/maps/gml/debug/GMLShapeInfo.java b/modules/maps/src/maps/gml/debug/GMLShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e1d5127ab3624f6b1fbb2b901399663207edc406 --- /dev/null +++ b/modules/maps/src/maps/gml/debug/GMLShapeInfo.java @@ -0,0 +1,92 @@ +package maps.gml.debug; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.Polygon; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import java.util.List; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.ShapeDebugFrame; + +import maps.gml.GMLShape; +import maps.gml.GMLTools; +import maps.gml.GMLCoordinates; + +/** + A ShapeInfo that knows how to draw GMLShapes. +*/ +public class GMLShapeInfo extends ShapeDebugFrame.ShapeInfo { + private GMLShape shape; + private Color outlineColour; + private Color fillColour; + private Rectangle2D bounds; + + /** + Create a new GMLShapeInfo. + @param shape The shape to draw. + @param name The name of the shape. + @param outlineColour The colour to draw the outline of the shape. This may be null to indicate that the outline should not be painted. + @param fillColour The colour to draw the interior of the shape. This may be null to indicate that the interior should not be painted. + */ + public GMLShapeInfo(GMLShape shape, String name, Color outlineColour, Color fillColour) { + super(shape, name); + this.shape = shape; + this.outlineColour = outlineColour; + this.fillColour = fillColour; + if (shape != null) { + bounds = GMLTools.getBounds(shape.getCoordinates()); + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (shape == null) { + return null; + } + List<GMLCoordinates> coordinates = shape.getCoordinates(); + int n = coordinates.size(); + int[] xs = new int[n]; + int[] ys = new int[n]; + int i = 0; + for (GMLCoordinates next : coordinates) { + xs[i] = transform.xToScreen(next.getX()); + ys[i] = transform.yToScreen(next.getY()); + ++i; + } + Polygon p = new Polygon(xs, ys, n); + if (outlineColour != null) { + g.setColor(outlineColour); + g.draw(p); + } + if (fillColour != null) { + g.setColor(fillColour); + g.fill(p); + } + return p; + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (outlineColour != null) { + g.setColor(outlineColour); + g.drawRect(0, 0, width - 1, height - 1); + } + if (fillColour != null) { + g.setColor(fillColour); + g.fillRect(0, 0, width, height); + } + } + + @Override + public Rectangle2D getBoundsShape() { + return bounds; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return null; + } +} diff --git a/modules/maps/src/maps/gml/editor/AbstractFunction.java b/modules/maps/src/maps/gml/editor/AbstractFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..3716cf9ca98297a29edaf81868c029f311ce73c8 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/AbstractFunction.java @@ -0,0 +1,17 @@ +package maps.gml.editor; + +/** + Abstract base class for Function implementations. +*/ +public abstract class AbstractFunction implements Function { + /** The GMLEditor instance. */ + protected GMLEditor editor; + + /** + Construct an AbstractFunction. + @param editor The editor instance. + */ + protected AbstractFunction(GMLEditor editor) { + this.editor = editor; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/AbstractTool.java b/modules/maps/src/maps/gml/editor/AbstractTool.java new file mode 100644 index 0000000000000000000000000000000000000000..21fef75d1a90db4cf63882cd9d2ebfd59a7183a4 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/AbstractTool.java @@ -0,0 +1,17 @@ +package maps.gml.editor; + +/** + Abstract base class for Tool implementations. +*/ +public abstract class AbstractTool implements Tool { + /** The GMLEditor instance. */ + protected GMLEditor editor; + + /** + Construct an AbstractTool. + @param editor The editor instance. + */ + protected AbstractTool(GMLEditor editor) { + this.editor = editor; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/AddNoiseFunction.java b/modules/maps/src/maps/gml/editor/AddNoiseFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..fffd09cfb41b5250c5e7131e433dab8b3bf281df --- /dev/null +++ b/modules/maps/src/maps/gml/editor/AddNoiseFunction.java @@ -0,0 +1,52 @@ +package maps.gml.editor; + +import maps.gml.GMLNode; +import maps.gml.GMLCoordinates; + +import java.util.Random; + +import org.uncommons.maths.random.DiscreteUniformGenerator; +import org.uncommons.maths.random.MersenneTwisterRNG; + +/** + A function for adding noise to node coordinates. +*/ +public class AddNoiseFunction extends ProgressFunction { + private static final int RANGE = 5; + private static final double FACTOR = 0.001; + + private Random random; + + /** + Construct an AddNoiseFunction. + @param editor The editor instance. + */ + public AddNoiseFunction(GMLEditor editor) { + super(editor); + random = new MersenneTwisterRNG(); + } + + @Override + public String getName() { + return "Add noise"; + } + + @Override + protected String getTitle() { + return "Adding noise"; + } + + @Override + protected void executeImpl() { + DiscreteUniformGenerator generator = new DiscreteUniformGenerator(-RANGE, RANGE, random); + setProgressLimit(editor.getMap().getNodes().size()); + for (GMLNode next : editor.getMap().getNodes()) { + GMLCoordinates c = next.getCoordinates(); + c.setX(c.getX() + (generator.nextValue() * FACTOR)); + c.setY(c.getY() + (generator.nextValue() * FACTOR)); + bumpProgress(); + } + editor.setChanged(); + editor.getViewer().repaint(); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CancelledByUserException.java b/modules/maps/src/maps/gml/editor/CancelledByUserException.java new file mode 100644 index 0000000000000000000000000000000000000000..ec539fc794400fb83f9809722dad1616b6a78620 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CancelledByUserException.java @@ -0,0 +1,11 @@ +package maps.gml.editor; + +/** + Exception for indicating the the user has cancelled an operation. +*/ +public class CancelledByUserException extends Exception { + /** + Constructor. + */ + public CancelledByUserException() {} +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/ComputePassableEdgesFunction.java b/modules/maps/src/maps/gml/editor/ComputePassableEdgesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..047a54800c775b529fd456a4d230acc13e2d165a --- /dev/null +++ b/modules/maps/src/maps/gml/editor/ComputePassableEdgesFunction.java @@ -0,0 +1,88 @@ +package maps.gml.editor; + +import java.util.Collection; +import java.util.List; +import java.util.Iterator; + +import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLShape; +import maps.gml.GMLRoad; + +import rescuecore2.log.Logger; + +/** + A function for computing passable edges. +*/ +public class ComputePassableEdgesFunction extends ProgressFunction { + /** + Construct a ComputePassableEdgesFunction. + @param editor The editor instance. + */ + public ComputePassableEdgesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Compute passable edges"; + } + + @Override + protected String getTitle() { + return "Finding neighbours"; + } + + @Override + protected void executeImpl() { + final Collection<GMLEdge> edges = editor.getMap().getEdges(); + setProgressLimit(edges.size()); + int passable = 0; + int impassable = 0; + for (GMLEdge next : edges) { + Collection<GMLShape> shapes = editor.getMap().getAttachedShapes(next); + if (shapes.size() == 2) { + Iterator<GMLShape> it = shapes.iterator(); + GMLShape first = it.next(); + GMLShape second = it.next(); + if (first instanceof GMLRoad || second instanceof GMLRoad + || next.isPassable()) { + next.setPassable(true); + GMLDirectedEdge firstEdge = findDirectedEdge(first.getEdges(), next); + GMLDirectedEdge secondEdge = findDirectedEdge(second.getEdges(), next); + first.setNeighbour(firstEdge, second.getID()); + second.setNeighbour(secondEdge, first.getID()); + ++passable; + } + else { + makeImpassable(next, shapes); + ++impassable; + } + } + else { + makeImpassable(next, shapes); + ++impassable; + } + bumpProgress(); + } + editor.setChanged(); + editor.getViewer().repaint(); + Logger.debug("Made " + passable + " edges passable and " + impassable + " impassable"); + } + + private void makeImpassable(GMLEdge edge, Collection<GMLShape> attached) { + edge.setPassable(false); + for (GMLShape shape : attached) { + shape.setNeighbour(edge, null); + } + } + + private GMLDirectedEdge findDirectedEdge(List<GMLDirectedEdge> possible, GMLEdge target) { + for (GMLDirectedEdge next : possible) { + if (next.getEdge() == target) { + return next; + } + } + return null; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateBuildingTool.java b/modules/maps/src/maps/gml/editor/CreateBuildingTool.java new file mode 100644 index 0000000000000000000000000000000000000000..c31fbdb0e51104fb59c93ae785dcf001d29dbed1 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateBuildingTool.java @@ -0,0 +1,55 @@ +package maps.gml.editor; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.UndoableEdit; + +import java.util.List; + +import maps.gml.GMLNode; +import maps.gml.GMLBuilding; + +/** + A tool for creating buildings. +*/ +public class CreateBuildingTool extends CreateShapeTool { + /** + Construct a CreateBuildingTool. + @param editor The editor instance. + */ + public CreateBuildingTool(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Create building"; + } + + @Override + protected UndoableEdit finished(List<GMLNode> nodes) { + GMLBuilding building = editor.getMap().createBuildingFromNodes(nodes); + return new CreateBuildingEdit(building); + } + + private class CreateBuildingEdit extends AbstractUndoableEdit { + private GMLBuilding building; + + public CreateBuildingEdit(GMLBuilding building) { + this.building = building; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().removeBuilding(building); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().addBuilding(building); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateEdgeTool.java b/modules/maps/src/maps/gml/editor/CreateEdgeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..97b2418033c8818d6a1ab46c6dee86bcf5cc9dab --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateEdgeTool.java @@ -0,0 +1,202 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.view.LineOverlay; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; + +import rescuecore2.misc.geometry.Point2D; + +/** + A tool for creating edges. +*/ +public class CreateEdgeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + private static final int HIGHLIGHT_SIZE = 6; + + private Listener listener; + private NodeDecorator nodeHighlight; + private LineOverlay overlay; + + private GMLNode hover; + private GMLNode start; + private GMLNode end; + // private GMLEdge edge; + + /** + Construct a CreateEdgeTool. + @param editor The editor instance. + */ + public CreateEdgeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE); + overlay = new LineOverlay(HIGHLIGHT_COLOUR, true); + } + + @Override + public String getName() { + return "Create edge"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + editor.getViewer().addOverlay(overlay); + hover = null; + start = null; + end = null; + // edge = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().removeOverlay(overlay); + editor.getViewer().repaint(); + } + + private void setHover(GMLNode node) { + if (hover == node) { + return; + } + if (hover != null) { + editor.getViewer().clearNodeDecorator(hover); + } + hover = node; + if (hover != null) { + editor.getViewer().setNodeDecorator(nodeHighlight, hover); + } + editor.getViewer().repaint(); + } + + private void setStart(GMLNode node) { + if (start == node) { + return; + } + if (start != null) { + editor.getViewer().clearNodeDecorator(start); + } + start = node; + if (start != null) { + editor.getViewer().setNodeDecorator(nodeHighlight, start); + } + editor.getViewer().repaint(); + } + + private void setEnd(GMLNode node) { + if (start == node || end == node) { + return; + } + if (end != null) { + editor.getViewer().clearNodeDecorator(end); + } + end = node; + if (end != null) { + editor.getViewer().setNodeDecorator(nodeHighlight, end); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + overlay.setStart(new Point2D(node.getX(), node.getY())); + setStart(node); + setHover(null); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + if (start != null && end != null) { + GMLEdge edge = editor.getMap().createEdge(start, end); + editor.setChanged(); + editor.addEdit(new CreateEdgeEdit(edge)); + editor.getViewer().clearAllNodeDecorators(); + overlay.setStart(null); + overlay.setEnd(null); + editor.getViewer().repaint(); + start = null; + end = null; + hover = null; + } + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (start != null) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + overlay.setEnd(new Point2D(node.getX(), node.getY())); + setEnd(node); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + setHover(node); + } + + @Override + public void mouseClicked(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class CreateEdgeEdit extends AbstractUndoableEdit { + private GMLEdge edge; + + public CreateEdgeEdit(GMLEdge edge) { + this.edge = edge; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().removeEdge(edge); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().addEdge(edge); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateNodeTool.java b/modules/maps/src/maps/gml/editor/CreateNodeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..a6ff18ead1b7cc3e928fc8b43b29d0857236b32f --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateNodeTool.java @@ -0,0 +1,105 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLNode; +import maps.gml.GMLCoordinates; + +/** + A tool for creating nodes. +*/ +public class CreateNodeTool extends AbstractTool { + private Listener listener; + + /** + Construct a CreateNodeTool. + @param editor The editor instance. + */ + public CreateNodeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + } + + @Override + public String getName() { + return "Create node"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + GMLNode node = editor.getMap().createNode(c); + editor.setChanged(); + editor.addEdit(new CreateNodeEdit(node)); + editor.getViewer().repaint(); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + } + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class CreateNodeEdit extends AbstractUndoableEdit { + private GMLNode node; + + public CreateNodeEdit(GMLNode node) { + this.node = node; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().removeNode(node); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().addNode(node); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateRoadTool.java b/modules/maps/src/maps/gml/editor/CreateRoadTool.java new file mode 100644 index 0000000000000000000000000000000000000000..df52814bf261b61d01a0bf5fa06f48ec517e34fd --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateRoadTool.java @@ -0,0 +1,55 @@ +package maps.gml.editor; + +import javax.swing.undo.UndoableEdit; +import javax.swing.undo.AbstractUndoableEdit; + +import java.util.List; + +import maps.gml.GMLNode; +import maps.gml.GMLRoad; + +/** + A tool for creating roads. +*/ +public class CreateRoadTool extends CreateShapeTool { + /** + Construct a CreateRoadTool. + @param editor The editor instance. + */ + public CreateRoadTool(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Create road"; + } + + @Override + protected UndoableEdit finished(List<GMLNode> nodes) { + GMLRoad road = editor.getMap().createRoadFromNodes(nodes); + return new CreateRoadEdit(road); + } + + private class CreateRoadEdit extends AbstractUndoableEdit { + private GMLRoad road; + + public CreateRoadEdit(GMLRoad road) { + this.road = road; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().removeRoad(road); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().addRoad(road); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateShapeTool.java b/modules/maps/src/maps/gml/editor/CreateShapeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..51ee18126aadb230457ca732e2f8b0ce92ac9dcc --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateShapeTool.java @@ -0,0 +1,205 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.UndoableEdit; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; + +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; + +/** + A tool for creating shapes. +*/ +public abstract class CreateShapeTool extends AbstractTool { + private static final Color HOVER_COLOUR = Color.BLUE; + private static final Color SELECTED_COLOUR = Color.GREEN; + private static final Color POSSIBLE_COLOUR = Color.WHITE; + + private Listener listener; + private EdgeDecorator hoverHighlight; + private EdgeDecorator selectedHighlight; + private EdgeDecorator possibleHighlight; + + private List<GMLEdge> edges; + private List<GMLNode> nodes; + private Set<GMLEdge> possible; + private GMLNode startNode; + private GMLNode currentNode; + private GMLEdge hover; + + /** + Construct a CreateShapeTool. + @param editor The editor instance. + */ + protected CreateShapeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + hoverHighlight = new LineEdgeDecorator(HOVER_COLOUR); + selectedHighlight = new LineEdgeDecorator(SELECTED_COLOUR); + possibleHighlight = new LineEdgeDecorator(POSSIBLE_COLOUR); + edges = new ArrayList<GMLEdge>(); + nodes = new ArrayList<GMLNode>(); + possible = new HashSet<GMLEdge>(); + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + clearData(); + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + clearData(); + } + + /** + Perform whatever shape creation tasks are needed once the shape has been closed. + @param shapeNodes The nodes of the shape. + @return An UndoableEdit for the change. + */ + protected abstract UndoableEdit finished(List<GMLNode> shapeNodes); + + private void addEdge(GMLEdge edge) { + editor.getViewer().clearEdgeDecorator(possible); + edges.add(edge); + possible.clear(); + editor.getViewer().setEdgeDecorator(selectedHighlight, edge); + editor.getViewer().repaint(); + if (edges.size() == 1) { + startNode = edge.getStart(); + currentNode = edge.getEnd(); + possible.addAll(editor.getMap().getAttachedEdges(startNode)); + possible.addAll(editor.getMap().getAttachedEdges(currentNode)); + } + else if (edges.size() == 2) { + // Find the shared node + GMLEdge first = edges.get(0); + GMLEdge second = edges.get(1); + GMLNode shared; + if (first.getStart().equals(second.getStart()) || first.getStart().equals(second.getEnd())) { + startNode = first.getEnd(); + shared = first.getStart(); + } + else { + startNode = first.getStart(); + shared = first.getEnd(); + } + currentNode = shared.equals(second.getStart()) ? second.getEnd() : second.getStart(); + nodes.add(startNode); + nodes.add(shared); + nodes.add(currentNode); + possible.addAll(editor.getMap().getAttachedEdges(currentNode)); + } + else if (edges.size() > 2) { + // Update end node + currentNode = currentNode.equals(edge.getStart()) ? edge.getEnd() : edge.getStart(); + if (currentNode.equals(startNode)) { + // We're done + editor.addEdit(finished(nodes)); + editor.setChanged(); + clearData(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + } + else { + nodes.add(currentNode); + possible.addAll(editor.getMap().getAttachedEdges(currentNode)); + } + } + possible.removeAll(edges); + editor.getViewer().setEdgeDecorator(possibleHighlight, possible); + if (possible.size() == 1) { + addEdge(possible.iterator().next()); + } + editor.getViewer().repaint(); + } + + private void clearData() { + nodes.clear(); + edges.clear(); + possible.clear(); + startNode = null; + currentNode = null; + hover = null; + } + + private void hover(GMLEdge edge) { + if (hover == edge) { + return; + } + if (hover != null) { + editor.getViewer().clearEdgeDecorator(hover); + if (possible.contains(hover)) { + editor.getViewer().setEdgeDecorator(possibleHighlight, hover); + } + } + hover = edge; + if (hover != null) { + editor.getViewer().setEdgeDecorator(hoverHighlight, hover); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + if (edges.isEmpty()) { + hover(editor.getMap().findNearestEdge(c.getX(), c.getY())); + } + else { + hover(editor.getMap().findNearestEdge(c.getX(), c.getY(), possible)); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (hover != null && e.getButton() == MouseEvent.BUTTON1) { + GMLEdge edge = hover; + hover(null); + addEdge(edge); + } + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/CreateSpaceTool.java b/modules/maps/src/maps/gml/editor/CreateSpaceTool.java new file mode 100644 index 0000000000000000000000000000000000000000..e92c7092273a2e48ee6cb9e86dd7cc932157744c --- /dev/null +++ b/modules/maps/src/maps/gml/editor/CreateSpaceTool.java @@ -0,0 +1,55 @@ +package maps.gml.editor; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.UndoableEdit; + +import java.util.List; + +import maps.gml.GMLNode; +import maps.gml.GMLSpace; + +/** + A tool for creating spaces. +*/ +public class CreateSpaceTool extends CreateShapeTool { + /** + Construct a CreateSpaceTool. + @param editor The editor instance. + */ + public CreateSpaceTool(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Create space"; + } + + @Override + protected UndoableEdit finished(List<GMLNode> nodes) { + GMLSpace space = editor.getMap().createSpaceFromNodes(nodes); + return new CreateSpaceEdit(space); + } + + private class CreateSpaceEdit extends AbstractUndoableEdit { + private GMLSpace space; + + public CreateSpaceEdit(GMLSpace space) { + this.space = space; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().removeSpace(space); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().addSpace(space); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/DeleteEdgeTool.java b/modules/maps/src/maps/gml/editor/DeleteEdgeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..9370cf4d4cf62794cc051acf7cd2ccdeff4cddd0 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/DeleteEdgeTool.java @@ -0,0 +1,139 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLObject; + +import java.util.Collection; + +/** + A tool for deleting edges. +*/ +public class DeleteEdgeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + + private Listener listener; + private EdgeDecorator edgeHighlight; + + private GMLEdge edge; + + /** + Construct a DeleteEdgeTool. + @param editor The editor instance. + */ + public DeleteEdgeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + } + + @Override + public String getName() { + return "Delete edge"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + edge = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + } + + private void highlightEdge(GMLEdge newEdge) { + if (edge == newEdge) { + return; + } + if (edge != null) { + editor.getViewer().clearEdgeDecorator(edge); + } + edge = newEdge; + if (edge != null) { + editor.getViewer().setEdgeDecorator(edgeHighlight, edge); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Collection<GMLObject> deleted = editor.getMap().removeEdge(edge); + editor.getViewer().repaint(); + editor.setChanged(); + editor.addEdit(new DeleteEdgeEdit(edge, deleted)); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + highlightEdge(editor.getMap().findNearestEdge(c.getX(), c.getY())); + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class DeleteEdgeEdit extends AbstractUndoableEdit { + private GMLEdge edge; + private Collection<GMLObject> deleted; + + public DeleteEdgeEdit(GMLEdge edge, Collection<GMLObject> deleted) { + this.edge = edge; + this.deleted = deleted; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().addEdge(edge); + editor.getMap().add(deleted); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().removeEdge(edge); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/DeleteNodeTool.java b/modules/maps/src/maps/gml/editor/DeleteNodeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..219634285db580e28359dc4e01de997d6cfe6e10 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/DeleteNodeTool.java @@ -0,0 +1,250 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.view.RectangleOverlay; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLObject; + +import javax.swing.undo.AbstractUndoableEdit; + +/** + A tool for deleting nodes. +*/ +public class DeleteNodeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + private static final int HIGHLIGHT_SIZE = 6; + + private static final Color OVERLAY_COLOUR = new Color(0, 0, 128, 128); + + private Listener listener; + private NodeDecorator nodeHighlight; + private EdgeDecorator edgeHighlight; + private GMLNode selected; + private Collection<GMLEdge> attachedEdges; + + private GMLCoordinates pressPoint; + private GMLCoordinates dragPoint; + + private RectangleOverlay overlay; + + /** + Construct a DeleteNodeTool. + @param editor The editor instance. + */ + public DeleteNodeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE); + edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + selected = null; + attachedEdges = new HashSet<GMLEdge>(); + overlay = new RectangleOverlay(OVERLAY_COLOUR, true); + } + + @Override + public String getName() { + return "Delete node"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + selected = null; + attachedEdges.clear(); + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().removeOverlay(overlay); + editor.getViewer().repaint(); + } + + private void highlightNode(GMLNode node) { + if (selected == node) { + return; + } + if (selected != null) { + editor.getViewer().clearNodeDecorator(selected); + editor.getViewer().clearEdgeDecorator(attachedEdges); + } + selected = node; + attachedEdges.clear(); + if (selected != null) { + attachedEdges.addAll(editor.getMap().getAttachedEdges(selected)); + editor.getViewer().setNodeDecorator(nodeHighlight, selected); + editor.getViewer().setEdgeDecorator(edgeHighlight, attachedEdges); + } + editor.getViewer().repaint(); + } + + private void removeNodes() { + double xMin = Math.min(pressPoint.getX(), dragPoint.getX()); + double xMax = Math.max(pressPoint.getX(), dragPoint.getX()); + double yMin = Math.min(pressPoint.getY(), dragPoint.getY()); + double yMax = Math.max(pressPoint.getY(), dragPoint.getY()); + Collection<GMLNode> nodes = editor.getMap().getNodesInRegion(xMin, yMin, xMax, yMax); + Map<GMLNode, Collection<GMLObject>> deleted = new HashMap<GMLNode, Collection<GMLObject>>(); + for (GMLNode next : nodes) { + deleted.put(next, editor.getMap().removeNode(next)); + } + editor.getViewer().repaint(); + editor.setChanged(); + editor.addEdit(new DeleteNodesEdit(nodes, deleted)); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + highlightNode(node); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (selected == null) { + return; + } + if (e.getButton() == MouseEvent.BUTTON1) { + Collection<GMLObject> deleted = editor.getMap().removeNode(selected); + editor.getViewer().repaint(); + editor.setChanged(); + editor.addEdit(new DeleteNodeEdit(selected, deleted)); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + pressPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + overlay.setLeft(pressPoint.getX()); + overlay.setBottom(pressPoint.getY()); + editor.getViewer().addOverlay(overlay); + editor.getViewer().repaint(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + overlay.setLeft(Double.NaN); + overlay.setRight(Double.NaN); + overlay.setBottom(Double.NaN); + overlay.setTop(Double.NaN); + editor.getViewer().removeOverlay(overlay); + editor.getViewer().repaint(); + removeNodes(); + pressPoint = null; + dragPoint = null; + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (pressPoint != null) { + Point p = fixEventPoint(e.getPoint()); + dragPoint = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + overlay.setRight(dragPoint.getX()); + overlay.setTop(dragPoint.getY()); + editor.getViewer().repaint(); + highlightNode(null); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class DeleteNodeEdit extends AbstractUndoableEdit { + private GMLNode node; + private Collection<GMLObject> deletedObjects; + + public DeleteNodeEdit(GMLNode node, Collection<GMLObject> deletedObjects) { + this.node = node; + this.deletedObjects = deletedObjects; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().addNode(node); + editor.getMap().add(deletedObjects); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().removeNode(node); + editor.getMap().remove(deletedObjects); + editor.getViewer().repaint(); + } + } + + private class DeleteNodesEdit extends AbstractUndoableEdit { + private Collection<GMLNode> nodes; + private Map<GMLNode, Collection<GMLObject>> deletedObjects; + + public DeleteNodesEdit(Collection<GMLNode> nodes, Map<GMLNode, Collection<GMLObject>> deletedObjects) { + this.nodes = nodes; + this.deletedObjects = deletedObjects; + } + + @Override + public void undo() { + super.undo(); + for (GMLNode next : nodes) { + Collection<GMLObject> deleted = deletedObjects.get(next); + editor.getMap().addNode(next); + editor.getMap().add(deleted); + } + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + for (GMLNode next : nodes) { + Collection<GMLObject> deleted = deletedObjects.get(next); + editor.getMap().removeNode(next); + editor.getMap().remove(deleted); + } + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/DeleteShapeTool.java b/modules/maps/src/maps/gml/editor/DeleteShapeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..7d2031031eb9e4e540dc0696b711032d6057625d --- /dev/null +++ b/modules/maps/src/maps/gml/editor/DeleteShapeTool.java @@ -0,0 +1,153 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.view.FilledShapeDecorator; +import maps.gml.GMLRoad; +import maps.gml.GMLBuilding; +import maps.gml.GMLSpace; +import maps.gml.GMLShape; +import maps.gml.GMLCoordinates; + +/** + A tool for deleting shapes. +*/ +public class DeleteShapeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + + private Listener listener; + private FilledShapeDecorator highlight; + + private GMLShape shape; + + /** + Construct a DeleteShapeTool. + @param editor The editor instance. + */ + public DeleteShapeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + highlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR); + } + + @Override + public String getName() { + return "Delete shape"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + shape = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllBuildingDecorators(); + editor.getViewer().clearAllRoadDecorators(); + editor.getViewer().clearAllSpaceDecorators(); + editor.getViewer().repaint(); + } + + private void highlightShape(GMLShape newShape) { + if (shape == newShape) { + return; + } + if (shape != null) { + if (shape instanceof GMLBuilding) { + editor.getViewer().clearBuildingDecorator((GMLBuilding)shape); + } + if (shape instanceof GMLRoad) { + editor.getViewer().clearRoadDecorator((GMLRoad)shape); + } + if (shape instanceof GMLSpace) { + editor.getViewer().clearSpaceDecorator((GMLSpace)shape); + } + } + shape = newShape; + if (shape != null) { + if (shape instanceof GMLBuilding) { + editor.getViewer().setBuildingDecorator(highlight, (GMLBuilding)shape); + } + if (shape instanceof GMLRoad) { + editor.getViewer().setRoadDecorator(highlight, (GMLRoad)shape); + } + if (shape instanceof GMLSpace) { + editor.getViewer().setSpaceDecorator(highlight, (GMLSpace)shape); + } + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (shape != null && e.getButton() == MouseEvent.BUTTON1) { + editor.getMap().remove(shape); + editor.getViewer().repaint(); + editor.setChanged(); + editor.addEdit(new DeleteShapeEdit(shape)); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + highlightShape(editor.getMap().findShapeUnder(c.getX(), c.getY())); + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class DeleteShapeEdit extends AbstractUndoableEdit { + private GMLShape shape; + + public DeleteShapeEdit(GMLShape shape) { + this.shape = shape; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().add(shape); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().remove(shape); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/FixAttachedObjectsFunction.java b/modules/maps/src/maps/gml/editor/FixAttachedObjectsFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..86950964b8b5d53e3aa1610957cbb5e0fcde649d --- /dev/null +++ b/modules/maps/src/maps/gml/editor/FixAttachedObjectsFunction.java @@ -0,0 +1,59 @@ +package maps.gml.editor; + +import java.util.Set; +import java.util.HashSet; + +import maps.gml.GMLShape; +import maps.gml.GMLEdge; + +/** + A function for fixing the lists of attached shapes. +*/ +public class FixAttachedObjectsFunction extends ProgressFunction { + /** + Construct a FixAttachedObjectsFunction. + @param editor The editor instance. + */ + public FixAttachedObjectsFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Fix attached objects"; + } + + @Override + protected String getTitle() { + return "Fixing attached objects"; + } + + @Override + protected void executeImpl() { + // Remove and re-add all edges and shapes. + final Set<GMLShape> shapes = new HashSet<GMLShape>(); + final Set<GMLEdge> edges = new HashSet<GMLEdge>(); + synchronized (editor.getMap()) { + shapes.addAll(editor.getMap().getAllShapes()); + edges.addAll(editor.getMap().getEdges()); + } + setProgressLimit(shapes.size() + edges.size()); + synchronized (editor.getMap()) { + editor.getMap().removeAllEdges(); + } + for (GMLEdge next : edges) { + synchronized (editor.getMap()) { + editor.getMap().add(next); + } + bumpProgress(); + } + for (GMLShape next : shapes) { + synchronized (editor.getMap()) { + editor.getMap().add(next); + } + bumpProgress(); + } + editor.setChanged(); + editor.getViewer().repaint(); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/FixDegenerateShapesFunction.java b/modules/maps/src/maps/gml/editor/FixDegenerateShapesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..f499962c312a118354d41a464b6f9eb93b20c414 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/FixDegenerateShapesFunction.java @@ -0,0 +1,246 @@ +package maps.gml.editor; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +import maps.gml.GMLShape; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; + +import rescuecore2.log.Logger; + +/** + A function for fixing degenerate shapes. +*/ +public class FixDegenerateShapesFunction extends ProgressFunction { + /** + Construct a FixDegenerateShapesFunction. + @param editor The editor instance. + */ + public FixDegenerateShapesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Fix degenerate shapes"; + } + + @Override + protected String getTitle() { + return "Fixing degenerate shapes"; + } + + @Override + protected void executeImpl() { + // Go through all shapes and remove any that have two or fewer edges. + Set<GMLShape> shapes = new HashSet<GMLShape>(); + Set<GMLEdge> edges = new HashSet<GMLEdge>(); + synchronized (editor.getMap()) { + shapes.addAll(editor.getMap().getAllShapes()); + edges.addAll(editor.getMap().getEdges()); + } + setProgressLimit(shapes.size() + edges.size()); + int shapeCount = 0; + int spurCount = 0; + int edgeCount = 0; + int outlineCount = 0; + for (GMLShape next : shapes) { + synchronized (editor.getMap()) { + removeDuplicateEdges(next); + if (checkForDegenerateShape(next)) { + ++shapeCount; + } + else { + if (checkForSpurs(next)) { + ++spurCount; + } + if (!checkOutline(next)) { + ++outlineCount; + editor.getMap().remove(next); + } + } + } + bumpProgress(); + } + for (GMLEdge next : edges) { + synchronized (editor.getMap()) { + if (checkForDegenerateEdge(next)) { + ++edgeCount; + } + } + bumpProgress(); + } + Logger.debug("Removed " + shapeCount + " degenerate shapes and " + edgeCount + " edges"); + Logger.debug("Removed " + outlineCount + " shapes with broken outlines"); + Logger.debug("Fixed " + spurCount + " spurs"); + editor.setChanged(); + editor.getViewer().repaint(); + } + + private void removeDuplicateEdges(GMLShape shape) { + List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(shape.getEdges()); + Set<GMLEdge> seen = new HashSet<GMLEdge>(); + /* + Logger.debug("Checking for duplicate edges in " + shape); + Logger.debug("Edges:"); + for (GMLDirectedEdge next : result) { + Logger.debug(" " + next); + } + */ + for (Iterator<GMLDirectedEdge> it = result.iterator(); it.hasNext();) { + GMLDirectedEdge dEdge = it.next(); + GMLEdge edge = dEdge.getEdge(); + if (seen.contains(edge)) { + // Logger.debug("Duplicate found: " + dEdge); + it.remove(); + } + seen.add(edge); + } + /* + Logger.debug("Resulting edges:"); + for (GMLDirectedEdge next : result) { + Logger.debug(" " + next); + } + */ + editor.getMap().remove(shape); + shape.reorderEdges(result); + // Update attached edges by removing and re-adding + editor.getMap().add(shape); + } + + private boolean checkForSpurs(GMLShape shape) { + boolean spur = false; + List<GMLDirectedEdge> good = new ArrayList<GMLDirectedEdge>(shape.getEdges().size()); + /* + Logger.debug("Checking for spurs in " + shape); + Logger.debug("Edges:"); + for (GMLDirectedEdge next : shape.getEdges()) { + Logger.debug(" " + next); + } + */ + for (GMLDirectedEdge dEdge : shape.getEdges()) { + // This edge is good if both its nodes are part of other edges. + GMLNode start = dEdge.getStartNode(); + GMLNode end = dEdge.getEndNode(); + if (isFound(start, shape, dEdge) && isFound(end, shape, dEdge)) { + good.add(dEdge); + } + else { + // Logger.debug("Found spur edge: " + dEdge); + spur = true; + } + } + if (spur) { + editor.getMap().remove(shape); + shape.reorderEdges(good); + // Update attached edges by removing and re-adding + editor.getMap().add(shape); + } + return spur; + } + + private boolean checkOutline(GMLShape shape) { + List<GMLDirectedEdge> edges = shape.getEdges(); + List<GMLDirectedEdge> result = new ArrayList<GMLDirectedEdge>(edges.size()); + Set<GMLEdge> seen = new HashSet<GMLEdge>(); + GMLDirectedEdge dEdge = edges.get(0); + GMLNode start = dEdge.getStartNode(); + GMLNode current = dEdge.getEndNode(); + result.add(dEdge); + seen.add(dEdge.getEdge()); + /* + Logger.debug("Checking outline of " + shape); + Logger.debug("Edges:"); + for (GMLDirectedEdge next : edges) { + Logger.debug(" " + next); + } + Logger.debug("First edge: " + dEdge); + Logger.debug("Start node: " + start); + */ + while (current != start) { + // Logger.debug("Current node: " + current); + GMLDirectedEdge next = findNextEdge(current, edges, seen); + // Logger.debug("Next edge: " + next); + if (next == null) { + // Logger.debug("No next edge found!"); + return false; + } + current = next.getEndNode(); + seen.add(next.getEdge()); + result.add(next); + } + /* + Logger.debug("Finished checking outline"); + Logger.debug("New edges:"); + for (GMLDirectedEdge next : result) { + Logger.debug(" " + next); + } + */ + editor.getMap().remove(shape); + shape.reorderEdges(result); + // Update attached edges by removing and re-adding + editor.getMap().add(shape); + return true; + } + + private boolean checkForDegenerateShape(GMLShape shape) { + // CHECKSTYLE:OFF:MagicNumber + if (shape.getEdges().size() < 3) { + // CHECKSTYLE:ON:MagicNumber + editor.getMap().remove(shape); + return true; + } + return false; + } + + private boolean checkForDegenerateEdge(GMLEdge edge) { + if (edge.getStart().equals(edge.getEnd())) { + // Remove this edge from all attached shapes + Collection<GMLShape> attached = new HashSet<GMLShape>(editor.getMap().getAttachedShapes(edge)); + for (GMLShape shape : attached) { + editor.getMap().remove(shape); + shape.removeEdge(edge); + editor.getMap().add(shape); + } + editor.getMap().remove(edge); + return true; + } + return false; + } + + private boolean isFound(GMLNode node, GMLShape shape, GMLDirectedEdge ignore) { + for (GMLDirectedEdge edge : shape.getEdges()) { + if (edge == ignore) { + continue; + } + if (node.equals(edge.getStartNode()) || node.equals(edge.getEndNode())) { + return true; + } + } + return false; + } + + private GMLDirectedEdge findNextEdge(GMLNode start, Collection<GMLDirectedEdge> possible, Set<GMLEdge> seen) { + for (GMLDirectedEdge next : possible) { + if (next.getStartNode() == start && !seen.contains(next.getEdge())) { + return next; + } + } + // No edges found. Try reversing them. + for (GMLDirectedEdge next : possible) { + if (next.getEndNode() == start && !seen.contains(next.getEdge())) { + // Logger.debug("Reversed edge " + next); + next.reverse(); + return next; + } + } + // Nothing found. + return null; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/FixDuplicateEdgesFunction.java b/modules/maps/src/maps/gml/editor/FixDuplicateEdgesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..1408ef8087bdeb3901142966e6c54ebdac178d00 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/FixDuplicateEdgesFunction.java @@ -0,0 +1,64 @@ +package maps.gml.editor; + +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; + +import maps.gml.GMLEdge; + +import rescuecore2.log.Logger; + +/** + A function for fixing duplicate edges. +*/ +public class FixDuplicateEdgesFunction extends ProgressFunction { + /** + Construct a FixDuplicateEdgesFunction. + @param editor The editor instance. + */ + public FixDuplicateEdgesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Fix duplicate edges"; + } + + @Override + protected String getTitle() { + return "Fixing duplicate edges"; + } + + @Override + protected void executeImpl() { + // Go through all edges and replace any duplicates + final Set<GMLEdge> remaining = new HashSet<GMLEdge>(editor.getMap().getEdges()); + setProgressLimit(remaining.size()); + int count = 0; + while (!remaining.isEmpty()) { + GMLEdge next = remaining.iterator().next(); + remaining.remove(next); + // Look at other edges for a duplicate + Iterator<GMLEdge> it = remaining.iterator(); + while (it.hasNext()) { + GMLEdge test = it.next(); + if ((test.getStart() == next.getStart() || test.getStart() == next.getEnd()) + && (test.getEnd() == next.getStart() || test.getEnd() == next.getEnd())) { + // Duplicate found + editor.getMap().replaceEdge(test, next); + editor.getMap().removeEdge(test); + it.remove(); + ++count; + bumpProgress(); + } + } + bumpProgress(); + } + if (count != 0) { + editor.setChanged(); + editor.getViewer().repaint(); + } + Logger.debug("Removed " + count + " duplicate edges"); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/FixLatLongTool.java b/modules/maps/src/maps/gml/editor/FixLatLongTool.java new file mode 100644 index 0000000000000000000000000000000000000000..c358b99319bfefb1ec284a4d240ce8419ca6173d --- /dev/null +++ b/modules/maps/src/maps/gml/editor/FixLatLongTool.java @@ -0,0 +1,36 @@ +package maps.gml.editor; + +import maps.ScaleConversion; +import maps.MapTools; + +/** + A tool for fixing latitude/longitude coordinates. +*/ +public class FixLatLongTool extends AbstractTool { + /** + Construct a FixLatLongTool. + @param editor The editor instance. + */ + public FixLatLongTool(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Fix lat/long"; + } + + @Override + public void activate() { + double minX = editor.getMap().getMinX(); + double minY = editor.getMap().getMinY(); + double factor = 1.0 / MapTools.sizeOf1Metre(minX, minY); + ScaleConversion c = new ScaleConversion(minX, minY, factor, factor); + editor.getMap().convertCoordinates(c); + editor.setChanged(); + } + + @Override + public void deactivate() { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/FixNearbyNodesFunction.java b/modules/maps/src/maps/gml/editor/FixNearbyNodesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..f50b7d23f7556b5cd9e3fe7df39109f1cb7656e7 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/FixNearbyNodesFunction.java @@ -0,0 +1,80 @@ +package maps.gml.editor; + +import javax.swing.JOptionPane; + +import java.util.Set; +import java.util.HashSet; + +import maps.gml.GMLNode; + +import rescuecore2.log.Logger; + +/** + A function for fixing nearby nodes. +*/ +public class FixNearbyNodesFunction extends ProgressFunction { + private static final double DEFAULT_TOLERANCE = 0.001; + + private double tolerance; + + /** + Construct a FixNearbyNodesFunction. + @param editor The editor instance. + */ + public FixNearbyNodesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Fix nearby nodes"; + } + + @Override + protected String getTitle() { + return "Fixing nearby nodes"; + } + + @Override + public void execute() { + String s = JOptionPane.showInputDialog(editor.getViewer(), "Enter the desired tolerance (in m)", DEFAULT_TOLERANCE); + if (s == null) { + return; + } + tolerance = Double.parseDouble(s); + super.execute(); + } + + @Override + protected void executeImpl() { + // Go through all nodes and replace any nearby ones. + final Set<GMLNode> remaining = new HashSet<GMLNode>(editor.getMap().getNodes()); + setProgressLimit(remaining.size()); + int count = 0; + while (!remaining.isEmpty()) { + GMLNode next = remaining.iterator().next(); + remaining.remove(next); + double x = next.getX(); + double y = next.getY(); + // Logger.debug("Next node: " + next); + // Logger.debug("Finding nodes near " + x + ", " + y); + bumpProgress(); + for (GMLNode replaced : editor.getMap().getNodesInRegion(x - tolerance, y - tolerance, x + tolerance, y + tolerance)) { + if (replaced == next) { + continue; + } + // Logger.debug("Found " + replaced); + editor.getMap().replaceNode(replaced, next); + remaining.remove(replaced); + editor.getMap().removeNode(replaced); + ++count; + bumpProgress(); + } + } + if (count != 0) { + editor.setChanged(); + editor.getViewer().repaint(); + } + Logger.debug("Removed " + count + " nodes"); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/Function.java b/modules/maps/src/maps/gml/editor/Function.java new file mode 100644 index 0000000000000000000000000000000000000000..a151bda40b1484951858a2467eef73d9f2e93c33 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/Function.java @@ -0,0 +1,17 @@ +package maps.gml.editor; + +/** + Interface for an editing function. +*/ +public interface Function { + /** + Get the name of this function. + @return The name of the function. + */ + String getName(); + + /** + Execute this function. + */ + void execute(); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/GMLEditor.java b/modules/maps/src/maps/gml/editor/GMLEditor.java new file mode 100644 index 0000000000000000000000000000000000000000..3a4c3f2483f3253dc9de4b07c3d48856f3f0fc85 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/GMLEditor.java @@ -0,0 +1,638 @@ +package maps.gml.editor; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JSplitPane; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.UndoManager; +import javax.swing.undo.UndoableEdit; + +import maps.MapException; +import maps.MapReader; +import maps.MapWriter; +import maps.gml.GMLCoordinates; +import maps.gml.GMLMap; +import maps.gml.GMLObject; +import maps.gml.formats.RobocupFormat; +import maps.gml.view.GMLMapViewer; +import maps.gml.view.GMLObjectInspector; + +import rescuecore2.log.Logger; + +/** + * A component for editing GML maps. + */ +public class GMLEditor extends JPanel { + private static final int VIEWER_PREFERRED_SIZE = 500; + private static final int INSPECTOR_PREFERRED_WIDTH = 300; + private static final int INSPECTOR_PREFERRED_HEIGHT = 500; + + private static final double SNAP_MIN_RESOLUTION = 0.001; + private static final double SNAP_MAX_RESOLUTION = 1000; + + private static final NumberFormat FORMAT = new DecimalFormat("#0.000"); + + private GMLMap map; + private GMLMapViewer viewer; + private GMLObjectInspector inspector; + private JLabel x; + private JLabel y; + private boolean changed; + private ViewerMouseListener viewerMouseListener; + private Tool currentTool; + + private UndoManager undoManager; + private Action undoAction; + private Action redoAction; + + private File saveFile; + private File baseDir; + + private Snap snap; + + /** + * Construct a new GMLEditor. + * + * @param menuBar The menu bar to add menus to. + */ + public GMLEditor(JMenuBar menuBar) { + super(new BorderLayout()); + map = new GMLMap(); + viewer = new GMLMapViewer(map); + inspector = new GMLObjectInspector(map); + undoManager = new UndoManager(); + viewer.setPreferredSize(new Dimension(VIEWER_PREFERRED_SIZE, VIEWER_PREFERRED_SIZE)); + inspector.setPreferredSize(new Dimension(INSPECTOR_PREFERRED_WIDTH, INSPECTOR_PREFERRED_HEIGHT)); + viewer.setBackground(Color.GRAY); + viewer.getPanZoomListener().setPanOnRightMouse(); + snap = new Snap(); + changed = false; + x = new JLabel("X: "); + y = new JLabel("Y: "); + JToolBar fileToolbar = new JToolBar("File"); + JToolBar viewToolbar = new JToolBar("View"); + JToolBar editToolbar = new JToolBar("Edit"); + JToolBar toolsToolbar = new JToolBar("Tools"); + JToolBar functionsToolbar = new JToolBar("Functions"); + JMenu fileMenu = new JMenu("File", false); + JMenu viewMenu = new JMenu("View", false); + JMenu editMenu = new JMenu("Edit", false); + JMenu toolsMenu = new JMenu("Tools", false); + JMenu functionsMenu = new JMenu("Functions", false); + + createFileActions(fileMenu, fileToolbar); + createViewActions(viewMenu, viewToolbar); + createEditActions(editMenu, editToolbar); + createToolActions(toolsMenu, toolsToolbar); + createFunctionActions(functionsMenu, functionsToolbar); + + JPanel main = new JPanel(new BorderLayout()); + JPanel labels = new JPanel(new GridLayout(1, 2)); + labels.add(x); + labels.add(y); + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewer, inspector); + main.add(split, BorderLayout.CENTER); + main.add(labels, BorderLayout.SOUTH); + add(main, BorderLayout.CENTER); + JPanel toolbars = new JPanel(new GridLayout(0, 1)); + toolbars.add(fileToolbar); + toolbars.add(viewToolbar); + toolbars.add(editToolbar); + toolbars.add(toolsToolbar); + toolbars.add(functionsToolbar); + add(toolbars, BorderLayout.NORTH); + menuBar.add(fileMenu); + menuBar.add(viewMenu); + menuBar.add(editMenu); + menuBar.add(toolsMenu); + menuBar.add(functionsMenu); + + viewerMouseListener = new ViewerMouseListener(); + viewer.addMouseListener(viewerMouseListener); + viewer.addMouseMotionListener(viewerMouseListener); + + baseDir = new File(System.getProperty("user.dir")); + } + + /** + * Entry point. + * + * @param args Command line arguments. + */ + public static void main(String[] args) { + final JFrame frame = new JFrame("GMLEditor"); + JMenuBar menuBar = new JMenuBar(); + final GMLEditor editor = new GMLEditor(menuBar); + if (args.length > 0 && args[0].length() > 0) { + try { + editor.load(args[0]); + } catch (CancelledByUserException e) { + return; + } catch (MapException e) { + e.printStackTrace(); + } + } + + frame.setJMenuBar(menuBar); + frame.setContentPane(editor); + frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + frame.pack(); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + try { + editor.close(); + frame.setVisible(false); + frame.dispose(); + System.exit(0); + } catch (CancelledByUserException ex) { + frame.setVisible(true); + } + } + }); + frame.setVisible(true); + } + + /** + * Load a map by showing a file chooser dialog. + * + * @throws CancelledByUserException If the user cancels the change due to + * unsaved changes. + * @throws MapException If there is a problem reading the map. + */ + public void load() throws CancelledByUserException, MapException { + JFileChooser chooser = new JFileChooser(baseDir); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + load(chooser.getSelectedFile()); + } + } + + /** + * Load a map from a file. + * + * @param filename The name of the file to read. + * @throws CancelledByUserException If the user cancels the change due to + * unsaved changes. + * @throws MapException If there is a problem reading the map. + */ + public void load(String filename) throws CancelledByUserException, MapException { + load(new File(filename)); + } + + /** + * Load a map from a file. + * + * @param file The file to read. + * @throws CancelledByUserException If the user cancels the change due to + * unsaved changes. + * @throws MapException If there is a problem reading the map. + */ + public void load(File file) throws CancelledByUserException, MapException { + setMap((GMLMap) MapReader.readMap(file)); + saveFile = file; + baseDir = saveFile.getParentFile(); + } + + /** + * Set the map. + * + * @param newMap The new map. + * @throws CancelledByUserException If the user cancels the change due to + * unsaved changes. + */ + public void setMap(GMLMap newMap) throws CancelledByUserException { + checkForChanges(); + map = newMap; + changed = false; + viewer.setMap(map); + inspector.setMap(map); + viewer.repaint(); + } + + /** + * Get the map. + * + * @return The map. + */ + public GMLMap getMap() { + return map; + } + + /** + * Save the map. + * + * @throws MapException If there is a problem saving the map. + */ + public void save() throws MapException { + if (saveFile == null) { + JFileChooser chooser = new JFileChooser(baseDir); + if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { + saveFile = chooser.getSelectedFile(); + } + } + if (saveFile != null) { + Logger.debug("Saving to " + saveFile.getAbsolutePath()); + MapWriter.writeMap(map, saveFile, RobocupFormat.INSTANCE); + baseDir = saveFile.getParentFile(); + changed = false; + } + } + + /** + * Close the editor. + * + * @throws CancelledByUserException If the user cancels the close due to unsaved + * changes." + */ + public void close() throws CancelledByUserException { + checkForChanges(); + } + + /** + * Get the map viewer. + * + * @return The map viewer. + */ + public GMLMapViewer getViewer() { + return viewer; + } + + /** + * Get the object inspector. + * + * @return The object inspector. + */ + public GMLObjectInspector getInspector() { + return inspector; + } + + /** + * Register a change to the map. + */ + public void setChanged() { + changed = true; + } + + /** + * Register an undoable edit. + * + * @param edit The edit to add. + */ + public void addEdit(UndoableEdit edit) { + undoManager.addEdit(edit); + undoAction.setEnabled(undoManager.canUndo()); + redoAction.setEnabled(undoManager.canRedo()); + } + + /** + * Snap coordinates to the grid. + * + * @param c The coordinates to snap. + * @return The passed-in coordinates object. + */ + public GMLCoordinates snap(GMLCoordinates c) { + snap.snap(c); + return c; + } + + private void updatePositionLabels(final Point p) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + GMLCoordinates c = viewer.getCoordinatesAtPoint(p.x, p.y); + x.setText("X: " + FORMAT.format(c.getX())); + y.setText("Y: " + FORMAT.format(c.getY())); + } + }); + } + + private void checkForChanges() throws CancelledByUserException { + if (changed) { + switch (JOptionPane.showConfirmDialog(null, "The current map has changes. Do you want to save them?")) { + case JOptionPane.YES_OPTION: + try { + save(); + } catch (MapException e) { + JOptionPane.showMessageDialog(null, e); + throw new CancelledByUserException(); + } + break; + case JOptionPane.NO_OPTION: + changed = false; + return; + case JOptionPane.CANCEL_OPTION: + throw new CancelledByUserException(); + default: + throw new RuntimeException("JOptionPane.showConfirmDialog returned something weird"); + } + } + } + + private void createFileActions(JMenu menu, JToolBar toolbar) { + Action newAction = new AbstractAction("New") { + @Override + public void actionPerformed(ActionEvent e) { + try { + checkForChanges(); + setMap(new GMLMap()); + } catch (CancelledByUserException ex) { + return; + } + } + }; + Action loadAction = new AbstractAction("Load") { + @Override + public void actionPerformed(ActionEvent e) { + try { + checkForChanges(); + load(); + } catch (CancelledByUserException ex) { + return; + } catch (MapException ex) { + JOptionPane.showMessageDialog(null, ex); + } + } + }; + Action saveAction = new AbstractAction("Save") { + @Override + public void actionPerformed(ActionEvent e) { + try { + save(); + } catch (MapException ex) { + JOptionPane.showMessageDialog(null, ex); + } + } + }; + Action saveAsAction = new AbstractAction("Save as") { + @Override + public void actionPerformed(ActionEvent e) { + try { + saveFile = null; + save(); + } catch (MapException ex) { + JOptionPane.showMessageDialog(null, ex); + } + } + }; + toolbar.add(newAction); + toolbar.add(loadAction); + toolbar.add(saveAction); + toolbar.add(saveAsAction); + menu.add(newAction); + menu.add(loadAction); + menu.add(saveAction); + menu.add(saveAsAction); + } + + private void createViewActions(JMenu menu, JToolBar toolbar) { + final JCheckBox snapBox = new JCheckBox("Snap to grid", snap.isEnabled()); + final JSpinner snapSpinner = new JSpinner( + new SpinnerNumberModel(snap.getResolution(), SNAP_MIN_RESOLUTION, SNAP_MAX_RESOLUTION, SNAP_MIN_RESOLUTION)); + snapSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + snap.setResolution((Double) snapSpinner.getValue()); + } + }); + snapBox.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + snap.setEnabled(snapBox.isSelected()); + } + }); + Action gridAction = new AbstractAction("Show grid") { + @Override + public void actionPerformed(ActionEvent e) { + viewer.setGridEnabled((Boolean) getValue(Action.SELECTED_KEY)); + viewer.repaint(); + } + }; + gridAction.putValue(Action.SELECTED_KEY, false); + toolbar.add(snapSpinner); + toolbar.add(snapBox); + toolbar.add(new JToggleButton(gridAction)); + menu.add(new JCheckBoxMenuItem(gridAction)); + + // Create the "show objects" button and textfield + final JTextField showField = new JTextField(); + JButton showButton = new JButton("Show"); + showButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String s = showField.getText(); + List<GMLObject> objects = new ArrayList<GMLObject>(); + for (String next : s.split(",")) { + int id = Integer.parseInt(next.trim()); + GMLObject o = map.getObject(id); + if (o != null) { + objects.add(o); + } + } + viewer.view(objects); + viewer.repaint(); + } + }); + toolbar.addSeparator(); + toolbar.add(showField); + toolbar.add(showButton); + + // Add the reset zoom action + Action resetZoom = new AbstractAction("Reset zoom") { + @Override + public void actionPerformed(ActionEvent e) { + viewer.viewAll(); + viewer.repaint(); + } + }; + toolbar.addSeparator(); + menu.addSeparator(); + toolbar.add(resetZoom); + menu.add(resetZoom); + } + + private void createEditActions(JMenu menu, JToolBar toolbar) { + undoAction = new AbstractAction("Undo") { + @Override + public void actionPerformed(ActionEvent e) { + try { + undoManager.undo(); + } catch (CannotUndoException ex) { + JOptionPane.showMessageDialog(null, ex); + } + setEnabled(undoManager.canUndo()); + redoAction.setEnabled(undoManager.canRedo()); + } + }; + redoAction = new AbstractAction("Redo") { + @Override + public void actionPerformed(ActionEvent e) { + try { + undoManager.redo(); + } catch (CannotUndoException ex) { + JOptionPane.showMessageDialog(null, ex); + } + setEnabled(undoManager.canRedo()); + undoAction.setEnabled(undoManager.canUndo()); + } + }; + undoAction.setEnabled(false); + redoAction.setEnabled(false); + toolbar.add(undoAction); + toolbar.add(redoAction); + menu.add(undoAction); + menu.add(redoAction); + } + + private void createToolActions(JMenu menu, JToolBar toolbar) { + ButtonGroup toolbarGroup = new ButtonGroup(); + ButtonGroup menuGroup = new ButtonGroup(); + addTool(new InspectTool(this), menu, toolbar, menuGroup, toolbarGroup); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new CreateNodeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new CreateEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new CreateRoadTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new CreateBuildingTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new CreateSpaceTool(this), menu, null, menuGroup, null); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new DeleteNodeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new DeleteEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new DeleteShapeTool(this), menu, toolbar, menuGroup, toolbarGroup); + menu.addSeparator(); + toolbar.addSeparator(); + addTool(new MoveNodeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new MergeNodesTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new MergeLinesTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new SplitEdgeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new SplitShapeTool(this), menu, toolbar, menuGroup, toolbarGroup); + addTool(new TogglePassableTool(this), menu, toolbar, menuGroup, toolbarGroup); + } + + private void createFunctionActions(JMenu menu, JToolBar toolbar) { + addFunction(new ScaleFunction(this), menu, toolbar); + addFunction(new FixNearbyNodesFunction(this), menu, toolbar); + addFunction(new FixDuplicateEdgesFunction(this), menu, toolbar); + addFunction(new SplitEdgesFunction(this), menu, toolbar); + addFunction(new ComputePassableEdgesFunction(this), menu, toolbar); + addFunction(new PruneOrphanNodesFunction(this), menu, toolbar); + addFunction(new PruneOrphanEdgesFunction(this), menu, toolbar); + addFunction(new FixDegenerateShapesFunction(this), menu, toolbar); + addFunction(new FixAttachedObjectsFunction(this), menu, toolbar); + addFunction(new ValidateFunction(this), menu, toolbar); + addFunction(new AddNoiseFunction(this), menu, toolbar); + } + + private void addTool(final Tool t, JMenu menu, JToolBar toolbar, ButtonGroup menuGroup, ButtonGroup toolbarGroup) { + final JToggleButton toggle = new JToggleButton(); + final JCheckBoxMenuItem check = new JCheckBoxMenuItem(); + Action action = new AbstractAction(t.getName()) { + @Override + public void actionPerformed(ActionEvent e) { + if (currentTool != null) { + currentTool.deactivate(); + } + currentTool = t; + toggle.setSelected(true); + check.setSelected(true); + currentTool.activate(); + } + }; + toggle.setAction(action); + check.setAction(action); + menu.add(check); + if (toolbar != null) { + toolbar.add(toggle); + toolbarGroup.add(toggle); + } + menuGroup.add(check); + } + + private void addFunction(final Function f, JMenu menu, JToolBar toolbar) { + Action action = new AbstractAction(f.getName()) { + @Override + public void actionPerformed(ActionEvent e) { + f.execute(); + } + }; + toolbar.add(action); + menu.add(action); + } + + private class ViewerMouseListener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + updatePositionLabels(fixEventPoint(e.getPoint())); + } + + @Override + public void mouseDragged(MouseEvent e) { + updatePositionLabels(fixEventPoint(e.getPoint())); + } + + @Override + public void mouseClicked(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + updatePositionLabels(fixEventPoint(e.getPoint())); + } + + private Point fixEventPoint(Point p) { + Insets insets = viewer.getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/InspectTool.java b/modules/maps/src/maps/gml/editor/InspectTool.java new file mode 100644 index 0000000000000000000000000000000000000000..f4643c621ef259c1ff9f5588566929b86ff81423 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/InspectTool.java @@ -0,0 +1,174 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Point; +import java.awt.Insets; +import java.awt.Color; + +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLRoad; +import maps.gml.GMLBuilding; +import maps.gml.GMLSpace; +import maps.gml.GMLShape; +import maps.gml.GMLObject; +import maps.gml.GMLCoordinates; +import maps.gml.view.FilledShapeDecorator; +import maps.gml.view.NodeDecorator; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.view.LineEdgeDecorator; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A tool for inspecting objects. +*/ +public class InspectTool extends AbstractTool { + /** Distance in pixels to consider an object "nearby". */ + private static final int NEARBY = 5; + + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + private static final int NODE_SIZE = 5; + + private Listener listener; + private NodeDecorator nodeHighlight; + private EdgeDecorator edgeHighlight; + private FilledShapeDecorator shapeHighlight; + + /** + Construct an InspectTool. + @param editor The editor instance. + */ + public InspectTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, NODE_SIZE); + edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + shapeHighlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR); + } + + @Override + public String getName() { + return "Inspect object"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().clearAllBuildingDecorators(); + editor.getViewer().clearAllRoadDecorators(); + editor.getViewer().clearAllSpaceDecorators(); + editor.getViewer().repaint(); + } + + private void highlight(GMLObject object) { + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().clearAllBuildingDecorators(); + editor.getViewer().clearAllRoadDecorators(); + editor.getViewer().clearAllSpaceDecorators(); + if (object instanceof GMLNode) { + editor.getViewer().setNodeDecorator(nodeHighlight, (GMLNode)object); + } + if (object instanceof GMLEdge) { + editor.getViewer().setEdgeDecorator(edgeHighlight, (GMLEdge)object); + } + if (object instanceof GMLBuilding) { + editor.getViewer().setBuildingDecorator(shapeHighlight, (GMLBuilding)object); + } + if (object instanceof GMLRoad) { + editor.getViewer().setRoadDecorator(shapeHighlight, (GMLRoad)object); + } + if (object instanceof GMLSpace) { + editor.getViewer().setSpaceDecorator(shapeHighlight, (GMLSpace)object); + } + editor.getViewer().repaint(); + } + + private boolean closeEnough(GMLNode node, Point p) { + GMLCoordinates lowerLeft = editor.getViewer().getCoordinatesAtPoint(p.x - NEARBY, p.y + NEARBY); + GMLCoordinates topRight = editor.getViewer().getCoordinatesAtPoint(p.x + NEARBY, p.y - NEARBY); + return (node.getX() > lowerLeft.getX() && node.getX() < topRight.getX() && node.getY() > lowerLeft.getY() && node.getY() < topRight.getY()); + } + + private boolean closeEnough(GMLEdge edge, Point p) { + Point start = editor.getViewer().getScreenCoordinates(edge.getStart().getCoordinates()); + Point end = editor.getViewer().getScreenCoordinates(edge.getEnd().getCoordinates()); + Point2D startPoint = new Point2D(start.x, start.y); + Point2D endPoint = new Point2D(end.x, end.y); + Line2D line = new Line2D(startPoint, endPoint); + Point2D testPoint = new Point2D(p.x, p.y); + Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, testPoint); + return GeometryTools2D.getDistance(testPoint, closest) < NEARBY; + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + editor.getInspector().inspect(findNearbyObject(p)); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + highlight(findNearbyObject(p)); + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private GMLObject findNearbyObject(Point p) { + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + GMLEdge edge = editor.getMap().findNearestEdge(c.getX(), c.getY()); + GMLShape shape = editor.getMap().findShapeUnder(c.getX(), c.getY()); + // If the node is close enough inspect that + // Otherwise, if the edge is close enough + // Otherwise the shape + if (node != null && closeEnough(node, p)) { + return node; + } + else if (edge != null && closeEnough(edge, p)) { + return edge; + } + else { + return shape; + } + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/MergeLinesTool.java b/modules/maps/src/maps/gml/editor/MergeLinesTool.java new file mode 100644 index 0000000000000000000000000000000000000000..3ca00c1694892825161edf2599eacdcaa6a378e0 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/MergeLinesTool.java @@ -0,0 +1,169 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLObject; + +/** + A tool for merging lines by deleting a common node. +*/ +public class MergeLinesTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + private static final int HIGHLIGHT_SIZE = 6; + + private Listener listener; + private NodeDecorator nodeHighlight; + private EdgeDecorator edgeHighlight; + private GMLNode selected; + private Collection<GMLEdge> attachedEdges; + + /** + Construct a MergeLinesTool. + @param editor The editor instance. + */ + public MergeLinesTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE); + edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + selected = null; + attachedEdges = new HashSet<GMLEdge>(); + } + + @Override + public String getName() { + return "Merge lines"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + selected = null; + attachedEdges.clear(); + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + } + + private void highlightNode(GMLNode node, Collection<GMLEdge> attached) { + if (selected == node) { + return; + } + if (selected != null) { + editor.getViewer().clearNodeDecorator(selected); + editor.getViewer().clearEdgeDecorator(attachedEdges); + } + selected = node; + attachedEdges.clear(); + if (selected != null) { + attachedEdges.addAll(attached); + editor.getViewer().setNodeDecorator(nodeHighlight, selected); + editor.getViewer().setEdgeDecorator(edgeHighlight, attachedEdges); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + Collection<GMLEdge> attached = editor.getMap().getAttachedEdges(node); + if (attached.size() == 2) { + highlightNode(node, attached); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (selected == null) { + return; + } + if (e.getButton() == MouseEvent.BUTTON1) { + Iterator<GMLEdge> it = attachedEdges.iterator(); + GMLEdge newEdge = editor.getMap().mergeEdges(it.next(), it.next()); + Collection<GMLObject> deleted = editor.getMap().removeNode(selected); + editor.setChanged(); + editor.addEdit(new MergeEdit(selected, deleted, newEdge)); + highlightNode(null, null); + } + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class MergeEdit extends AbstractUndoableEdit { + private GMLNode deletedNode; + private Collection<GMLObject> deletedObjects; + private GMLEdge newEdge; + + public MergeEdit(GMLNode node, Collection<GMLObject> deletedObjects, GMLEdge newEdge) { + this.deletedNode = node; + this.deletedObjects = deletedObjects; + this.newEdge = newEdge; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().addNode(deletedNode); + editor.getMap().add(deletedObjects); + editor.getMap().removeEdge(newEdge); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().removeNode(deletedNode); + editor.getMap().remove(deletedObjects); + editor.getMap().addEdge(newEdge); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/MergeNodesTool.java b/modules/maps/src/maps/gml/editor/MergeNodesTool.java new file mode 100644 index 0000000000000000000000000000000000000000..c9b128494e4077295c31881f2092dffd42c53238 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/MergeNodesTool.java @@ -0,0 +1,162 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.GMLNode; +import maps.gml.GMLCoordinates; + +/** + A tool for merging nodes. +*/ +public class MergeNodesTool extends AbstractTool { + private static final Color HOVER_COLOUR = Color.BLUE; + private static final Color MERGE_COLOUR = Color.RED; + private static final int HIGHLIGHT_SIZE = 6; + + private Listener listener; + private NodeDecorator hoverHighlight; + private NodeDecorator mergeHighlight; + private GMLNode hover; + private GMLNode merge; + private boolean merging; + + /** + Construct a MergeNodesTool. + @param editor The editor instance. + */ + public MergeNodesTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + hoverHighlight = new SquareNodeDecorator(HOVER_COLOUR, HIGHLIGHT_SIZE); + mergeHighlight = new SquareNodeDecorator(MERGE_COLOUR, HIGHLIGHT_SIZE); + } + + @Override + public String getName() { + return "Merge nodes"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + hover = null; + merge = null; + merging = false; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().repaint(); + } + + private void hover(GMLNode node) { + if (hover == node) { + return; + } + if (hover != null) { + editor.getViewer().clearNodeDecorator(hover); + } + hover = node; + if (hover != null) { + editor.getViewer().setNodeDecorator(hoverHighlight, hover); + } + editor.getViewer().repaint(); + } + + private void setMerge(GMLNode node) { + if (merge == node) { + return; + } + if (node == hover) { + return; + } + if (merge != null) { + editor.getViewer().clearNodeDecorator(merge); + } + merge = node; + if (merge != null) { + editor.getViewer().setNodeDecorator(mergeHighlight, merge); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + hover(node); + } + + @Override + public void mouseDragged(MouseEvent e) { + if (hover == null) { + return; + } + if (!merging) { + return; + } + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + setMerge(node); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + if (merging) { + if (hover != null && merge != null) { + editor.getMap().replaceNode(hover, merge); + editor.getMap().removeNode(hover); + editor.setChanged(); + } + if (hover != null) { + editor.getViewer().clearNodeDecorator(hover); + } + if (merge != null) { + editor.getViewer().clearNodeDecorator(merge); + } + editor.getViewer().repaint(); + hover = null; + merge = null; + merging = false; + } + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + merging = true; + } + } + + @Override + public void mouseClicked(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/MoveNodeTool.java b/modules/maps/src/maps/gml/editor/MoveNodeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..f6dc6e75f8447a884d9e81489a31f6336176b813 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/MoveNodeTool.java @@ -0,0 +1,162 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.GMLNode; +import maps.gml.GMLCoordinates; + +import javax.swing.undo.AbstractUndoableEdit; + +/** + A tool for moving nodes. +*/ +public class MoveNodeTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLACK; + private static final int HIGHLIGHT_SIZE = 6; + + private Listener listener; + private NodeDecorator highlight; + private GMLNode selected; + private GMLCoordinates pressCoords; + private GMLCoordinates originalCoords; + + /** + Construct a MoveNodeTool. + @param editor The editor instance. + */ + public MoveNodeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + highlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE); + selected = null; + } + + @Override + public String getName() { + return "Move node"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + selected = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().repaint(); + } + + private void highlightNode(GMLNode node) { + if (selected == node) { + return; + } + if (selected != null) { + editor.getViewer().clearNodeDecorator(selected); + } + selected = node; + if (selected != null) { + editor.getViewer().setNodeDecorator(highlight, selected); + } + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLNode node = editor.getMap().findNearestNode(c.getX(), c.getY()); + highlightNode(node); + } + + @Override + public void mousePressed(MouseEvent e) { + if (selected == null) { + return; + } + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = fixEventPoint(e.getPoint()); + pressCoords = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + originalCoords = new GMLCoordinates(selected.getCoordinates()); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + pressCoords = null; + editor.addEdit(new MoveNodeEdit(selected, originalCoords, new GMLCoordinates(selected.getCoordinates()))); + } + + @Override + public void mouseDragged(MouseEvent e) { + if (selected == null) { + return; + } + if (pressCoords == null) { + return; + } + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates dragCoords = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + double dx = dragCoords.getX() - pressCoords.getX(); + double dy = dragCoords.getY() - pressCoords.getY(); + GMLCoordinates result = new GMLCoordinates(originalCoords.getX() + dx, originalCoords.getY() + dy); + editor.snap(result); + selected.setCoordinates(result); + editor.setChanged(); + editor.getViewer().repaint(); + } + + @Override + public void mouseClicked(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class MoveNodeEdit extends AbstractUndoableEdit { + private GMLNode node; + private GMLCoordinates oldPosition; + private GMLCoordinates newPosition; + + public MoveNodeEdit(GMLNode node, GMLCoordinates oldPosition, GMLCoordinates newPosition) { + this.node = node; + this.oldPosition = oldPosition; + this.newPosition = newPosition; + } + + @Override + public void undo() { + super.undo(); + node.setCoordinates(oldPosition); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + node.setCoordinates(newPosition); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/PanZoomTool.java b/modules/maps/src/maps/gml/editor/PanZoomTool.java new file mode 100644 index 0000000000000000000000000000000000000000..308375bd4c0b011a51df4e6e7e315b0ffc0413bb --- /dev/null +++ b/modules/maps/src/maps/gml/editor/PanZoomTool.java @@ -0,0 +1,27 @@ +package maps.gml.editor; + +/** + A tool for panning and zooming the view. +*/ +public class PanZoomTool extends AbstractTool { + /** + Construct a PanZoomTool. + @param editor The editor instance. + */ + public PanZoomTool(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Pan/Zoom"; + } + + @Override + public void activate() { + } + + @Override + public void deactivate() { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/ProgressFunction.java b/modules/maps/src/maps/gml/editor/ProgressFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..70c0ca37b9da75dc735c6577f8ac9fd69832db76 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/ProgressFunction.java @@ -0,0 +1,139 @@ +package maps.gml.editor; + +import java.awt.Window; +import java.awt.Dialog; +import java.awt.BorderLayout; +import javax.swing.JDialog; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; + +import rescuecore2.log.Logger; + +/** + Abstract base class for Function implementations that require a progress dialog. +*/ +public abstract class ProgressFunction extends AbstractFunction { + private JProgressBar progress; + + /** + Construct a ProgressFunction. + @param editor The editor instance. + */ + protected ProgressFunction(GMLEditor editor) { + super(editor); + progress = new JProgressBar(); + progress.setStringPainted(true); + } + + @Override + public void execute() { + final JDialog dialog = new JDialog((Window)editor.getViewer().getTopLevelAncestor(), getTitle(), Dialog.ModalityType.APPLICATION_MODAL); + dialog.getContentPane().add(progress, BorderLayout.CENTER); + dialog.pack(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setValue(0); + progress.setIndeterminate(true); + } + }); + Thread t = new Thread() { + @Override + public void run() { + try { + executeImpl(); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (RuntimeException e) { + // CHECKSTYLE:ON:IllegalCatch + Logger.error("Error running " + this, e); + } + dialog.setVisible(false); + dialog.dispose(); + } + }; + t.start(); + dialog.setVisible(true); + } + + /** + Execute the function. + */ + protected abstract void executeImpl(); + + /** + Get the title for the progress dialog. + @return The dialog title. + */ + protected abstract String getTitle(); + + /** + Set the progress level. + @param amount The new progress. + */ + protected void setProgress(final int amount) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setValue(amount); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Increase the progress level by one. + */ + protected void bumpProgress() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setValue(progress.getValue() + 1); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Increase the maximum progress level by one. + */ + protected void bumpMaxProgress() { + bumpMaxProgress(1); + } + + /** + Increase the maximum progress level by some amount. + @param amount The amount to increase the maximum progress level. + */ + protected void bumpMaxProgress(final int amount) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setMaximum(progress.getMaximum() + amount); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Set the progress maximum. + @param max The new progress maximum. + */ + protected void setProgressLimit(final int max) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setIndeterminate(false); + progress.setMaximum(max); + progress.setString(progress.getValue() + " / " + progress.getMaximum()); + } + }); + } + + /** + Set the progress string. + @param s The new progress string. + */ + protected void setProgressString(final String s) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + progress.setString(s); + } + }); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/PruneOrphanEdgesFunction.java b/modules/maps/src/maps/gml/editor/PruneOrphanEdgesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..039940aa375272f595a7f90c66896351ba45bb6f --- /dev/null +++ b/modules/maps/src/maps/gml/editor/PruneOrphanEdgesFunction.java @@ -0,0 +1,69 @@ +package maps.gml.editor; + +import javax.swing.undo.AbstractUndoableEdit; + +import java.util.HashSet; +import java.util.Collection; + +import maps.gml.GMLEdge; + +import rescuecore2.log.Logger; + +/** + A function for pruning edges that are not attached to any shapes. +*/ +public class PruneOrphanEdgesFunction extends AbstractFunction { + /** + Construct a PruneOrphanEdgesFunction. + @param editor The editor instance. + */ + public PruneOrphanEdgesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Prune orphaned edges"; + } + + @Override + public void execute() { + // Go through all edges and remove any that are not attached to shapes. + final Collection<GMLEdge> remaining = new HashSet<GMLEdge>(editor.getMap().getEdges()); + final Collection<GMLEdge> deleted = new HashSet<GMLEdge>(); + for (GMLEdge next : remaining) { + if (editor.getMap().getAttachedShapes(next).isEmpty()) { + editor.getMap().removeEdge(next); + deleted.add(next); + } + } + if (!deleted.isEmpty()) { + editor.setChanged(); + editor.getViewer().repaint(); + } + Logger.debug("Removed " + deleted.size() + " edges"); + editor.addEdit(new DeleteEdgesEdit(deleted)); + } + + private class DeleteEdgesEdit extends AbstractUndoableEdit { + private Collection<GMLEdge> edges; + + public DeleteEdgesEdit(Collection<GMLEdge> edges) { + this.edges = edges; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().add(edges); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().remove(edges); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/PruneOrphanNodesFunction.java b/modules/maps/src/maps/gml/editor/PruneOrphanNodesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..91c18e8f7693a89c74396b68c9849c14a927cd33 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/PruneOrphanNodesFunction.java @@ -0,0 +1,69 @@ +package maps.gml.editor; + +import javax.swing.undo.AbstractUndoableEdit; + +import java.util.HashSet; +import java.util.Collection; + +import maps.gml.GMLNode; + +import rescuecore2.log.Logger; + +/** + A function for pruning nodes that are not attached to any edges. +*/ +public class PruneOrphanNodesFunction extends AbstractFunction { + /** + Construct a PruneOrphanNodesFunction. + @param editor The editor instance. + */ + public PruneOrphanNodesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Prune orphaned nodes"; + } + + @Override + public void execute() { + // Go through all nodes and remove any that are not attached to edges. + final Collection<GMLNode> remaining = new HashSet<GMLNode>(editor.getMap().getNodes()); + final Collection<GMLNode> deleted = new HashSet<GMLNode>(); + for (GMLNode next : remaining) { + if (editor.getMap().getAttachedEdges(next).isEmpty()) { + editor.getMap().removeNode(next); + deleted.add(next); + } + } + if (!deleted.isEmpty()) { + editor.setChanged(); + editor.getViewer().repaint(); + } + Logger.debug("Removed " + deleted.size() + " nodes"); + editor.addEdit(new DeleteNodesEdit(deleted)); + } + + private class DeleteNodesEdit extends AbstractUndoableEdit { + private Collection<GMLNode> nodes; + + public DeleteNodesEdit(Collection<GMLNode> nodes) { + this.nodes = nodes; + } + + @Override + public void undo() { + super.undo(); + editor.getMap().add(nodes); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + editor.getMap().remove(nodes); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/ScaleFunction.java b/modules/maps/src/maps/gml/editor/ScaleFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..59178d91a9e309335d4b381d9939dd7dfbe468d0 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/ScaleFunction.java @@ -0,0 +1,38 @@ +package maps.gml.editor; + +import javax.swing.JOptionPane; + +import maps.ScaleConversion; + +/** + A function for scaling the map. +*/ +public class ScaleFunction extends AbstractFunction { + /** + Construct a ScaleFunction. + @param editor The editor instance. + */ + public ScaleFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Scale map"; + } + + @Override + public void execute() { + String s = JOptionPane.showInputDialog("Enter scale factor"); + if (s != null) { + try { + double factor = Double.parseDouble(s); + editor.getMap().convertCoordinates(new ScaleConversion(editor.getMap().getMinX(), editor.getMap().getMinY(), factor, factor)); + editor.setChanged(); + } + catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/Snap.java b/modules/maps/src/maps/gml/editor/Snap.java new file mode 100644 index 0000000000000000000000000000000000000000..2886082e116f6aebea5ee2fa543afe1934d9dd16 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/Snap.java @@ -0,0 +1,73 @@ +package maps.gml.editor; + +import maps.gml.GMLCoordinates; + +/** + Class for snapping coordinates to a grid. +*/ +public class Snap { + private static final double DEFAULT_RESOLUTION = 0.001; + + private double resolution; + private boolean enabled; + + /** + Create a disabled Snap with default resolution. + */ + public Snap() { + resolution = DEFAULT_RESOLUTION; + enabled = false; + } + + /** + Set the resolution. + @param d The new resolution. + */ + public void setResolution(double d) { + resolution = d; + } + + /** + Get the resolution. + @return The resolution. + */ + public double getResolution() { + return resolution; + } + + /** + Set whether to enable snapping. + @param b Whether to enable snapping. + */ + public void setEnabled(boolean b) { + enabled = b; + } + + /** + Find out if this Snap is enabled. + @return True if enabled. + */ + public boolean isEnabled() { + return enabled; + } + + /** + Snap a set of coordinates to the grid. + @param c The coordinates to snap. + */ + public void snap(GMLCoordinates c) { + if (!enabled) { + return; + } + double x = round(c.getX()); + double y = round(c.getY()); + c.setX(x); + c.setY(y); + } + + private double round(double d) { + // CHECKSTYLE:OFF:MagicNumber + return Math.floor((d / resolution) + 0.5) * resolution; + // CHECKSTYLE:ON:MagicNumber + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/SplitEdgeTool.java b/modules/maps/src/maps/gml/editor/SplitEdgeTool.java new file mode 100644 index 0000000000000000000000000000000000000000..e72c9a20d0675a0e959343c602ed6ddba83e2dd2 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/SplitEdgeTool.java @@ -0,0 +1,148 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLTools; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A tool for splitting edges. +*/ +public class SplitEdgeTool extends AbstractTool { + private static final Color EDGE_COLOUR = Color.BLUE; + private static final Color NODE_COLOUR = Color.BLACK; + private static final int NODE_SIZE = 6; + + private Listener listener; + private NodeDecorator nodeHighlight; + private EdgeDecorator edgeHighlight; + private GMLNode node; + private GMLEdge edge; + + /** + Construct a SplitEdgeTool. + @param editor The editor instance. + */ + public SplitEdgeTool(GMLEditor editor) { + super(editor); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator(NODE_COLOUR, NODE_SIZE); + edgeHighlight = new LineEdgeDecorator(EDGE_COLOUR); + edge = null; + node = null; + } + + @Override + public String getName() { + return "Split edge"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + node = null; + edge = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + if (node != null) { + editor.getMap().removeNode(node); + } + } + + private void update(GMLCoordinates c) { + if (node == null) { + node = editor.getMap().createNode(c); + editor.getViewer().setNodeDecorator(nodeHighlight, node); + } + GMLEdge newEdge = editor.getMap().findNearestEdge(c.getX(), c.getY()); + if (newEdge != edge) { + if (edge != null) { + editor.getViewer().clearEdgeDecorator(edge); + } + edge = newEdge; + editor.getViewer().setEdgeDecorator(edgeHighlight, edge); + } + // Snap the node coordinates to the edge + Line2D line = GMLTools.toLine(edge); + Point2D point = new Point2D(c.getX(), c.getY()); + Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, point); + c.setX(closest.getX()); + c.setY(closest.getY()); + node.setCoordinates(c); + editor.getViewer().repaint(); + } + + private void split() { + if (node == null || edge == null) { + return; + } + editor.getMap().splitEdge(edge, node); + editor.getMap().removeEdge(edge); + editor.setChanged(); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + node = null; + edge = null; + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.snap(editor.getViewer().getCoordinatesAtPoint(p.x, p.y)); + update(c); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + split(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/SplitEdgesFunction.java b/modules/maps/src/maps/gml/editor/SplitEdgesFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..7b99c24eeba91170418ea749a2d39283c94a6814 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/SplitEdgesFunction.java @@ -0,0 +1,98 @@ +package maps.gml.editor; + +import javax.swing.JOptionPane; + +import java.util.Queue; +import java.util.LinkedList; +import java.util.Collection; +import java.util.HashSet; + +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLTools; + +import rescuecore2.log.Logger; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A function for splitting edges that cover nearby nodes. +*/ +public class SplitEdgesFunction extends ProgressFunction { + private static final double DEFAULT_THRESHOLD = 0.001; + + private double threshold; + + /** + Construct a SplitEdgesFunction. + @param editor The editor instance. + */ + public SplitEdgesFunction(GMLEditor editor) { + super(editor); + } + + @Override + public String getName() { + return "Split edges"; + } + + @Override + public void execute() { + String s = JOptionPane.showInputDialog(editor.getViewer(), "Enter the desired distance threshold (in m)", DEFAULT_THRESHOLD); + if (s == null) { + return; + } + threshold = Double.parseDouble(s); + super.execute(); + } + + @Override + protected String getTitle() { + return "Splitting edges"; + } + + @Override + protected void executeImpl() { + // Go through all edges and split any that cover nearby nodes + final Queue<GMLEdge> remaining = new LinkedList<GMLEdge>(); + final Collection<GMLNode> nodes = new HashSet<GMLNode>(); + synchronized (editor.getMap()) { + remaining.addAll(editor.getMap().getEdges()); + nodes.addAll(editor.getMap().getNodes()); + } + setProgressLimit(remaining.size()); + int count = 0; + while (!remaining.isEmpty()) { + GMLEdge next = remaining.remove(); + Line2D line = GMLTools.toLine(next); + // Look for nodes that are close to the line + for (GMLNode node : nodes) { + if (node == next.getStart() || node == next.getEnd()) { + continue; + } + Point2D p = GMLTools.toPoint(node); + Point2D closest = GeometryTools2D.getClosestPointOnSegment(line, p); + if (GeometryTools2D.getDistance(p, closest) < threshold) { + // Split the edge + Collection<GMLEdge> newEdges; + synchronized (editor.getMap()) { + newEdges = editor.getMap().splitEdge(next, node); + editor.getMap().removeEdge(next); + newEdges.removeAll(editor.getMap().getEdges()); + } + remaining.addAll(newEdges); + bumpMaxProgress(newEdges.size()); + ++count; + break; + } + } + bumpProgress(); + } + if (count != 0) { + editor.setChanged(); + editor.getViewer().repaint(); + } + Logger.debug("Split " + count + " edges"); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/SplitShapeTool.java b/modules/maps/src/maps/gml/editor/SplitShapeTool.java new file mode 100755 index 0000000000000000000000000000000000000000..24695995035f54e8c5ab6e6e253874544394cf64 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/SplitShapeTool.java @@ -0,0 +1,362 @@ +package maps.gml.editor; + +import java.awt.Color; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import javax.swing.undo.AbstractUndoableEdit; + +import maps.gml.GMLBuilding; +import maps.gml.GMLCoordinates; +import maps.gml.GMLEdge; +import maps.gml.GMLNode; +import maps.gml.GMLRoad; +import maps.gml.GMLShape; +import maps.gml.GMLSpace; +import maps.gml.view.LineOverlay; +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import rescuecore2.log.Logger; +import rescuecore2.misc.Pair; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Point2D; + +/** + * A tool for creating edges. + */ +public class SplitShapeTool extends AbstractTool { + + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + private static final int HIGHLIGHT_SIZE = 6; + + private static final double THRESHOLD = 0.001; + + private Listener listener; + private NodeDecorator nodeHighlight; + private LineOverlay overlay; + + private GMLNode hover; + private GMLNode start; + private GMLNode end; + // private GMLEdge edge; + + + /** + * Construct a CreateEdgeTool. + * + * @param editor + * The editor instance. + */ + public SplitShapeTool( GMLEditor editor ) { + super( editor ); + listener = new Listener(); + nodeHighlight = new SquareNodeDecorator( HIGHLIGHT_COLOUR, HIGHLIGHT_SIZE ); + overlay = new LineOverlay( HIGHLIGHT_COLOUR, true ); + } + + + @Override + public String getName() { + return "Split shape"; + } + + + @Override + public void activate() { + editor.getViewer().addMouseListener( listener ); + editor.getViewer().addMouseMotionListener( listener ); + editor.getViewer().addOverlay( overlay ); + hover = null; + start = null; + end = null; + // edge = null; + } + + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener( listener ); + editor.getViewer().removeMouseMotionListener( listener ); + editor.getViewer().clearAllNodeDecorators(); + editor.getViewer().removeOverlay( overlay ); + editor.getViewer().repaint(); + } + + + private void setHover( GMLNode node ) { + if ( hover == node ) { + return; + } + if ( hover != null ) { + editor.getViewer().clearNodeDecorator( hover ); + } + hover = node; + if ( hover != null ) { + editor.getViewer().setNodeDecorator( nodeHighlight, hover ); + } + editor.getViewer().repaint(); + } + + + private void setStart( GMLNode node ) { + if ( start == node ) { + return; + } + if ( start != null ) { + editor.getViewer().clearNodeDecorator( start ); + } + start = node; + if ( start != null ) { + editor.getViewer().setNodeDecorator( nodeHighlight, start ); + } + editor.getViewer().repaint(); + } + + + private void setEnd( GMLNode node ) { + if ( start == node || end == node ) { + return; + } + if ( end != null ) { + editor.getViewer().clearNodeDecorator( end ); + } + end = node; + if ( end != null ) { + editor.getViewer().setNodeDecorator( nodeHighlight, end ); + } + editor.getViewer().repaint(); + } + + + private class Listener implements MouseListener, MouseMotionListener { + + @Override + public void mousePressed( MouseEvent e ) { + if ( e.getButton() == MouseEvent.BUTTON1 ) { + Point p = fixEventPoint( e.getPoint() ); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint( p.x, p.y ); + GMLNode node = editor.getMap().findNearestNode( c.getX(), c.getY() ); + overlay.setStart( new Point2D( node.getX(), node.getY() ) ); + setStart( node ); + setHover( null ); + } + } + + + @Override + public void mouseReleased( MouseEvent e ) { + if ( e.getButton() == MouseEvent.BUTTON1 ) { + if ( start != null && end != null ) { + SplitShapeEdit edit = splitByEdge(); + editor.setChanged(); + if ( edit != null ) { + editor.addEdit( edit ); + } + editor.getViewer().clearAllNodeDecorators(); + overlay.setStart( null ); + overlay.setEnd( null ); + editor.getViewer().repaint(); + start = null; + end = null; + hover = null; + } + } + } + + + private SplitShapeEdit splitByEdge() { + Collection<GMLShape> add = new ArrayList<GMLShape>(); + Collection<GMLShape> delete = new ArrayList<GMLShape>(); + + GMLEdge edge = editor.getMap().createEdge( start, end ); + Collection<GMLEdge> startEdges = editor.getMap() + .getAttachedEdges( start ); + Collection<GMLEdge> endEdges = editor.getMap().getAttachedEdges( end ); + Collection<GMLShape> startShapes = new HashSet<GMLShape>(); + Collection<GMLShape> endShapes = new HashSet<GMLShape>(); + for ( GMLEdge next : startEdges ) { + startShapes.addAll( editor.getMap().getAttachedShapes( next ) ); + } + for ( GMLEdge next : endEdges ) { + endShapes.addAll( editor.getMap().getAttachedShapes( next ) ); + } + for ( GMLShape shape : startShapes ) { + if ( endShapes.contains( shape ) ) { + Pair<GMLShape, GMLShape> split = splitShape( shape, edge ); + if ( split != null ) { + add.add( split.first() ); + add.add( split.second() ); + delete.add( shape ); + } + } + } + if ( !add.isEmpty() ) { + edge.setPassable( true ); + return new SplitShapeEdit( edge, add, delete ); + } else { + editor.getMap().remove( edge ); + return null; + } + } + + + private Pair<GMLShape, GMLShape> splitShape( GMLShape shape, + GMLEdge edge ) { + List<GMLNode> nodes1 = new ArrayList<GMLNode>(); + List<GMLNode> nodes2 = new ArrayList<GMLNode>(); + boolean first = true; + for ( GMLNode n : shape.getUnderlyingNodes() ) { + if ( n == edge.getStart() || n == edge.getEnd() ) { + first = !first; + nodes1.add( n ); + nodes2.add( n ); + } else if ( first ) { + nodes1.add( n ); + } else { + nodes2.add( n ); + } + } + if ( nodes1.size() <= 2 || nodes2.size() <= 2 ) { + return null; + } + + // Check if we really split an interior edge + double oldArea = area( shape.getUnderlyingNodes() ); + double area1 = area( nodes1 ); + double area2 = area( nodes2 ); + if ( area1 + area2 > oldArea + THRESHOLD ) { + return null; + } + + GMLShape s1 = null; + GMLShape s2 = null; + if ( shape instanceof GMLBuilding ) { + GMLBuilding b = (GMLBuilding) shape; + GMLBuilding b1 = editor.getMap().createBuildingFromNodes( nodes1 ); + GMLBuilding b2 = editor.getMap().createBuildingFromNodes( nodes2 ); + b1.setCode( b.getCode() ); + b2.setCode( b.getCode() ); + b1.setFloors( b.getFloors() ); + b2.setFloors( b.getFloors() ); + b1.setImportance( b.getImportance() ); + b2.setImportance( b.getImportance() ); + b1.setCapacity( b.getCapacity() ); + b2.setCapacity( b.getCapacity() ); + s1 = b1; + s2 = b2; + } else if ( shape instanceof GMLRoad ) { + // GMLBuilding b = (GMLBuilding) shape; + s1 = editor.getMap().createRoadFromNodes( nodes1 ); + s2 = editor.getMap().createRoadFromNodes( nodes2 ); + } else if ( shape instanceof GMLSpace ) { + // GMLBuilding b = (GMLBuilding) shape; + s1 = editor.getMap().createSpaceFromNodes( nodes1 ); + s2 = editor.getMap().createSpaceFromNodes( nodes2 ); + } else { + throw new IllegalArgumentException( + "Shape is not a building, road or space" ); + } + editor.getMap().remove( shape ); + return new Pair<GMLShape, GMLShape>( s1, s2 ); + } + + + private double area( List<GMLNode> nodes ) { + List<Point2D> vertices = new ArrayList<Point2D>(); + for ( GMLNode n : nodes ) { + vertices.add( new Point2D( n.getX(), n.getY() ) ); + } + return GeometryTools2D.computeArea( vertices ); + } + + + @Override + public void mouseDragged( MouseEvent e ) { + if ( start != null ) { + Point p = fixEventPoint( e.getPoint() ); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint( p.x, p.y ); + GMLNode node = editor.getMap().findNearestNode( c.getX(), c.getY() ); + overlay.setEnd( new Point2D( node.getX(), node.getY() ) ); + setEnd( node ); + } + } + + + @Override + public void mouseMoved( MouseEvent e ) { + Point p = fixEventPoint( e.getPoint() ); + GMLCoordinates c = editor + .snap( editor.getViewer().getCoordinatesAtPoint( p.x, p.y ) ); + GMLNode node = editor.getMap().findNearestNode( c.getX(), c.getY() ); + setHover( node ); + } + + + @Override + public void mouseClicked( MouseEvent e ) { + } + + + @Override + public void mouseEntered( MouseEvent e ) { + } + + + @Override + public void mouseExited( MouseEvent e ) { + } + + + private Point fixEventPoint( Point p ) { + Insets insets = editor.getViewer().getInsets(); + return new Point( p.x - insets.left, p.y - insets.top ); + } + } + + private class SplitShapeEdit extends AbstractUndoableEdit { + + private Collection<GMLShape> add; + private Collection<GMLShape> remove; + private GMLEdge edge; + + + public SplitShapeEdit( GMLEdge edge, Collection<GMLShape> add, Collection<GMLShape> remove ) { + this.edge = edge; + this.add = add; + this.remove = remove; + } + + + @Override + public void undo() { + super.undo(); + editor.getMap().removeEdge( edge ); + editor.getMap().remove( add ); + editor.getMap().add( remove ); + editor.getViewer().repaint(); + } + + + @Override + public void redo() { + super.redo(); + editor.getMap().addEdge( edge ); + for ( GMLShape r : remove ) { + Logger.debug( "remove: " + r.toString() ); + } + for ( GMLShape r : add ) { + Logger.debug( "add: " + r.toString() ); + } + editor.getMap().remove( remove ); + editor.getMap().add( add ); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/TogglePassableTool.java b/modules/maps/src/maps/gml/editor/TogglePassableTool.java new file mode 100644 index 0000000000000000000000000000000000000000..39b0735cfb361cdf7067e10d2fa37a02462235d2 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/TogglePassableTool.java @@ -0,0 +1,166 @@ +package maps.gml.editor; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.Color; +import java.awt.Point; +import java.awt.Insets; + +import javax.swing.undo.AbstractUndoableEdit; + +import java.util.Collection; +import java.util.Iterator; + +import maps.gml.GMLEdge; +import maps.gml.GMLShape; +import maps.gml.GMLCoordinates; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.LineEdgeDecorator; + +/** + A tool for toggling the passable flag on edges. +*/ +public class TogglePassableTool extends AbstractTool { + private static final Color HIGHLIGHT_COLOUR = Color.BLUE; + + private Listener listener; + private EdgeDecorator highlight; + private GMLEdge selected; + + /** + Construct a TogglePassableTool. + @param editor The editor instance. + */ + public TogglePassableTool(GMLEditor editor) { + super(editor); + highlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + listener = new Listener(); + } + + @Override + public String getName() { + return "Toggle passable"; + } + + @Override + public void activate() { + editor.getViewer().addMouseListener(listener); + editor.getViewer().addMouseMotionListener(listener); + selected = null; + } + + @Override + public void deactivate() { + editor.getViewer().removeMouseListener(listener); + editor.getViewer().removeMouseMotionListener(listener); + editor.getViewer().clearAllEdgeDecorators(); + editor.getViewer().repaint(); + } + + private void highlight(GMLEdge edge) { + if (selected == edge) { + return; + } + if (selected != null) { + editor.getViewer().clearEdgeDecorator(selected); + } + selected = edge; + if (selected != null) { + editor.getViewer().setEdgeDecorator(highlight, selected); + } + editor.getViewer().repaint(); + } + + private void toggle() { + boolean isPassable = !selected.isPassable(); + setPassable(selected, isPassable); + editor.addEdit(new ToggleEdit(selected, isPassable)); + } + + private void setPassable(GMLEdge edge, boolean passable) { + edge.setPassable(passable); + Collection<GMLShape> attached = editor.getMap().getAttachedShapes(edge); + Iterator<GMLShape> it = attached.iterator(); + GMLShape first = it.next(); + GMLShape second = it.next(); + if (passable) { + first.setNeighbour(edge, second.getID()); + second.setNeighbour(edge, first.getID()); + } + else { + first.setNeighbour(edge, null); + second.setNeighbour(edge, null); + } + editor.setChanged(); + editor.getViewer().repaint(); + } + + private class Listener implements MouseListener, MouseMotionListener { + @Override + public void mouseMoved(MouseEvent e) { + Point p = fixEventPoint(e.getPoint()); + GMLCoordinates c = editor.getViewer().getCoordinatesAtPoint(p.x, p.y); + GMLEdge edge = editor.getMap().findNearestEdge(c.getX(), c.getY()); + if (editor.getMap().getAttachedShapes(edge).size() == 2) { + highlight(edge); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + if (selected == null) { + return; + } + if (e.getButton() == MouseEvent.BUTTON1) { + toggle(); + highlight(null); + } + } + + @Override + public void mousePressed(MouseEvent e) { + } + @Override + public void mouseReleased(MouseEvent e) { + } + @Override + public void mouseDragged(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + + private Point fixEventPoint(Point p) { + Insets insets = editor.getViewer().getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } + } + + private class ToggleEdit extends AbstractUndoableEdit { + private GMLEdge edge; + private boolean newPassable; + + public ToggleEdit(GMLEdge edge, boolean newPassable) { + this.edge = edge; + this.newPassable = newPassable; + } + + @Override + public void undo() { + super.undo(); + setPassable(edge, !newPassable); + editor.getViewer().repaint(); + } + + @Override + public void redo() { + super.redo(); + setPassable(edge, newPassable); + editor.getViewer().repaint(); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/Tool.java b/modules/maps/src/maps/gml/editor/Tool.java new file mode 100644 index 0000000000000000000000000000000000000000..405c59e78c9af7ad5944bc8b678588dbf91cf3aa --- /dev/null +++ b/modules/maps/src/maps/gml/editor/Tool.java @@ -0,0 +1,22 @@ +package maps.gml.editor; + +/** + Interface for an editing tool. +*/ +public interface Tool { + /** + Get the name of this tool. + @return The name of the tool. + */ + String getName(); + + /** + Activate this tool. + */ + void activate(); + + /** + Deactivate this tool. + */ + void deactivate(); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/editor/ValidateFunction.java b/modules/maps/src/maps/gml/editor/ValidateFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..31ecad85f6c7f9af923baa03559ee8f3686f9007 --- /dev/null +++ b/modules/maps/src/maps/gml/editor/ValidateFunction.java @@ -0,0 +1,109 @@ +package maps.gml.editor; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collection; + +import maps.gml.GMLBuilding; +import maps.gml.GMLEdge; +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLObject; +import maps.gml.GMLRoad; +import maps.gml.GMLSpace; +import maps.gml.view.DecoratorOverlay; +import maps.gml.view.EdgeDecorator; +import maps.gml.view.FilledShapeDecorator; +import maps.gml.view.LineEdgeDecorator; +import maps.gml.view.NodeDecorator; +import maps.gml.view.SquareNodeDecorator; +import maps.validate.GMLMapValidator; +import maps.validate.MapValidator; +import maps.validate.ValidationError; + +import rescuecore2.log.Logger; + +/** + * Check the map for errors and highlight them on the map. + * + */ +public class ValidateFunction extends AbstractFunction { + private static final Color HIGHLIGHT_COLOUR = Color.RED; + private static final int NODE_SIZE = 5; + + private DecoratorOverlay overlay = new DecoratorOverlay(); + + private NodeDecorator nodeHighlight; + private EdgeDecorator edgeHighlight; + private FilledShapeDecorator shapeHighlight; + + /** + * Create a new ValidateFunction. + * @param editor The editor. + */ + public ValidateFunction(GMLEditor editor) { + super(editor); + nodeHighlight = new SquareNodeDecorator(HIGHLIGHT_COLOUR, NODE_SIZE); + edgeHighlight = new LineEdgeDecorator(HIGHLIGHT_COLOUR); + shapeHighlight = new FilledShapeDecorator(HIGHLIGHT_COLOUR, + HIGHLIGHT_COLOUR, HIGHLIGHT_COLOUR); + } + + @Override + public void execute() { + overlay.clearAllDecorators(); + + Collection<ValidationError> allErrors = new ArrayList<ValidationError>(); + for (MapValidator<GMLMap> validator : GMLMapValidator + .getDefaultValidators()) { + Logger.info("Validating " + validator); + Collection<ValidationError> errors = validator.validate(editor + .getMap()); + allErrors.addAll(errors); + + for (ValidationError e : errors) { + System.out.println(e); + addDecorator(e.getId()); + } + } + editor.getInspector().setErrors(allErrors); + + editor.getViewer().removeOverlay(overlay); + editor.getViewer().addOverlay(overlay); + editor.getViewer().repaint(); + + } + + /** + * Add a new error decorator for the object with the given id. + * @param id + */ + private void addDecorator(int id) { + GMLObject obj = editor.getMap().getObject(id); + if (obj == null) { + return; + } + if (obj instanceof GMLBuilding) { + overlay.setBuildingDecorator(shapeHighlight, (GMLBuilding) obj); + } + else if (obj instanceof GMLRoad) { + overlay.setRoadDecorator(shapeHighlight, (GMLRoad) obj); + } + else if (obj instanceof GMLSpace) { + overlay.setSpaceDecorator(shapeHighlight, (GMLSpace) obj); + } + else if (obj instanceof GMLEdge) { + overlay.setEdgeDecorator(edgeHighlight, (GMLEdge) obj); + } + else if (obj instanceof GMLNode) { + overlay.setNodeDecorator(nodeHighlight, (GMLNode) obj); + } + + } + + @Override + public String getName() { + return "Validate map"; + } + +} diff --git a/modules/maps/src/maps/gml/formats/Common.java b/modules/maps/src/maps/gml/formats/Common.java new file mode 100644 index 0000000000000000000000000000000000000000..a4b20a65ed252b7df315cc654dca7e07e5395dfe --- /dev/null +++ b/modules/maps/src/maps/gml/formats/Common.java @@ -0,0 +1,41 @@ +package maps.gml.formats; + +import org.dom4j.DocumentHelper; +import org.dom4j.Namespace; +import org.dom4j.QName; + +/** + A bunch of common GML format namespaces and qnames. +*/ +public final class Common { + // CHECKSTYLE:OFF:JavadocVariable + public static final String GML_NAMESPACE_URI = "http://www.opengis.net/gml"; + public static final String GML_3_2_NAMESPACE_URI = "http://www.opengis.net/gml/3.2"; + public static final String XLINK_NAMESPACE_URI = "http://www.w3.org/1999/xlink"; + + public static final Namespace GML_NAMESPACE = DocumentHelper.createNamespace("gml", GML_NAMESPACE_URI); + public static final Namespace GML_3_2_NAMESPACE = DocumentHelper.createNamespace("gml32", GML_3_2_NAMESPACE_URI); + public static final Namespace XLINK_NAMESPACE = DocumentHelper.createNamespace("xlink", XLINK_NAMESPACE_URI); + + public static final QName GML_ID_QNAME = DocumentHelper.createQName("id", GML_NAMESPACE); + public static final QName GML_NODE_QNAME = DocumentHelper.createQName("Node", GML_NAMESPACE); + public static final QName GML_EDGE_QNAME = DocumentHelper.createQName("Edge", GML_NAMESPACE); + public static final QName GML_FACE_QNAME = DocumentHelper.createQName("Face", GML_NAMESPACE); + public static final QName GML_POINT_PROPERTY_QNAME = DocumentHelper.createQName("pointProperty", GML_NAMESPACE); + public static final QName GML_POINT_QNAME = DocumentHelper.createQName("Point", GML_NAMESPACE); + public static final QName GML_COORDINATES_QNAME = DocumentHelper.createQName("coordinates", GML_NAMESPACE); + public static final QName GML_ORIENTATION_QNAME = DocumentHelper.createQName("orientation"); + public static final QName GML_DIRECTED_NODE_QNAME = DocumentHelper.createQName("directedNode", GML_NAMESPACE); + public static final QName GML_DIRECTED_EDGE_QNAME = DocumentHelper.createQName("directedEdge", GML_NAMESPACE); + public static final QName GML_DIRECTED_FACE_QNAME = DocumentHelper.createQName("directedFace", GML_NAMESPACE); + public static final QName GML_CENTRE_LINE_OF_QNAME = DocumentHelper.createQName("centerLineOf", GML_NAMESPACE); + public static final QName GML_LINE_STRING_QNAME = DocumentHelper.createQName("LineString", GML_NAMESPACE); + public static final QName GML_POLYGON_QNAME = DocumentHelper.createQName("polygon", GML_NAMESPACE); + public static final QName GML_LINEAR_RING_QNAME = DocumentHelper.createQName("LinearRing", GML_NAMESPACE); + + public static final QName XLINK_HREF_QNAME = DocumentHelper.createQName("href", XLINK_NAMESPACE); + // CHECKSTYLE:ON:JavadocVariable + + private Common() { + } +} diff --git a/modules/maps/src/maps/gml/formats/GeospatialInformationAuthorityFormat.java b/modules/maps/src/maps/gml/formats/GeospatialInformationAuthorityFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..56acd0e8b8b531ab0d27016d0f01575635edea7b --- /dev/null +++ b/modules/maps/src/maps/gml/formats/GeospatialInformationAuthorityFormat.java @@ -0,0 +1,166 @@ +package maps.gml.formats; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLMapFormat; +import maps.MapTools; +import maps.CoordinateConversion; +import maps.ScaleConversion; + +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Namespace; +import org.dom4j.QName; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.StringTokenizer; +import java.util.Collections; + +import rescuecore2.log.Logger; + +/** + A MapFormat that can handle maps from Japan's Geospatial Information Authority. + */ +public final class GeospatialInformationAuthorityFormat extends GMLMapFormat { + /** Singleton instance. */ + public static final GeospatialInformationAuthorityFormat INSTANCE = new GeospatialInformationAuthorityFormat(); + + private static final String FGD_NAMESPACE_URI = "http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema"; + + private static final Namespace FGD_NAMESPACE = DocumentHelper.createNamespace("fgd", FGD_NAMESPACE_URI); + + private static final QName DATASET_QNAME = DocumentHelper.createQName("Dataset", FGD_NAMESPACE); + private static final QName BUILDING_QNAME = DocumentHelper.createQName("BldL", FGD_NAMESPACE); + private static final QName ROAD_QNAME = DocumentHelper.createQName("RdEdg", FGD_NAMESPACE); + private static final QName LOC_QNAME = DocumentHelper.createQName("loc", FGD_NAMESPACE); + private static final QName CURVE_QNAME = DocumentHelper.createQName("Curve", Common.GML_3_2_NAMESPACE); + private static final QName SEGMENTS_QNAME = DocumentHelper.createQName("segments", Common.GML_3_2_NAMESPACE); + private static final QName LINE_STRING_SEGMENT_QNAME = DocumentHelper.createQName("LineStringSegment", Common.GML_3_2_NAMESPACE); + private static final QName POS_LIST_QNAME = DocumentHelper.createQName("posList", Common.GML_3_2_NAMESPACE); + + // Map from uri prefix to uri for XPath expressions and for output + private static final Map<String, String> URIS = new HashMap<String, String>(); + + static { + URIS.put("gml", Common.GML_NAMESPACE_URI); + URIS.put("xlink", Common.XLINK_NAMESPACE_URI); + URIS.put("fgd", FGD_NAMESPACE_URI); + } + + private GeospatialInformationAuthorityFormat() { + } + + @Override + public String toString() { + return "Japan Geospatial Information Authority"; + } + + @Override + public Map<String, String> getNamespaces() { + return Collections.unmodifiableMap(URIS); + } + + @Override + public boolean isCorrectRootElement(String uri, String localName) { + return FGD_NAMESPACE_URI.equals(uri) && "Dataset".equals(localName); + } + + @Override + public GMLMap read(Document doc) { + GMLMap result = new GMLMap(); + readBuildings(doc, result); + readRoads(doc, result); + // Convert from lat/lon to metres + double scale = 1.0 / MapTools.sizeOf1Metre((result.getMinY() + result.getMaxY()) / 2, (result.getMinX() + result.getMaxX()) / 2); + CoordinateConversion conversion = new ScaleConversion(result.getMinX(), result.getMinY(), scale, scale); + result.convertCoordinates(conversion); + return result; + } + + @Override + public Document write(GMLMap map) { + // Not implemented + throw new RuntimeException("GeospatialInformationAuthorityFormat.write not implemented"); + } + + private void readBuildings(Document doc, GMLMap result) { + List elements = doc.getRootElement().elements(BUILDING_QNAME); + Logger.debug("Found " + elements.size() + " buildings"); + for (Object next : elements) { + Element e = (Element)next; + try { + Element posList = e.element(LOC_QNAME).element(CURVE_QNAME).element(SEGMENTS_QNAME).element(LINE_STRING_SEGMENT_QNAME).element(POS_LIST_QNAME); + String coords = posList.getText(); + List<GMLDirectedEdge> edges = readEdges(coords, result); + result.createBuilding(edges); + } + catch (NullPointerException ex) { + Logger.debug("Building with wonky outline found: " + ex); + } + } + } + + private void readRoads(Document doc, GMLMap result) { + List elements = doc.getRootElement().elements(ROAD_QNAME); + Logger.debug("Found " + elements.size() + " roads"); + for (Object next : elements) { + Element e = (Element)next; + try { + Element posList = e.element(LOC_QNAME).element(CURVE_QNAME).element(SEGMENTS_QNAME).element(LINE_STRING_SEGMENT_QNAME).element(POS_LIST_QNAME); + String coords = posList.getText(); + createEdges(coords, result); + } + catch (NullPointerException ex) { + Logger.debug("Road with wonky outline found: " + ex); + } + } + } + + private List<GMLDirectedEdge> readEdges(String coordinatesString, GMLMap map) { + List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>(); + StringTokenizer tokens = new StringTokenizer(coordinatesString, " \t\n\r"); + GMLCoordinates lastApex = null; + GMLNode fromNode = null; + GMLNode toNode = null; + while (tokens.hasMoreTokens()) { + String north = tokens.nextToken(); + String east = tokens.nextToken(); + double x = Double.parseDouble(east); + double y = Double.parseDouble(north); + GMLCoordinates nextApex = new GMLCoordinates(x, y); + toNode = map.createNode(nextApex); + if (lastApex != null) { + edges.add(new GMLDirectedEdge(map.createEdge(fromNode, toNode), true)); + } + lastApex = nextApex; + fromNode = toNode; + } + return edges; + } + + private void createEdges(String coordinatesString, GMLMap map) { + StringTokenizer tokens = new StringTokenizer(coordinatesString, " \t\n\r"); + GMLCoordinates lastApex = null; + GMLNode fromNode = null; + GMLNode toNode = null; + while (tokens.hasMoreTokens()) { + String north = tokens.nextToken(); + String east = tokens.nextToken(); + double x = Double.parseDouble(east); + double y = Double.parseDouble(north); + GMLCoordinates nextApex = new GMLCoordinates(x, y); + toNode = map.createNode(nextApex); + if (lastApex != null) { + map.createEdge(fromNode, toNode); + } + lastApex = nextApex; + fromNode = toNode; + } + } +} diff --git a/modules/maps/src/maps/gml/formats/MeijoFormat.java b/modules/maps/src/maps/gml/formats/MeijoFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..de0d73f8c3827d90cee06b9f9267a95695f44cea --- /dev/null +++ b/modules/maps/src/maps/gml/formats/MeijoFormat.java @@ -0,0 +1,418 @@ +package maps.gml.formats; + +import maps.gml.GMLMap; +import maps.gml.GMLCoordinates; +import maps.gml.GMLShape; +import maps.gml.GMLBuilding; +import maps.gml.GMLRoad; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLTools; +import maps.gml.GMLMapFormat; + +import maps.ConstantConversion; + +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.Attribute; +import org.dom4j.QName; +import org.dom4j.Namespace; +import org.dom4j.XPath; +import org.dom4j.DocumentHelper; + +import org.jaxen.SimpleVariableContext; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Iterator; +import java.util.Collections; + +import rescuecore2.misc.Pair; +import rescuecore2.log.Logger; + +/** + A MapFormat that can handle GML maps from Meijo University. + */ +public final class MeijoFormat extends GMLMapFormat { + /** Singleton instance. */ + public static final MeijoFormat INSTANCE = new MeijoFormat(); + + private static final String MEIJO_NAMESPACE_URI = "http://sakura.meijo-u.ac.jp/rcrs"; + private static final String GML_APP_NAMESPACE_URI = "http://www.opengis.net/app"; + private static final Namespace RCRS_NAMESPACE = DocumentHelper.createNamespace("rcrs", MEIJO_NAMESPACE_URI); + private static final Namespace GML_APP_NAMESPACE = DocumentHelper.createNamespace("app", GML_APP_NAMESPACE_URI); + + private static final QName ROOT_QNAME = DocumentHelper.createQName("Topology", GML_APP_NAMESPACE); + private static final QName VERSION_QNAME = DocumentHelper.createQName("Version", RCRS_NAMESPACE); + private static final QName DESCRIPTION_QNAME = DocumentHelper.createQName("Description", RCRS_NAMESPACE); + private static final QName AREA_QNAME = DocumentHelper.createQName("Area", RCRS_NAMESPACE); + private static final QName NODE_LIST_QNAME = DocumentHelper.createQName("NodeList", RCRS_NAMESPACE); + private static final QName EDGE_LIST_QNAME = DocumentHelper.createQName("EdgeList", RCRS_NAMESPACE); + private static final QName FACE_LIST_QNAME = DocumentHelper.createQName("FaceList", RCRS_NAMESPACE); + private static final QName FACE_QNAME = DocumentHelper.createQName("Face", RCRS_NAMESPACE); + private static final QName BUILDING_PROPERTY_QNAME = DocumentHelper.createQName("BuildingProperty", RCRS_NAMESPACE); + + // Map from uri prefix to uri for writing XML documents + private static final Map<String, String> URIS = new HashMap<String, String>(); + + private static final XPath NODE_XPATH = DocumentHelper.createXPath("//app:Topology/rcrs:Area/rcrs:NodeList/gml:Node"); + private static final XPath EDGE_XPATH = DocumentHelper.createXPath("//app:Topology/rcrs:Area/rcrs:EdgeList/gml:Edge"); + private static final XPath FACE_XPATH = DocumentHelper.createXPath("//app:Topology/rcrs:Area/rcrs:FaceList/rcrs:Face"); + + private static final XPath NODE_COORDINATES_XPATH = DocumentHelper.createXPath("gml:pointProperty/gml:Point/gml:coordinates"); + private static final XPath EDGE_COORDINATES_XPATH = DocumentHelper.createXPath("gml:centerLineOf/gml:LineString/gml:coordinates"); + private static final XPath FACE_COORDINATES_XPATH = DocumentHelper.createXPath("gml:polygon/gml:LinearRing/gml:coordinates"); + + private static final XPath EDGE_START_XPATH = DocumentHelper.createXPath("gml:directedNode[1]//@xlink:href"); + private static final XPath EDGE_END_XPATH = DocumentHelper.createXPath("gml:directedNode[2]/@xlink:href"); + + private static final SimpleVariableContext FACE_NEIGHBOUR_XPATH_CONTEXT = new SimpleVariableContext(); + private static final String FACE_NEIGHBOUR_XPATH_STRING = "//rcrs:EdgeList/gml:Edge[@gml:id=\"$edgeid\"]/gml:directedFace[@xlink:href!=\"#$faceid\"]/@xlink:href"; + // private static final XPath FACE_NEIGHBOUR_XPATH = DocumentHelper.createXPath("//rcrs:EdgeList/gml:Edge[@gml:id=\"$edgeid\"]/gml:directedFace[@xlink:href!=\"#$faceid\"]/@xlink:href"); + // private static final XPath FACE_NEIGHBOUR_XPATH = DocumentHelper.createXPath("//rcrs:EdgeList/gml:Edge[@gml:id=\"$edgeid\"]/gml:directedFace", FACE_NEIGHBOUR_XPATH_CONTEXT); + + private static final double THRESHOLD = 0.0001; + + + static { + URIS.put("gml", Common.GML_NAMESPACE_URI); + URIS.put("app", GML_APP_NAMESPACE_URI); + URIS.put("xlink", Common.XLINK_NAMESPACE_URI); + URIS.put("rcrs", MEIJO_NAMESPACE_URI); + + NODE_XPATH.setNamespaceURIs(URIS); + EDGE_XPATH.setNamespaceURIs(URIS); + FACE_XPATH.setNamespaceURIs(URIS); + + NODE_COORDINATES_XPATH.setNamespaceURIs(URIS); + EDGE_COORDINATES_XPATH.setNamespaceURIs(URIS); + FACE_COORDINATES_XPATH.setNamespaceURIs(URIS); + + EDGE_START_XPATH.setNamespaceURIs(URIS); + EDGE_END_XPATH.setNamespaceURIs(URIS); + + // FACE_NEIGHBOUR_XPATH.setNamespaceURIs(URIS); + } + + private MeijoFormat() { + } + + @Override + public Map<String, String> getNamespaces() { + return Collections.unmodifiableMap(URIS); + } + + @Override + public String toString() { + return "Meijo"; + } + + @Override + public boolean isCorrectRootElement(String uri, String localName) { + return MEIJO_NAMESPACE_URI.equals(uri) && "Topology".equals(localName); + } + + @Override + public GMLMap read(Document doc) { + GMLMap result = new GMLMap(); + readNodes(doc, result); + // This format has coordinates in mm, so divide by 1000 to convert to m. + // CHECKSTYLE:OFF:MagicNumber + result.convertCoordinates(new ConstantConversion(0.001)); + // CHECKSTYLE:ON:MagicNumber + readEdges(doc, result); + readFaces(doc, result); + splitMultipleEdges(result); + // checkEdgeOrderAndDirection(result); + return result; + } + + @Override + public Document write(GMLMap map) { + Element root = DocumentHelper.createElement(ROOT_QNAME); + Document result = DocumentHelper.createDocument(root); + writeNodes(map, root.addElement(NODE_LIST_QNAME)); + writeEdges(map, root.addElement(EDGE_LIST_QNAME)); + writeFaces(map, root.addElement(FACE_LIST_QNAME)); + return result; + } + + private void writeNodes(GMLMap map, Element parent) { + for (GMLNode next : map.getNodes()) { + Element e = parent.addElement(Common.GML_NODE_QNAME); + e.addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())); + e.addElement(Common.GML_POINT_PROPERTY_QNAME).addElement(Common.GML_POINT_QNAME).addElement(Common.GML_COORDINATES_QNAME).setText(next.getCoordinates().toString()); + } + } + + private void writeEdges(GMLMap map, Element parent) { + for (GMLEdge next : map.getEdges()) { + Element e = parent.addElement(Common.GML_EDGE_QNAME); + e.addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())); + e.addElement(Common.GML_DIRECTED_NODE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, "+").addAttribute(Common.XLINK_HREF_QNAME, "#" + next.getStart().getID()); + e.addElement(Common.GML_DIRECTED_NODE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, "+").addAttribute(Common.XLINK_HREF_QNAME, "#" + next.getEnd().getID()); + // Add directed faces + // This will be real slow + for (GMLShape shape : map.getAllShapes()) { + for (GMLDirectedEdge edge : shape.getEdges()) { + if (edge.getEdge() == next) { + e.addElement(Common.GML_DIRECTED_FACE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, "+").addAttribute(Common.XLINK_HREF_QNAME, "#" + shape.getID()); + } + } + } + } + } + + private void writeFaces(GMLMap map, Element parent) { + for (GMLShape next : map.getAllShapes()) { + Element e = parent.addElement(FACE_QNAME); + if (next instanceof GMLBuilding) { + parent.addElement(BUILDING_PROPERTY_QNAME); + } + e = e.addElement(Common.GML_FACE_QNAME); + e.addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())); + for (GMLDirectedEdge edge : next.getEdges()) { + String orientation = "-"; + if (edge.getEdge().isPassable()) { + orientation = "+"; + } + e.addElement(Common.GML_DIRECTED_EDGE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, orientation).addAttribute(Common.XLINK_HREF_QNAME, "#" + edge.getEdge().getID()); + } + e.addElement(Common.GML_POLYGON_QNAME).addElement(Common.GML_LINEAR_RING_QNAME).addElement(Common.GML_COORDINATES_QNAME).setText(GMLTools.getCoordinatesString(next.getCoordinates())); + } + } + + private void readNodes(Document doc, GMLMap result) { + for (Object next : NODE_XPATH.selectNodes(doc)) { + Element e = (Element)next; + int id = readID(e); + String coordinates = ((Element)NODE_COORDINATES_XPATH.evaluate(e)).getText(); + GMLCoordinates c = new GMLCoordinates(coordinates); + GMLNode node = new GMLNode(id, c); + result.addNode(node); + Logger.debug("Read node " + node); + } + } + + private void readEdges(Document doc, GMLMap result) { + for (Object next : EDGE_XPATH.selectNodes(doc)) { + Element e = (Element)next; + int id = readID(e); + // Logger.debug("Children: " + e.elements()); + // Logger.debug("Start: " + EDGE_START_XPATH.evaluate(e)); + int startID = Integer.parseInt(((Attribute)EDGE_START_XPATH.evaluate(e)).getValue().substring(1)); + int endID = Integer.parseInt(((Attribute)EDGE_END_XPATH.evaluate(e)).getValue().substring(1)); + GMLEdge edge = new GMLEdge(id, result.getNode(startID), result.getNode(endID), false); + // Read the edge coordinates + edge.setPoints(GMLTools.getCoordinatesList(((Element)EDGE_COORDINATES_XPATH.evaluate(e)).getText())); + result.addEdge(edge); + Logger.debug("Read edge " + edge); + } + } + + private void readFaces(Document doc, GMLMap result) { + // Logger.debug("Reading buildings"); + for (Object next : FACE_XPATH.selectNodes(doc)) { + // Logger.debug("Reading " + next); + Element e = (Element)next; + String type = e.attributeValue("type"); + Element gmlFace = e.element(Common.GML_FACE_QNAME); + int id = readID(gmlFace); + // Logger.debug("ID = " + id); + // Logger.debug("Type = " + type); + Pair<List<GMLDirectedEdge>, List<Integer>> edges = readEdges(gmlFace, result, id); + // Logger.debug("Edges: " + edges); + GMLShape shape = null; + if ("building".equals(type)) { + shape = new GMLBuilding(id, edges.first(), edges.second()); + } + else { + shape = new GMLRoad(id, edges.first(), edges.second()); + } + // Logger.debug("Computing coordinates"); + String coordsString = ((Element)FACE_COORDINATES_XPATH.evaluate(gmlFace)).getText(); + // Logger.debug("coordsString = " + coordsString); + List<GMLCoordinates> coords = GMLTools.getCoordinatesList(coordsString); + // Logger.debug("coords = " + coords); + shape.setCoordinates(coords); + result.add(shape); + Logger.debug("Read shape: " + shape); + } + } + + private Pair<List<GMLDirectedEdge>, List<Integer>> readEdges(Element e, GMLMap map, int faceID) { + List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>(); + List<Integer> neighbours = new ArrayList<Integer>(); + + // Logger.debug("Reading edges for face " + faceID); + for (Object next : e.elements(Common.GML_DIRECTED_EDGE_QNAME)) { + Element dEdge = (Element)next; + boolean passable = "+".equals(dEdge.attributeValue(Common.GML_ORIENTATION_QNAME)); + int edgeID = Integer.parseInt(dEdge.attributeValue(Common.XLINK_HREF_QNAME).substring(1)); + // Logger.debug("Edge ID: " + edgeID); + // Logger.debug("Passable? " + passable); + edges.add(new GMLDirectedEdge(map.getEdge(edgeID), true)); + XPath xpath = makeFaceNeighbourXPath(edgeID, faceID); + // FACE_NEIGHBOUR_XPATH_CONTEXT.setVariableValue("edgeid", String.valueOf(edgeID)); + // FACE_NEIGHBOUR_XPATH_CONTEXT.setVariableValue("faceid", String.valueOf(faceID)); + Object o = xpath.evaluate(e); + // Logger.debug("Neighbours: " + o); + if (o == null) { + neighbours.add(null); + } + else if (o instanceof Collection && ((Collection)o).isEmpty()) { + neighbours.add(null); + } + else { + int neighbourID = Integer.parseInt(((Attribute)o).getValue().substring(1)); + neighbours.add(neighbourID); + } + // Logger.debug("Edge list : " + edges); + // Logger.debug("Neighbour list: " + neighbours); + } + // Logger.debug("Finished reading edges for face " + faceID); + return new Pair<List<GMLDirectedEdge>, List<Integer>>(edges, neighbours); + } + + private int readID(Element e) { + return Integer.parseInt(e.attributeValue(Common.GML_ID_QNAME)); + } + + private XPath makeFaceNeighbourXPath(int edgeID, int faceID) { + String path = FACE_NEIGHBOUR_XPATH_STRING.replace("$edgeid", String.valueOf(edgeID)).replace("$faceid", String.valueOf(faceID)); + // Logger.debug("Neighbour XPath: " + path); + XPath result = DocumentHelper.createXPath(path); + result.setNamespaceURIs(URIS); + return result; + } + + private void splitMultipleEdges(GMLMap map) { + // Look for edges that have more then 2 GMLCoordinates and split them into multiple edges + for (GMLEdge edge : map.getEdges()) { + if (edge.getPoints().size() != 2) { + // Split this edge + Iterator<GMLCoordinates> it = edge.getPoints().iterator(); + GMLCoordinates first = it.next(); + List<GMLEdge> newEdges = new ArrayList<GMLEdge>(); + while (it.hasNext()) { + GMLCoordinates second = it.next(); + GMLNode n1 = map.createNode(first); + GMLNode n2 = map.createNode(second); + GMLEdge newEdge = map.createEdge(n1, n2); + newEdges.add(newEdge); + first = second; + } + // Update any shapes that reference the old edge + for (GMLShape shape : map.getAllShapes()) { + replaceEdge(shape, edge, newEdges); + } + map.removeEdge(edge); + // Logger.debug("Split " + edge); + // Logger.debug("New edges: " + newEdges); + } + } + } + + private void replaceEdge(GMLShape shape, GMLEdge oldEdge, List<GMLEdge> newEdges) { + List<GMLDirectedEdge> newShapeEdges = new ArrayList<GMLDirectedEdge>(); + List<Integer> newShapeNeighbours = new ArrayList<Integer>(); + boolean found = false; + for (GMLDirectedEdge e : shape.getEdges()) { + if (e.getEdge().equals(oldEdge)) { + found = true; + GMLNode start = e.getStartNode(); + Integer neighbour = shape.getNeighbour(e); + for (GMLEdge next : newEdges) { + GMLDirectedEdge newDEdge = new GMLDirectedEdge(next, start); + newShapeEdges.add(newDEdge); + newShapeNeighbours.add(neighbour); + start = newDEdge.getEndNode(); + } + } + else { + newShapeEdges.add(e); + newShapeNeighbours.add(shape.getNeighbour(e)); + } + } + if (found) { + shape.setEdges(newShapeEdges); + Iterator<GMLDirectedEdge> it = newShapeEdges.iterator(); + Iterator<Integer> ix = newShapeNeighbours.iterator(); + while (it.hasNext() && ix.hasNext()) { + shape.setNeighbour(it.next(), ix.next()); + } + } + } + + /* + private void checkEdgeOrderAndDirection(GMLMap map) { + Set<GMLDirectedEdge> remaining = new HashSet<GMLDirectedEdge>(); + List<GMLDirectedEdge> reordered = new ArrayList<GMLDirectedEdge>(); + for (GMLShape shape : map.getAllShapes()) { + remaining.clear(); + reordered.clear(); + remaining.addAll(shape.getEdges()); + // Iterator<GMLDirectedEdge> it = shape.getEdges().iterator(); + GMLDirectedEdge edge = shape.getEdges().get(0); + GMLNode start = edge.getEndNode(); + // Logger.debug("Reordering " + remaining.size() + " edges for " + shape); + // Logger.debug("Original order"); + // for (GMLDirectedEdge e : shape.getEdges()) { + // logEdge(e); + // } + // Logger.debug("First edge"); + // logEdge(edge); + remaining.remove(edge); + reordered.add(edge); + while (!remaining.isEmpty()) { + edge = null; + // Find the next edge + for (GMLDirectedEdge next : remaining) { + if (closeEnough(next.getStartNode(), start)) { + edge = next; + break; + } + if (closeEnough(next.getEndNode(), start)) { + edge = next; + edge.reverse(); + break; + } + } + if (edge == null) { + throw new RuntimeException("Failed to reorder edges: found discontinuity in shape outline"); + } + // Logger.debug("Next edge"); + // logEdge(edge); + remaining.remove(edge); + reordered.add(edge); + start = edge.getEndNode(); + } + // Logger.debug("Reordered"); + // for (GMLDirectedEdge e : reordered) { + // logEdge(e); + // } + shape.reorderEdges(reordered); + } + } + */ + + // private void logEdge(GMLDirectedEdge e) { + // Logger.debug(e.getEdge().getID() + ": " + e.getStartNode().getID() + " -> " + e.getEndNode().getID()); + // } + + private boolean closeEnough(GMLNode n1, GMLNode n2) { + if (n1 == n2) { + return true; + } + double dx = n1.getX() - n2.getX(); + double dy = n1.getY() - n2.getY(); + return (dx > -THRESHOLD + && dx < THRESHOLD + && dy > -THRESHOLD + && dy < THRESHOLD); + } +} diff --git a/modules/maps/src/maps/gml/formats/OrdnanceSurveyFormat.java b/modules/maps/src/maps/gml/formats/OrdnanceSurveyFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..5f2149eec10856c194fa3300568a9b2d45d657d4 --- /dev/null +++ b/modules/maps/src/maps/gml/formats/OrdnanceSurveyFormat.java @@ -0,0 +1,171 @@ +package maps.gml.formats; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLCoordinates; +import maps.gml.GMLBuilding; +import maps.gml.GMLRoad; +import maps.gml.GMLMapFormat; + +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Namespace; +import org.dom4j.QName; +import org.dom4j.XPath; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.StringTokenizer; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.log.Logger; + +// TO DO: Handle inner boundaries + +/** + A MapFormat that can handle maps from the UK Ordnance Survey. + */ +public final class OrdnanceSurveyFormat extends GMLMapFormat { + /** Singleton instance. */ + public static final OrdnanceSurveyFormat INSTANCE = new OrdnanceSurveyFormat(); + + private static final String FEATURE_CODE_BUILDING = "10021"; + private static final String FEATURE_CODE_ROAD = "10172"; + private static final String FEATURE_CODE_FOOTPATH = "10183"; + + private static final String FEATURE_CODE_OPEN_SPACE = "10053"; + private static final String FEATURE_CODE_GENERAL_SPACE = "10056"; + + private static final String OSGB_NAMESPACE_URI = "http://www.ordnancesurvey.co.uk/xml/namespaces/osgb"; + + private static final Namespace OSGB_NAMESPACE = DocumentHelper.createNamespace("osgb", OSGB_NAMESPACE_URI); + + private static final QName FEATURE_COLLECTION_QNAME = DocumentHelper.createQName("FeatureCollection", OSGB_NAMESPACE); + private static final QName TOPOGRAPHIC_AREA_QNAME = DocumentHelper.createQName("TopographicArea", OSGB_NAMESPACE); + + private static final XPath BUILDING_XPATH = DocumentHelper.createXPath("//osgb:topographicMember/osgb:TopographicArea[osgb:featureCode[text()='" + FEATURE_CODE_BUILDING + "']]"); + // private static final XPath ROAD_XPATH = DocumentHelper.createXPath("//osgb:topographicMember/osgb:TopographicArea[osgb:featureCode[text()='" + FEATURE_CODE_ROAD + "' or text()='" + FEATURE_CODE_FOOTPATH + "']]"); + private static final XPath ROAD_XPATH = DocumentHelper.createXPath("//osgb:topographicMember/osgb:TopographicArea[osgb:featureCode[text()='" + FEATURE_CODE_ROAD + "']]"); + private static final XPath SPACE_XPATH = DocumentHelper.createXPath("//osgb:topographicMember/osgb:TopographicArea[osgb:featureCode[text()='" + FEATURE_CODE_OPEN_SPACE + "' or text()='" + FEATURE_CODE_GENERAL_SPACE + "']]"); + private static final XPath SHAPE_XPATH = DocumentHelper.createXPath("osgb:polygon/gml:Polygon/gml:outerBoundaryIs/gml:LinearRing/gml:coordinates"); + private static final XPath INNER_RING_XPATH = DocumentHelper.createXPath("osgb:polygon/gml:Polygon/gml:innerBoundaryIs/gml:LinearRing/gml:coordinates"); + + // Map from uri prefix to uri for XPath expressions + private static final Map<String, String> URIS = new HashMap<String, String>(); + + private static final int FID_PREFIX_LENGTH = 4; + + static { + URIS.put("gml", Common.GML_NAMESPACE_URI); + URIS.put("xlink", Common.XLINK_NAMESPACE_URI); + URIS.put("osgb", OSGB_NAMESPACE_URI); + + BUILDING_XPATH.setNamespaceURIs(URIS); + ROAD_XPATH.setNamespaceURIs(URIS); + SPACE_XPATH.setNamespaceURIs(URIS); + SHAPE_XPATH.setNamespaceURIs(URIS); + INNER_RING_XPATH.setNamespaceURIs(URIS); + } + + private OrdnanceSurveyFormat() { + } + + @Override + public String toString() { + return "Ordnance survey"; + } + + @Override + public Map<String, String> getNamespaces() { + return Collections.unmodifiableMap(URIS); + } + + @Override + public boolean isCorrectRootElement(String uri, String localName) { + return OSGB_NAMESPACE_URI.equals(uri) && "FeatureCollection".equals(localName); + } + + @Override + public GMLMap read(Document doc) { + GMLMap result = new GMLMap(); + readBuildings(doc, result); + readRoads(doc, result); + readSpaces(doc, result); + return result; + } + + @Override + public Document write(GMLMap map) { + // Not implemented + throw new RuntimeException("OrdnanceSurveyFormat.write not implemented"); + } + + private void readBuildings(Document doc, GMLMap result) { + for (Object next : BUILDING_XPATH.selectNodes(doc)) { + Logger.debug("Found building element: " + next); + Element e = (Element)next; + // String fid = e.attributeValue("fid"); + // long id = Long.parseLong(fid.substring(FID_PREFIX_LENGTH)); // Strip off the 'osgb' prefix + String coordinatesString = ((Element)SHAPE_XPATH.evaluate(e)).getText(); + List<GMLDirectedEdge> edges = readEdges(coordinatesString, result); + GMLBuilding b = result.createBuilding(edges); + } + } + + private void readRoads(Document doc, GMLMap result) { + for (Object next : ROAD_XPATH.selectNodes(doc)) { + Logger.debug("Found road element: " + next); + Element e = (Element)next; + // String fid = e.attributeValue("fid"); + // long id = Long.parseLong(fid.substring(FID_PREFIX_LENGTH)); // Strip off the 'osgb' prefix + String coordinatesString = ((Element)SHAPE_XPATH.evaluate(e)).getText(); + Object inner = INNER_RING_XPATH.evaluate(e); + if ((inner instanceof Collection) && ((Collection)inner).isEmpty()) { + List<GMLDirectedEdge> edges = readEdges(coordinatesString, result); + GMLRoad road = result.createRoad(edges); + } + else { + Logger.debug("Inner ring found: ignoring"); + Logger.debug("Found: " + inner); + } + } + } + + private void readSpaces(Document doc, GMLMap result) { + /* + for (Object next : SPACE_XPATH.selectNodes(doc)) { + Logger.debug("Found space element: " + next); + Element e = (Element)next; + String fid = e.attributeValue("fid"); + long id = Long.parseLong(fid.substring(4)); // Strip off the 'osgb' prefix + String coordinatesString = ((Element)SHAPE_XPATH.evaluate(e)).getText(); + List<GMLEdge> edges = readEdges(coordinatesString, result); + result.createSpace(edges); + } + */ + } + + private List<GMLDirectedEdge> readEdges(String coordinatesString, GMLMap map) { + List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>(); + StringTokenizer tokens = new StringTokenizer(coordinatesString, " "); + GMLCoordinates lastApex = null; + GMLNode fromNode = null; + GMLNode toNode = null; + while (tokens.hasMoreTokens()) { + String token = tokens.nextToken(); + GMLCoordinates nextApex = new GMLCoordinates(token); + toNode = map.createNode(nextApex); + if (lastApex != null) { + edges.add(new GMLDirectedEdge(map.createEdge(fromNode, toNode), true)); + } + lastApex = nextApex; + fromNode = toNode; + } + return edges; + } +} diff --git a/modules/maps/src/maps/gml/formats/RobocupFormat.java b/modules/maps/src/maps/gml/formats/RobocupFormat.java new file mode 100755 index 0000000000000000000000000000000000000000..1a4347ac3090442dea2201cf9fed1232ba739345 --- /dev/null +++ b/modules/maps/src/maps/gml/formats/RobocupFormat.java @@ -0,0 +1,367 @@ +package maps.gml.formats; + +import maps.gml.GMLMap; +import maps.gml.GMLCoordinates; +import maps.gml.GMLObject; +import maps.gml.GMLShape; +import maps.gml.GMLBuilding; +import maps.gml.GMLRoad; +import maps.gml.GMLSpace; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLMapFormat; +import maps.MapException; + +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.QName; +import org.dom4j.Namespace; +import org.dom4j.DocumentHelper; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; + +import rescuecore2.misc.Pair; +import rescuecore2.log.Logger; + +/** + A MapFormat that can handle Robocup Rescue GML maps. + */ +public final class RobocupFormat extends GMLMapFormat { + /** Singleton instance. */ + public static final RobocupFormat INSTANCE = new RobocupFormat(); + + private static final String RCR_NAMESPACE_URI = "urn:roborescue:map:gml"; + private static final Namespace RCR_NAMESPACE = DocumentHelper.createNamespace("rcr", RCR_NAMESPACE_URI); + + private static final QName RCR_ROOT_QNAME = DocumentHelper.createQName("map", RCR_NAMESPACE); + private static final QName RCR_NODE_LIST_QNAME = DocumentHelper.createQName("nodelist", RCR_NAMESPACE); + private static final QName RCR_EDGE_LIST_QNAME = DocumentHelper.createQName("edgelist", RCR_NAMESPACE); + private static final QName RCR_BUILDING_LIST_QNAME = DocumentHelper.createQName("buildinglist", RCR_NAMESPACE); + private static final QName RCR_ROAD_LIST_QNAME = DocumentHelper.createQName("roadlist", RCR_NAMESPACE); + private static final QName RCR_SPACE_LIST_QNAME = DocumentHelper.createQName("spacelist", RCR_NAMESPACE); + private static final QName RCR_NODE_QNAME = DocumentHelper.createQName("node", RCR_NAMESPACE); + private static final QName RCR_EDGE_QNAME = DocumentHelper.createQName("edge", RCR_NAMESPACE); + private static final QName RCR_BUILDING_QNAME = DocumentHelper.createQName("building", RCR_NAMESPACE); + private static final QName RCR_ROAD_QNAME = DocumentHelper.createQName("road", RCR_NAMESPACE); + private static final QName RCR_SPACE_QNAME = DocumentHelper.createQName("space", RCR_NAMESPACE); + private static final QName RCR_NEIGHBOUR_QNAME = DocumentHelper.createQName("neighbour", RCR_NAMESPACE); + + private static final QName RCR_FLOORS_QNAME = DocumentHelper.createQName("floors", RCR_NAMESPACE); + private static final QName RCR_BUILDING_CODE_QNAME = DocumentHelper.createQName("buildingcode", RCR_NAMESPACE); + private static final QName RCR_IMPORTANCE_QNAME = DocumentHelper.createQName("importance", RCR_NAMESPACE); + private static final QName RCR_CAPACITY_QNAME = DocumentHelper.createQName("capacity", RCR_NAMESPACE); + + // Map from uri prefix to uri for writing XML documents + private static final Map<String, String> URIS = new HashMap<String, String>(); + + private static final Comparator<GMLObject> ID_SORTER = new Comparator<GMLObject>() { + @Override + public int compare(GMLObject first, GMLObject second) { + if (first.getID() < second.getID()) { + return -1; + } + if (first.getID() > second.getID()) { + return 1; + } + return 0; + } + }; + + static { + URIS.put("gml", Common.GML_NAMESPACE_URI); + URIS.put("xlink", Common.XLINK_NAMESPACE_URI); + URIS.put("rcr", RCR_NAMESPACE_URI); + } + + private RobocupFormat() { + } + + @Override + public Map<String, String> getNamespaces() { + return Collections.unmodifiableMap(URIS); + } + + @Override + public String toString() { + return "Robocup rescue"; + } + + @Override + public boolean isCorrectRootElement(String uri, String localName) { + return RCR_NAMESPACE_URI.equals(uri) && "map".equals(localName); + } + + @Override + public GMLMap read(Document doc) throws MapException { + GMLMap result = new GMLMap(); + readNodes(doc, result); + readEdges(doc, result); + readBuildings(doc, result); + readRoads(doc, result); + readSpaces(doc, result); + return result; + } + + @Override + public Document write(GMLMap map) { + Element root = DocumentHelper.createElement(RCR_ROOT_QNAME); + Document result = DocumentHelper.createDocument(root); + writeNodes(map, root.addElement(RCR_NODE_LIST_QNAME)); + writeEdges(map, root.addElement(RCR_EDGE_LIST_QNAME)); + writeShapes(map.getBuildings(), RCR_BUILDING_QNAME, root.addElement(RCR_BUILDING_LIST_QNAME)); + writeShapes(map.getRoads(), RCR_ROAD_QNAME, root.addElement(RCR_ROAD_LIST_QNAME)); + writeShapes(map.getSpaces(), RCR_SPACE_QNAME, root.addElement(RCR_SPACE_LIST_QNAME)); + return result; + } + + private void writeNodes(GMLMap map, Element parent) { + List<GMLNode> nodes = new ArrayList<GMLNode>(map.getNodes()); + Collections.sort(nodes, ID_SORTER); + for (GMLNode next : nodes) { + Element e = parent.addElement(Common.GML_NODE_QNAME); + e.addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())); + e.addElement(Common.GML_POINT_PROPERTY_QNAME).addElement(Common.GML_POINT_QNAME).addElement(Common.GML_COORDINATES_QNAME).setText(next.getCoordinates().toString()); + } + } + + private void writeEdges(GMLMap map, Element parent) { + List<GMLEdge> edges = new ArrayList<GMLEdge>(map.getEdges()); + Collections.sort(edges, ID_SORTER); + for (GMLEdge next : edges) { + Element e = parent.addElement(Common.GML_EDGE_QNAME); + e.addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())); + e.addElement(Common.GML_DIRECTED_NODE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, "-").addAttribute(Common.XLINK_HREF_QNAME, "#" + next.getStart().getID()); + e.addElement(Common.GML_DIRECTED_NODE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, "+").addAttribute(Common.XLINK_HREF_QNAME, "#" + next.getEnd().getID()); + } + } + + private void writeShapes(Collection<? extends GMLShape> shapes, QName qname, Element parent) { + List<GMLShape> sorted = new ArrayList<GMLShape>(shapes); + Collections.sort(sorted, ID_SORTER); + for (GMLShape next : sorted) { + Element e = parent.addElement(qname).addAttribute(Common.GML_ID_QNAME, String.valueOf(next.getID())).addElement(Common.GML_FACE_QNAME); + for (GMLDirectedEdge dEdge : next.getEdges()) { + String orientation = dEdge.isForward() ? "+" : "-"; + Element dEdgeElement = e.addElement(Common.GML_DIRECTED_EDGE_QNAME).addAttribute(Common.GML_ORIENTATION_QNAME, orientation).addAttribute(Common.XLINK_HREF_QNAME, "#" + dEdge.getEdge().getID()); + Integer neighbour = next.getNeighbour(dEdge); + if (neighbour != null) { + dEdgeElement.addAttribute(RCR_NEIGHBOUR_QNAME, String.valueOf(neighbour)); + } + } + if (next instanceof GMLBuilding) { + GMLBuilding b = (GMLBuilding)next; + e.addAttribute(RCR_FLOORS_QNAME, String.valueOf(b.getFloors())); + e.addAttribute(RCR_BUILDING_CODE_QNAME, String.valueOf(b.getCode())); + e.addAttribute(RCR_IMPORTANCE_QNAME, String.valueOf(b.getImportance())); + //e.addAttribute(RCR__QNAME, String.valueOf(b.getImportance())); + } + } + } + + private void readNodes(Document doc, GMLMap result) throws MapException { + Logger.debug("Reading nodes"); + for (Object next : doc.getRootElement().elements(RCR_NODE_LIST_QNAME)) { + Element nodeList = (Element)next; + for (Object nextNode : nodeList.elements(Common.GML_NODE_QNAME)) { + Element e = (Element)nextNode; + int id = readID(e); + String coordinates = readNodeCoordinates(e); + GMLCoordinates c = new GMLCoordinates(coordinates); + GMLNode node = new GMLNode(id, c); + result.addNode(node); + } + } + Logger.debug("Read " + result.getNodes().size() + " nodes"); + } + + private void readEdges(Document doc, GMLMap result) throws MapException { + Logger.debug("Reading edges"); + for (Object next : doc.getRootElement().elements(RCR_EDGE_LIST_QNAME)) { + Element edgeList = (Element)next; + for (Object nextEdge : edgeList.elements(Common.GML_EDGE_QNAME)) { + Element e = (Element)nextEdge; + int id = readID(e); + int startID = -1; + int endID = -1; + for (Object directedNode : e.elements(Common.GML_DIRECTED_NODE_QNAME)) { + Element directedNodeElement = (Element)directedNode; + if ("-".equals(directedNodeElement.attributeValue(Common.GML_ORIENTATION_QNAME))) { + if (startID != -1) { + throw new MapException("Edge has multiple start nodes: " + e); + } + startID = readHref(directedNodeElement, "start node"); + } + if ("+".equals(directedNodeElement.attributeValue(Common.GML_ORIENTATION_QNAME))) { + if (endID != -1) { + throw new MapException("Edge has multiple end nodes: " + e); + } + endID = readHref(directedNodeElement, "end node"); + } + } + GMLEdge edge = new GMLEdge(id, result.getNode(startID), result.getNode(endID), false); + result.addEdge(edge); + } + } + Logger.debug("Read " + result.getEdges().size() + " edges"); + } + + private void readBuildings(Document doc, GMLMap result) throws MapException { + Logger.debug("Reading buildings"); + for (Object next : doc.getRootElement().elements(RCR_BUILDING_LIST_QNAME)) { + Element buildingList = (Element)next; + for (Object nextBuilding : buildingList.elements(RCR_BUILDING_QNAME)) { + Element e = (Element)nextBuilding; + Pair<List<GMLDirectedEdge>, List<Integer>> edges = readEdges(e, result); + GMLBuilding b = new GMLBuilding(readID(e), edges.first(), edges.second()); + Element f = e.element(Common.GML_FACE_QNAME); + int floors = readInt(f, RCR_FLOORS_QNAME, 1); + int code = readInt(f, RCR_BUILDING_CODE_QNAME, 0); + int importance = readInt(f, RCR_IMPORTANCE_QNAME, 1); + int capacity = readInt(f, RCR_CAPACITY_QNAME, 0); + b.setFloors(floors); + b.setCode(code); + b.setImportance(importance); + b.setCapacity(capacity); + result.addBuilding(b); + } + } + Logger.debug("Read " + result.getBuildings().size() + " buildings"); + } + + private void readRoads(Document doc, GMLMap result) throws MapException { + Logger.debug("Reading roads"); + for (Object next : doc.getRootElement().elements(RCR_ROAD_LIST_QNAME)) { + Element roadList = (Element)next; + for (Object nextRoad : roadList.elements(RCR_ROAD_QNAME)) { + Element e = (Element)nextRoad; + Pair<List<GMLDirectedEdge>, List<Integer>> edges = readEdges(e, result); + GMLRoad r = new GMLRoad(readID(e), edges.first(), edges.second()); + result.addRoad(r); + } + } + Logger.debug("Read " + result.getRoads().size() + " roads"); + } + + private void readSpaces(Document doc, GMLMap result) throws MapException { + Logger.debug("Reading spaces"); + for (Object next : doc.getRootElement().elements(RCR_SPACE_LIST_QNAME)) { + Element spaceList = (Element)next; + for (Object nextSpace : spaceList.elements(RCR_SPACE_QNAME)) { + Element e = (Element)nextSpace; + Pair<List<GMLDirectedEdge>, List<Integer>> edges = readEdges(e, result); + GMLSpace s = new GMLSpace(readID(e), edges.first(), edges.second()); + result.addSpace(s); + } + } + Logger.debug("Read " + result.getSpaces().size() + " spaces"); + } + + private Pair<List<GMLDirectedEdge>, List<Integer>> readEdges(Element e, GMLMap map) throws MapException { + List<GMLDirectedEdge> edges = new ArrayList<GMLDirectedEdge>(); + List<Integer> neighbours = new ArrayList<Integer>(); + Element faceElement = e.element(Common.GML_FACE_QNAME); + if (faceElement == null) { + throw new MapException("Shape does not contain a gml:Face: " + e); + } + for (Object nextEdge : faceElement.elements(Common.GML_DIRECTED_EDGE_QNAME)) { + Element directedEdge = (Element)nextEdge; + // Logger.debug("Next directed edge: " + directedEdge); + int nextID = readHref(directedEdge, "underlying edge"); + String orientation = directedEdge.attributeValue(Common.GML_ORIENTATION_QNAME); + boolean forward; + if (orientation == null) { + throw new MapException("Directed edge has no orientation attribute: " + e); + } + if ("+".equals(orientation)) { + forward = true; + } + else if ("-".equals(orientation)) { + forward = false; + } + else { + throw new MapException("Directed edge has invalid orientation attribute: " + e); + } + GMLEdge edge = map.getEdge(nextID); + GMLDirectedEdge dEdge = new GMLDirectedEdge(edge, forward); + String neighbourString = directedEdge.attributeValue(RCR_NEIGHBOUR_QNAME); + Integer neighbourID = null; + if (neighbourString != null) { + try { + neighbourID = Integer.valueOf(neighbourString); + } + catch (NumberFormatException ex) { + throw new MapException("Directed edge has invalid neighbour: " + e, ex); + } + edge.setPassable(true); + } + edges.add(dEdge); + neighbours.add(neighbourID); + } + if (edges.isEmpty()) { + throw new MapException("Shape contains no edges: " + e); + } + return new Pair<List<GMLDirectedEdge>, List<Integer>>(edges, neighbours); + } + + private int readID(Element e) throws MapException { + String s = e.attributeValue(Common.GML_ID_QNAME); + if (s == null) { + throw new MapException("No ID attribute found: " + e); + } + try { + return Integer.parseInt(s); + } + catch (NumberFormatException ex) { + throw new MapException("Couldn't parse ID attribute", ex); + } + } + + private String readNodeCoordinates(Element node) throws MapException { + Element pointProperty = node.element(Common.GML_POINT_PROPERTY_QNAME); + if (pointProperty == null) { + throw new MapException("Couldn't find gml:pointProperty child of node"); + } + Element point = pointProperty.element(Common.GML_POINT_QNAME); + if (point == null) { + throw new MapException("Couldn't find gml:Point child of node"); + } + Element coords = point.element(Common.GML_COORDINATES_QNAME); + if (coords == null) { + throw new MapException("Couldn't find gml:coordinates child of node"); + } + return coords.getText(); + } + + private int readHref(Element e, String type) throws MapException { + String href = e.attributeValue(Common.XLINK_HREF_QNAME); + if (href == null || href.length() == 0) { + throw new MapException("Edge has no " + type + " ID"); + } + try { + return Integer.parseInt(href.substring(1)); + } + catch (NumberFormatException ex) { + throw new MapException("Edge has invalid " + type + " ID"); + } + } + + private int readInt(Element e, QName attributeName, int defaultValue) throws MapException { + String s = e.attributeValue(attributeName); + if (s == null) { + return defaultValue; + } + try { + return Integer.parseInt(s); + } + catch (NumberFormatException ex) { + throw new MapException("Attribute " + attributeName + " is not an integer: " + e); + } + } +} diff --git a/modules/maps/src/maps/gml/generator/GMLMapGenerator.java b/modules/maps/src/maps/gml/generator/GMLMapGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..4c45ae8b5037f192a2b593b8115974502e03b568 --- /dev/null +++ b/modules/maps/src/maps/gml/generator/GMLMapGenerator.java @@ -0,0 +1,62 @@ +package maps.gml.generator; + +import maps.gml.GMLMap; +import maps.gml.formats.RobocupFormat; +import maps.MapWriter; +import maps.MapException; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.log.Logger; + +import java.io.File; + +/** + A tool for generating GML maps. +*/ +public class GMLMapGenerator { + private static final String OUTPUT_FILE_KEY = "generator.output"; + + private Config config; + + /** + Construct a GMLMapGenerator. + @param config The configuration to use. + */ + public GMLMapGenerator(Config config) { + this.config = config; + } + + /** + Entry point. + @param args Command line arguments. + */ + public static void main(String[] args) { + try { + Config config = new Config(); + for (int i = 0; i < args.length; ++i) { + config.read(new File(args[i])); + } + GMLMap map = new GMLMapGenerator(config).generateMap(); + String outFile = config.getValue(OUTPUT_FILE_KEY); + Logger.debug("Writing generated map to " + outFile); + MapWriter.writeMap(map, outFile, RobocupFormat.INSTANCE); + } + catch (MapException e) { + e.printStackTrace(); + } + catch (ConfigException e) { + e.printStackTrace(); + } + } + + /** + Generate a new map. + @return The new map. + */ + public GMLMap generateMap() { + GMLMap result = new GMLMap(); + new ManhattanGenerator(config).populate(result); + return result; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/generator/ManhattanGenerator.java b/modules/maps/src/maps/gml/generator/ManhattanGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..98f2a926a2b64ba847bc72c4069e0b18e30e9022 --- /dev/null +++ b/modules/maps/src/maps/gml/generator/ManhattanGenerator.java @@ -0,0 +1,207 @@ +package maps.gml.generator; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +//import maps.gml.GMLEdge; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLBuilding; +//import maps.gml.GMLRoad; +import maps.gml.GMLCoordinates; + +import rescuecore2.config.Config; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.log.Logger; + +import org.uncommons.maths.number.NumberGenerator; +import org.uncommons.maths.random.Probability; +import org.uncommons.maths.random.ContinuousUniformGenerator; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; + +/** + A MapGenerator that generates a grid world. + */ +public class ManhattanGenerator implements MapGenerator { + private static final String GRID_WIDTH_KEY = "generator.manhattan.grid.width"; + private static final String GRID_HEIGHT_KEY = "generator.manhattan.grid.height"; + private static final String GRID_SIZE_KEY = "generator.manhattan.grid.size"; + private static final String ROAD_WIDTH_KEY = "generator.manhattan.road.width"; + private static final String BUILDING_WIDTH_MIN_KEY = "generator.manhattan.building.width.min"; + private static final String BUILDING_HEIGHT_MIN_KEY = "generator.manhattan.building.height.min"; + private static final String BUILDING_SEPARATION_MIN_KEY = "generator.manhattan.building.separation.min"; + private static final String BUILDING_SEPARATION_MAX_KEY = "generator.manhattan.building.separation.max"; + private static final String BUILDING_MIN_SIZE_KEY = "generator.manhattan.building.split.min-size"; + private static final String BUILDING_MAX_SIZE_KEY = "generator.manhattan.building.split.max-size"; + private static final String BUILDING_SPLIT_CHANCE_KEY = "generator.manhattan.building.split.chance"; + + private Config config; + // private NumberGenerator<Double> widthGenerator; + // private NumberGenerator<Double> heightGenerator; + private NumberGenerator<Double> separationGenerator; + private Probability split; + private double minSize; + private double maxSize; + private double minWidth; + private double minHeight; + + private GMLMap map; + + /** + Construct a ManhattanGenerator. + @param config The configuration to use. + */ + public ManhattanGenerator(Config config) { + this.config = config; + // widthGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_WIDTH_MIN_KEY), config.getFloatValue(BUILDING_WIDTH_MAX_KEY), config.getRandom()); + // heightGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_HEIGHT_MIN_KEY), config.getFloatValue(BUILDING_HEIGHT_MAX_KEY), config.getRandom()); + // Logger.debug("separation min: " + config.getFloatValue(BUILDING_SEPARATION_MIN_KEY)); + // Logger.debug("separation max: " + config.getFloatValue(BUILDING_SEPARATION_MAX_KEY)); + separationGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_SEPARATION_MIN_KEY), config.getFloatValue(BUILDING_SEPARATION_MAX_KEY), config.getRandom()); + // Logger.debug("Generator: "+ separationGenerator); + split = new Probability(config.getFloatValue(BUILDING_SPLIT_CHANCE_KEY)); + minSize = config.getFloatValue(BUILDING_MIN_SIZE_KEY); + maxSize = config.getFloatValue(BUILDING_MAX_SIZE_KEY); + minWidth = config.getFloatValue(BUILDING_WIDTH_MIN_KEY); + minHeight = config.getFloatValue(BUILDING_HEIGHT_MIN_KEY); + } + + @Override + public void populate(GMLMap gmlMap) { + this.map = gmlMap; + int gridWidth = config.getIntValue(GRID_WIDTH_KEY); + int gridHeight = config.getIntValue(GRID_HEIGHT_KEY); + double gridSize = config.getIntValue(GRID_SIZE_KEY); + double roadWidth = config.getIntValue(ROAD_WIDTH_KEY); + Logger.debug("Generating manhattan map: grid size " + gridWidth + " x " + gridHeight); + Logger.debug("Grid cell size: " + gridSize + "m"); + Logger.debug("Road width: " + roadWidth + "m"); + Collection<GMLBuilding> allBuildings = new ArrayList<GMLBuilding>(); + for (int gridX = 0; gridX < gridWidth; ++gridX) { + for (int gridY = 0; gridY < gridHeight; ++gridY) { + double cellXMin = (gridX * gridSize) + roadWidth; + double cellYMin = (gridY * gridSize) + roadWidth; + double cellXMax = ((gridX + 1) * gridSize) - roadWidth; + double cellYMax = ((gridY + 1) * gridSize) - roadWidth; + GMLBuilding base = createBuilding(cellXMin, cellYMin, cellXMax, cellYMax); + allBuildings.addAll(divide(base)); + } + } + map.removeAllNodes(); + map.removeAllEdges(); + map.removeAllBuildings(); + for (GMLBuilding next : allBuildings) { + map.add(next); + for (GMLDirectedEdge edge : next.getEdges()) { + map.add(edge.getEdge()); + map.add(edge.getEdge().getStart()); + map.add(edge.getEdge().getEnd()); + } + } + } + + private Collection<GMLBuilding> divide(GMLBuilding b) { + // Logger.debug("Possibly dividing building " + b); + Collection<GMLBuilding> result = new HashSet<GMLBuilding>(); + List<Point2D> vertices = coordinatesToVertices(b.getUnderlyingCoordinates()); + double area = GeometryTools2D.computeArea(vertices); + // Logger.debug("Area: " + area + " sqm"); + if (area <= minSize) { + result.add(b); + } + else { + if (area > maxSize || split.nextEvent(config.getRandom())) { + // Split the building + double xMin = b.getBounds().getMinX(); + double xMax = b.getBounds().getMaxX(); + double yMin = b.getBounds().getMinY(); + double yMax = b.getBounds().getMaxY(); + double width = xMax - xMin; + double height = yMax - yMin; + // Logger.debug("Width: " + width); + // Logger.debug("Height: " + height); + // Logger.debug("width * height = " + (width * height)); + // Logger.debug("Area = " + area); + if (height > width) { + // Logger.debug("Splitting horizontally"); + // Horizontal split + double splitY = (yMax + yMin) / 2; + double topOffset = separationGenerator.nextValue(); + double bottomOffset = separationGenerator.nextValue(); + double topY = splitY + topOffset; + double bottomY = splitY - bottomOffset; + // Logger.debug("yMin = " + yMin); + // Logger.debug("yMax = " + yMax); + // Logger.debug("splitY = " + splitY); + // Logger.debug("topOffset = " + topOffset); + // Logger.debug("bottomOffset = " + bottomOffset); + // Logger.debug("topY = " + topY); + // Logger.debug("bottomY = " + bottomY); + if (yMax - topY < minHeight || bottomY - yMin < minHeight) { + // Logger.debug("Split too thin"); + // Logger.debug("Top piece: " + (yMax - topY)); + // Logger.debug("Bottom piece: " + (bottomY - yMin)); + // Logger.debug("Minimum height: " + minHeight); + result.add(b); + } + else { + result.addAll(divide(createBuilding(xMin, yMin, xMax, bottomY))); + result.addAll(divide(createBuilding(xMin, topY, xMax, yMax))); + } + } + else { + // Logger.debug("Splitting vertically"); + // Vertical split + double splitX = (xMax + xMin) / 2; + double leftOffset = separationGenerator.nextValue(); + double rightOffset = separationGenerator.nextValue(); + double leftX = splitX - leftOffset; + double rightX = splitX + rightOffset; + // Logger.debug("xMin = " + xMin); + // Logger.debug("xMax = " + xMax); + // Logger.debug("splitX = " + splitX); + // Logger.debug("leftOffset = " + leftOffset); + // Logger.debug("rightOffset = " + rightOffset); + // Logger.debug("leftX = " + leftX); + // Logger.debug("rightX = " + rightX); + if (xMax - rightX < minWidth || leftX - xMin < minWidth) { + // Logger.debug("Split too thin"); + // Logger.debug("Left piece: " + (leftX - xMin)); + // Logger.debug("Right piece: " + (xMax - rightX)); + // Logger.debug("Minimum width: " + minWidth); + result.add(b); + } + else { + result.addAll(divide(createBuilding(xMin, yMin, leftX, yMax))); + result.addAll(divide(createBuilding(rightX, yMin, xMax, yMax))); + } + } + } + else { + // Logger.debug("Not splitting"); + result.add(b); + } + } + return result; + } + + private List<Point2D> coordinatesToVertices(List<GMLCoordinates> coords) { + List<Point2D> result = new ArrayList<Point2D>(coords.size()); + for (GMLCoordinates c : coords) { + result.add(new Point2D(c.getX(), c.getY())); + } + return result; + } + + private GMLBuilding createBuilding(double xMin, double yMin, double xMax, double yMax) { + List<GMLNode> nodes = new ArrayList<GMLNode>(); + nodes.add(map.createNode(xMin, yMin)); + nodes.add(map.createNode(xMax, yMin)); + nodes.add(map.createNode(xMax, yMax)); + nodes.add(map.createNode(xMin, yMax)); + return map.createBuildingFromNodes(nodes); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/generator/MapGenerator.java b/modules/maps/src/maps/gml/generator/MapGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..3a06e4247afdaf1809b975c4aad80436cfc73818 --- /dev/null +++ b/modules/maps/src/maps/gml/generator/MapGenerator.java @@ -0,0 +1,14 @@ +package maps.gml.generator; + +import maps.gml.GMLMap; + +/** + Top-level interface for map generation strategies. + */ +public interface MapGenerator { + /** + Generate a map. + @param map The map object to populate. + */ + void populate(GMLMap map); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/BuildingDecorator b/modules/maps/src/maps/gml/view/BuildingDecorator new file mode 100644 index 0000000000000000000000000000000000000000..2189ba2f03929400e6b47015ec9b5858eb05a16e --- /dev/null +++ b/modules/maps/src/maps/gml/view/BuildingDecorator @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLBuilding; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLBuildings. +*/ +public interface BuildingDecorator { + /** + Decorate a GMLBuilding. + @param building The building to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLBuilding building, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/BuildingDecorator.java b/modules/maps/src/maps/gml/view/BuildingDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..2189ba2f03929400e6b47015ec9b5858eb05a16e --- /dev/null +++ b/modules/maps/src/maps/gml/view/BuildingDecorator.java @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLBuilding; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLBuildings. +*/ +public interface BuildingDecorator { + /** + Decorate a GMLBuilding. + @param building The building to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLBuilding building, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/CrossNodeDecorator.java b/modules/maps/src/maps/gml/view/CrossNodeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..0f292c7b485ea7220645d2ebf13c3c2dcf8c3d2f --- /dev/null +++ b/modules/maps/src/maps/gml/view/CrossNodeDecorator.java @@ -0,0 +1,33 @@ +package maps.gml.view; + +import maps.gml.GMLNode; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; +import java.awt.Color; + +/** + A NodeDecorator that draws a cross for each node. +*/ +public class CrossNodeDecorator implements NodeDecorator { + private Color colour; + private int size; + + /** + Construct a CrossNodeDecorator. + @param colour The colour to draw the cross. + @param size The size of each arm of the cross. + */ + public CrossNodeDecorator(Color colour, int size) { + this.colour = colour; + this.size = size; + } + + @Override + public void decorate(GMLNode node, Graphics2D g, ScreenTransform transform) { + int x = transform.xToScreen(node.getX()); + int y = transform.yToScreen(node.getY()); + g.setColor(colour); + g.drawLine(x - size, y - size, x + size, y + size); + g.drawLine(x - size, y + size, x + size, y - size); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/DecoratorOverlay.java b/modules/maps/src/maps/gml/view/DecoratorOverlay.java new file mode 100755 index 0000000000000000000000000000000000000000..19ff1a464e0d331c21140834e3ff010d4d22100d --- /dev/null +++ b/modules/maps/src/maps/gml/view/DecoratorOverlay.java @@ -0,0 +1,467 @@ +package maps.gml.view; + +import maps.gml.GMLBuilding; +import maps.gml.GMLEdge; +import maps.gml.GMLNode; +import maps.gml.GMLRoad; +import maps.gml.GMLSpace; +import maps.gml.GMLRefuge; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; +import java.awt.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Draws an overlay consisting of Decorators. + */ +public class DecoratorOverlay implements Overlay { + + private transient Map<GMLNode, NodeDecorator> nodeDecorators; + private transient Map<GMLEdge, EdgeDecorator> edgeDecorators; + private transient Map<GMLBuilding, BuildingDecorator> buildingDecorators; + private transient Map<GMLRoad, RoadDecorator> roadDecorators; + private transient Map<GMLSpace, SpaceDecorator> spaceDecorators; + + + /** + * Construct a DecoratorOverlay. + */ + public DecoratorOverlay() { + nodeDecorators = new HashMap<GMLNode, NodeDecorator>(); + edgeDecorators = new HashMap<GMLEdge, EdgeDecorator>(); + buildingDecorators = new HashMap<GMLBuilding, BuildingDecorator>(); + roadDecorators = new HashMap<GMLRoad, RoadDecorator>(); + spaceDecorators = new HashMap<GMLSpace, SpaceDecorator>(); + } + + + /** + * Set the NodeDecorator for a set of GMLNodes. + * + * @param decorator + * The decorator to set. + * @param nodes + * The nodes to set the decorator for. + */ + public void setNodeDecorator( NodeDecorator decorator, GMLNode... nodes ) { + setNodeDecorator( decorator, Arrays.asList( nodes ) ); + } + + + /** + * Set the NodeDecorator for a set of GMLNodes. + * + * @param decorator + * The decorator to set. + * @param nodes + * The nodes to set the decorator for. + */ + public void setNodeDecorator( NodeDecorator decorator, + Collection<? extends GMLNode> nodes ) { + for ( GMLNode next : nodes ) { + nodeDecorators.put( next, decorator ); + } + } + + + /** + * Get the NodeDecorator for a GMLNodes. + * + * @param node + * The node to look up. + * @return The NodeDecorator for that node. This will be null if no custom + * decorator has been set for that node. + */ + public NodeDecorator getNodeDecorator( GMLNode node ) { + NodeDecorator result = nodeDecorators.get( node ); + return result; + } + + + /** + * Remove any custom NodeDecorator for a set of GMLNodes. + * + * @param nodes + * The nodes to remove any custom decorator for. + */ + public void clearNodeDecorator( GMLNode... nodes ) { + clearNodeDecorator( Arrays.asList( nodes ) ); + } + + + /** + * Remove any custom NodeDecorator for a set of GMLNodes. + * + * @param nodes + * The nodes to remove any custom decorator for. + */ + public void clearNodeDecorator( Collection<? extends GMLNode> nodes ) { + for ( GMLNode next : nodes ) { + nodeDecorators.remove( next ); + } + } + + + /** + * Remove any custom NodeDecorators. + */ + public void clearAllNodeDecorators() { + nodeDecorators.clear(); + } + + + /** + * Set the EdgeDecorator for a set of GMLEdges. + * + * @param decorator + * The decorator to set. + * @param edges + * The edges to set the decorator for. + */ + public void setEdgeDecorator( EdgeDecorator decorator, GMLEdge... edges ) { + setEdgeDecorator( decorator, Arrays.asList( edges ) ); + } + + + /** + * Set the EdgeDecorator for a set of GMLEdges. + * + * @param decorator + * The decorator to set. + * @param edges + * The edges to set the decorator for. + */ + public void setEdgeDecorator( EdgeDecorator decorator, + Collection<? extends GMLEdge> edges ) { + for ( GMLEdge next : edges ) { + edgeDecorators.put( next, decorator ); + } + } + + + /** + * Get the EdgeDecorator for a GMLEdge. + * + * @param edge + * The edge to look up. + * @return The EdgeDecorator for that edge. This will be null if no custom + * decorator has been set for that edge. + */ + public EdgeDecorator getEdgeDecorator( GMLEdge edge ) { + EdgeDecorator result = edgeDecorators.get( edge ); + return result; + } + + + /** + * Remove any custom EdgeDecorator for a set of GMLEdges. + * + * @param edges + * The edges to remove any custom decorator for. + */ + public void clearEdgeDecorator( GMLEdge... edges ) { + clearEdgeDecorator( Arrays.asList( edges ) ); + } + + + /** + * Remove any custom EdgeDecorator for a set of GMLEdges. + * + * @param edges + * The edges to remove any custom decorator for. + */ + public void clearEdgeDecorator( Collection<? extends GMLEdge> edges ) { + for ( GMLEdge next : edges ) { + edgeDecorators.remove( next ); + } + } + + + /** + * Remove any custom EdgeDecorators. + */ + public void clearAllEdgeDecorators() { + edgeDecorators.clear(); + } + + + /** + * Set the BuildingDecorator for a set of GMLBuildings. + * + * @param decorator + * The decorator to set. + * @param buildings + * The buildings to set the decorator for. + */ + public void setBuildingDecorator( BuildingDecorator decorator, + GMLBuilding... buildings ) { + setBuildingDecorator( decorator, Arrays.asList( buildings ) ); + } + + + /** + * Set the BuildingDecorator for a set of GMLBuildings. + * + * @param decorator + * The decorator to set. + * @param buildings + * The buildings to set the decorator for. + */ + public void setBuildingDecorator( BuildingDecorator decorator, + Collection<? extends GMLBuilding> buildings ) { + for ( GMLBuilding next : buildings ) { + buildingDecorators.put( next, decorator ); + } + } + + + /** + * Get the BuildingDecorator for a GMLBuildings. + * + * @param building + * The building to look up. + * @return The BuildingDecorator for that building. This will be null if no + * custom decorator has been set for that building. + */ + public BuildingDecorator getBuildingDecorator( GMLBuilding building ) { + BuildingDecorator result = buildingDecorators.get( building ); + return result; + } + + + /** + * Remove any custom BuildingDecorator for a set of GMLBuildings. + * + * @param buildings + * The buildings to remove any custom decorator for. + */ + public void clearBuildingDecorator( GMLBuilding... buildings ) { + clearBuildingDecorator( Arrays.asList( buildings ) ); + } + + + /** + * Remove any custom BuildingDecorator for a set of GMLBuildings. + * + * @param buildings + * The buildings to remove any custom decorator for. + */ + public void + clearBuildingDecorator( Collection<? extends GMLBuilding> buildings ) { + for ( GMLBuilding next : buildings ) { + buildingDecorators.remove( next ); + } + } + + + /** + * Remove any custom BuildingDecorators. + */ + public void clearAllBuildingDecorators() { + buildingDecorators.clear(); + } + + + /** + * Set the RoadDecorator for a set of GMLRoads. + * + * @param decorator + * The decorator to set. + * @param roads + * The roads to set the decorator for. + */ + public void setRoadDecorator( RoadDecorator decorator, GMLRoad... roads ) { + setRoadDecorator( decorator, Arrays.asList( roads ) ); + } + + + /** + * Set the RoadDecorator for a set of GMLRoads. + * + * @param decorator + * The decorator to set. + * @param roads + * The roads to set the decorator for. + */ + public void setRoadDecorator( RoadDecorator decorator, + Collection<? extends GMLRoad> roads ) { + for ( GMLRoad next : roads ) { + roadDecorators.put( next, decorator ); + } + } + + + /** + * Get the RoadDecorator for a GMLRoads. + * + * @param road + * The road to look up. + * @return The RoadDecorator for that road. Will return null if no custom + * decorator has been set for that road. + */ + public RoadDecorator getRoadDecorator( GMLRoad road ) { + RoadDecorator result = roadDecorators.get( road ); + return result; + } + + + /** + * Remove any custom RoadDecorator for a set of GMLRoads. + * + * @param roads + * The roads to remove any custom decorator for. + */ + public void clearRoadDecorator( GMLRoad... roads ) { + clearRoadDecorator( Arrays.asList( roads ) ); + } + + + /** + * Remove any custom RoadDecorator for a set of GMLRoads. + * + * @param roads + * The roads to remove any custom decorator for. + */ + public void clearRoadDecorator( Collection<? extends GMLRoad> roads ) { + for ( GMLRoad next : roads ) { + roadDecorators.remove( next ); + } + } + + + /** + * Remove any custom RoadDecorators. + */ + public void clearAllRoadDecorators() { + roadDecorators.clear(); + } + + + /** + * Set the SpaceDecorator for a set of GMLSpaces. + * + * @param decorator + * The decorator to set. + * @param spaces + * The spaces to set the decorator for. + */ + public void setSpaceDecorator( SpaceDecorator decorator, + GMLSpace... spaces ) { + setSpaceDecorator( decorator, Arrays.asList( spaces ) ); + } + + + /** + * Set the SpaceDecorator for a set of GMLSpaces. + * + * @param decorator + * The decorator to set. + * @param spaces + * The spaces to set the decorator for. + */ + public void setSpaceDecorator( SpaceDecorator decorator, + Collection<? extends GMLSpace> spaces ) { + for ( GMLSpace next : spaces ) { + spaceDecorators.put( next, decorator ); + } + } + + + /** + * Get the SpaceDecorator for a GMLSpaces. + * + * @param space + * The space to look up. + * @return The SpaceDecorator for that space. This will be null if no custom + * decorator has been set for that space. + */ + public SpaceDecorator getSpaceDecorator( GMLSpace space ) { + SpaceDecorator result = spaceDecorators.get( space ); + return result; + } + + + /** + * Remove any custom SpaceDecorator for a set of GMLSpaces. + * + * @param spaces + * The spaces to remove any custom decorator for. + */ + public void clearSpaceDecorator( GMLSpace... spaces ) { + clearSpaceDecorator( Arrays.asList( spaces ) ); + } + + + /** + * Remove any custom SpaceDecorator for a set of GMLSpaces. + * + * @param spaces + * The spaces to remove any custom decorator for. + */ + public void clearSpaceDecorator( Collection<? extends GMLSpace> spaces ) { + for ( GMLSpace next : spaces ) { + spaceDecorators.remove( next ); + } + } + + + /** + * Remove any custom SpaceDecorators. + */ + public void clearAllSpaceDecorators() { + spaceDecorators.clear(); + } + + + /** + * Remove all types of Decorators. + */ + public void clearAllDecorators() { + clearAllBuildingDecorators(); + clearAllRoadDecorators(); + clearAllSpaceDecorators(); + clearAllEdgeDecorators(); + clearAllNodeDecorators(); + } + + + @Override + public void render( Graphics2D g, ScreenTransform transform ) { + for ( Entry<GMLRoad, RoadDecorator> e : roadDecorators.entrySet() ) { + e.getValue().decorate( e.getKey(), (Graphics2D) g.create(), transform ); + } + for ( Entry<GMLBuilding, BuildingDecorator> e : buildingDecorators + .entrySet() ) { + e.getValue().decorate( e.getKey(), (Graphics2D) g.create(), transform ); + + if ( e.getKey() instanceof GMLRefuge ) { + int x = transform.xToScreen( e.getKey().getCentreX() ); + int y = transform.yToScreen( e.getKey().getCentreY() ); + Graphics2D oldg = g; + g.setColor( new Color( 0, 0, 0 ) ); + g.setFont( new Font( g.getFont().getName(), Font.BOLD, + g.getFont().getSize() ) ); + g.drawString( + String + .valueOf( "C=" + ( (GMLRefuge) e.getKey() ).getBedCapacity() ), + x - 20, y ); + // g.drawString(String.valueOf("R " + + // ((GMLRefuge)e.getKey()).getRefillCapacity()), x - 10, y + 10); + g = oldg; + } + } + for ( Entry<GMLSpace, SpaceDecorator> e : spaceDecorators.entrySet() ) { + e.getValue().decorate( e.getKey(), (Graphics2D) g.create(), transform ); + } + for ( Entry<GMLEdge, EdgeDecorator> e : edgeDecorators.entrySet() ) { + e.getValue().decorate( e.getKey(), (Graphics2D) g.create(), transform ); + } + for ( Entry<GMLNode, NodeDecorator> e : nodeDecorators.entrySet() ) { + e.getValue().decorate( e.getKey(), (Graphics2D) g.create(), transform ); + } + } +} diff --git a/modules/maps/src/maps/gml/view/EdgeDecorator.java b/modules/maps/src/maps/gml/view/EdgeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..a4297c633866f85731db9c0fdb539f86e7a0a4d5 --- /dev/null +++ b/modules/maps/src/maps/gml/view/EdgeDecorator.java @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLEdge; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLEdges. +*/ +public interface EdgeDecorator { + /** + Decorate a GMLEdge. + @param edge The edge to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLEdge edge, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/FilledShapeDecorator.java b/modules/maps/src/maps/gml/view/FilledShapeDecorator.java new file mode 100755 index 0000000000000000000000000000000000000000..6d0c170d8a9df3cf9c59a6d98bcce8822b215fe6 --- /dev/null +++ b/modules/maps/src/maps/gml/view/FilledShapeDecorator.java @@ -0,0 +1,80 @@ +package maps.gml.view; + +import maps.gml.GMLShape; +import maps.gml.GMLRoad; +import maps.gml.GMLBuilding; +import maps.gml.GMLSpace; +import maps.gml.GMLCoordinates; + +import rescuecore2.misc.gui.ScreenTransform; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Polygon; +import java.util.List; + +/** + This class knows how to decorate buildings, roads and spaces. +*/ +public class FilledShapeDecorator implements BuildingDecorator, RoadDecorator, SpaceDecorator { + private Color buildingColour; + private Color roadColour; + private Color spaceColour; + + /** + Construct a FilledShapeDecorator. + @param buildingColour The colour of buildings. + @param roadColour The colour of roads. + @param spaceColour The colour of spaces. + */ + public FilledShapeDecorator(Color buildingColour, Color roadColour, Color spaceColour) { + this.buildingColour = buildingColour; + this.roadColour = roadColour; + this.spaceColour = spaceColour; + } + + @Override + public void decorate(GMLBuilding building, Graphics2D g, ScreenTransform transform) { + if (buildingColour == null) { + return; + } + g.setColor(buildingColour); + draw(building, g, transform); + } + + @Override + public void decorate(GMLRoad road, Graphics2D g, ScreenTransform transform) { + if (roadColour == null) { + return; + } + g.setColor(roadColour); + draw(road, g, transform); + } + + @Override + public void decorate(GMLSpace space, Graphics2D g, ScreenTransform transform) { + if (spaceColour == null) { + return; + } + g.setColor(spaceColour); + draw(space, g, transform); + } + + private void draw(GMLShape shape, Graphics2D g, ScreenTransform transform) { + List<GMLCoordinates> coords = shape.getUnderlyingCoordinates(); + int n = coords.size(); + int[] xs = new int[n]; + int[] ys = new int[n]; + int i = 0; + for (GMLCoordinates next : coords) { + xs[i] = transform.xToScreen(next.getX()); + ys[i] = transform.yToScreen(next.getY()); + ++i; + } + g.fill(new Polygon(xs, ys, n)); + } + + public Color getBuildingColour() { + return buildingColour; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/GMLMapViewer.java b/modules/maps/src/maps/gml/view/GMLMapViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..46ee3f1e06a3019218df04399d82ac6e95962f5e --- /dev/null +++ b/modules/maps/src/maps/gml/view/GMLMapViewer.java @@ -0,0 +1,697 @@ +package maps.gml.view; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Insets; +import java.awt.Point; +import java.awt.geom.Rectangle2D; +import javax.swing.JComponent; + +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.PanZoomListener; + +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLEdge; +import maps.gml.GMLBuilding; +import maps.gml.GMLRoad; +import maps.gml.GMLSpace; +import maps.gml.GMLObject; +import maps.gml.GMLCoordinates; +import maps.gml.GMLTools; + +/** + A component for viewing GML maps. +*/ +public class GMLMapViewer extends JComponent { + private static final Color BUILDING_COLOUR = new Color(67, 67, 67, 67); // Transparent dark gray + private static final Color ROAD_COLOUR = new Color(192, 192, 192, 128); // Transparent light gray + private static final Color SPACE_COLOUR = new Color(0, 128, 0, 128); // Transparent green + + private static final Color GRID_COLOUR = new Color(0, 255, 0, 128); // Transparent lime + + private static final Color NODE_COLOUR = Color.BLACK; + private static final int NODE_SIZE = 3; + + private static final Color EDGE_COLOUR = Color.BLACK; + + private static final double MIN_ZOOM_BOUNDS_SIZE = 0.1; + + private GMLMap map; + private ScreenTransform transform; + private PanZoomListener panZoom; + + private transient NodeDecorator defaultNodeDecorator; + private transient Map<GMLNode, NodeDecorator> nodeDecorators; + + private transient EdgeDecorator defaultEdgeDecorator; + private transient Map<GMLEdge, EdgeDecorator> edgeDecorators; + + private transient BuildingDecorator defaultBuildingDecorator; + private transient Map<GMLBuilding, BuildingDecorator> buildingDecorators; + + private transient RoadDecorator defaultRoadDecorator; + private transient Map<GMLRoad, RoadDecorator> roadDecorators; + + private transient SpaceDecorator defaultSpaceDecorator; + private transient Map<GMLSpace, SpaceDecorator> spaceDecorators; + + private transient List<Overlay> overlays; + + private boolean grid; + private double gridResolution; + private boolean paintNodes=true; + + /** + Create a GMLMapViewer. + */ + public GMLMapViewer() { + this(null); + } + + /** + Create a GMLMapViewer. + @param map The map to view. + */ + public GMLMapViewer(GMLMap map) { + panZoom = new PanZoomListener(this); + defaultNodeDecorator = new CrossNodeDecorator(NODE_COLOUR, NODE_SIZE); + defaultEdgeDecorator = new LineEdgeDecorator(EDGE_COLOUR); + FilledShapeDecorator d = new FilledShapeDecorator(BUILDING_COLOUR, ROAD_COLOUR, SPACE_COLOUR); + defaultBuildingDecorator = d; + defaultRoadDecorator = d; + defaultSpaceDecorator = d; + nodeDecorators = new HashMap<GMLNode, NodeDecorator>(); + edgeDecorators = new HashMap<GMLEdge, EdgeDecorator>(); + buildingDecorators = new HashMap<GMLBuilding, BuildingDecorator>(); + roadDecorators = new HashMap<GMLRoad, RoadDecorator>(); + spaceDecorators = new HashMap<GMLSpace, SpaceDecorator>(); + grid = false; + gridResolution = 1; + overlays = new ArrayList<Overlay>(); + setMap(map); + } + + /** + Set the map. + @param map The map to view. + */ + public void setMap(GMLMap map) { + this.map = map; + transform = null; + if (map != null) { + if (!map.hasSize()) { + // CHECKSTYLE:OFF:MagicNumber + transform = new ScreenTransform(0, 0, 100, 100); + // CHECKSTYLE:ON:MagicNumber + } + else { + transform = new ScreenTransform(map.getMinX(), map.getMinY(), map.getMaxX(), map.getMaxY()); + } + } + panZoom.setScreenTransform(transform); + } + + /** + View a particular set of objects. + @param objects The objects to view. + */ + public void view(GMLObject... objects) { + view(Arrays.asList(objects)); + } + + /** + View a particular set of objects. + @param objects The objects to view. + */ + public void view(List<? extends GMLObject> objects) { + if (objects == null || objects.isEmpty()) { + return; + } + Rectangle2D bounds = GMLTools.getObjectBounds(objects); + if (bounds == null) { + return; + } + if (bounds.getWidth() < MIN_ZOOM_BOUNDS_SIZE) { + bounds = new Rectangle2D.Double(bounds.getX() - MIN_ZOOM_BOUNDS_SIZE / 2, + bounds.getY(), MIN_ZOOM_BOUNDS_SIZE, bounds.getHeight()); + } + if (bounds.getHeight() < MIN_ZOOM_BOUNDS_SIZE) { + bounds = new Rectangle2D.Double(bounds.getX(), + bounds.getY() - MIN_ZOOM_BOUNDS_SIZE / 2, + bounds.getWidth(), MIN_ZOOM_BOUNDS_SIZE); + } + transform.show(bounds); + } + + /** + View all objects. + */ + public void viewAll() { + transform.resetZoom(); + } + + /** + Get the PanZoomListener for this component. + @return The PanZoomListener. + */ + public PanZoomListener getPanZoomListener() { + return panZoom; + } + + /** + Set the default node decorator. + @param defaultDecorator The new default node decorator. + */ + public void setDefaultNodeDecorator(NodeDecorator defaultDecorator) { + defaultNodeDecorator = defaultDecorator; + } + + /** + Get the default node decorator. + @return The default node decorator. + */ + public NodeDecorator getDefaultNodeDecorator() { + return defaultNodeDecorator; + } + + /** + Set the NodeDecorator for a set of GMLNodes. + @param decorator The decorator to set. + @param nodes The nodes to set the decorator for. + */ + public void setNodeDecorator(NodeDecorator decorator, GMLNode... nodes) { + setNodeDecorator(decorator, Arrays.asList(nodes)); + } + + /** + Set the NodeDecorator for a set of GMLNodes. + @param decorator The decorator to set. + @param nodes The nodes to set the decorator for. + */ + public void setNodeDecorator(NodeDecorator decorator, Collection<? extends GMLNode> nodes) { + for (GMLNode next : nodes) { + nodeDecorators.put(next, decorator); + } + } + + /** + Get the NodeDecorator for a GMLNodes. + @param node The node to look up. + @return The NodeDecorator for that node. This will be the default decorator if no custom decorator has been set for that node. + */ + public NodeDecorator getNodeDecorator(GMLNode node) { + NodeDecorator result = nodeDecorators.get(node); + if (result == null) { + result = defaultNodeDecorator; + } + return result; + } + + /** + Remove any custom NodeDecorator for a set of GMLNodes. + @param nodes The nodes to remove any custom decorator for. + */ + public void clearNodeDecorator(GMLNode... nodes) { + clearNodeDecorator(Arrays.asList(nodes)); + } + + /** + Remove any custom NodeDecorator for a set of GMLNodes. + @param nodes The nodes to remove any custom decorator for. + */ + public void clearNodeDecorator(Collection<? extends GMLNode> nodes) { + for (GMLNode next : nodes) { + nodeDecorators.remove(next); + } + } + + /** + Remove any custom NodeDecorators. + */ + public void clearAllNodeDecorators() { + nodeDecorators.clear(); + } + + /** + Set the default edge decorator. + @param defaultDecorator The new default edge decorator. + */ + public void setDefaultEdgeDecorator(EdgeDecorator defaultDecorator) { + defaultEdgeDecorator = defaultDecorator; + } + + /** + Get the default edge decorator. + @return The default edge decorator. + */ + public EdgeDecorator getDefaultEdgeDecorator() { + return defaultEdgeDecorator; + } + + /** + Set the EdgeDecorator for a set of GMLEdges. + @param decorator The decorator to set. + @param edges The edges to set the decorator for. + */ + public void setEdgeDecorator(EdgeDecorator decorator, GMLEdge... edges) { + setEdgeDecorator(decorator, Arrays.asList(edges)); + } + + /** + Set the EdgeDecorator for a set of GMLEdges. + @param decorator The decorator to set. + @param edges The edges to set the decorator for. + */ + public void setEdgeDecorator(EdgeDecorator decorator, Collection<? extends GMLEdge> edges) { + for (GMLEdge next : edges) { + edgeDecorators.put(next, decorator); + } + } + + /** + Get the EdgeDecorator for a GMLEdge. + @param edge The edge to look up. + @return The EdgeDecorator for that edge. This will be the default decorator if no custom decorator has been set for that edge. + */ + public EdgeDecorator getEdgeDecorator(GMLEdge edge) { + EdgeDecorator result = edgeDecorators.get(edge); + if (result == null) { + result = defaultEdgeDecorator; + } + return result; + } + + /** + Remove any custom EdgeDecorator for a set of GMLEdges. + @param edges The edges to remove any custom decorator for. + */ + public void clearEdgeDecorator(GMLEdge... edges) { + clearEdgeDecorator(Arrays.asList(edges)); + } + + /** + Remove any custom EdgeDecorator for a set of GMLEdges. + @param edges The edges to remove any custom decorator for. + */ + public void clearEdgeDecorator(Collection<? extends GMLEdge> edges) { + for (GMLEdge next : edges) { + edgeDecorators.remove(next); + } + } + + /** + Remove any custom EdgeDecorators. + */ + public void clearAllEdgeDecorators() { + edgeDecorators.clear(); + } + + /** + Set the default building decorator. + @param defaultDecorator The new default building decorator. + */ + public void setDefaultBuildingDecorator(BuildingDecorator defaultDecorator) { + defaultBuildingDecorator = defaultDecorator; + } + + /** + Get the default building decorator. + @return The default building decorator. + */ + public BuildingDecorator getDefaultBuildingDecorator() { + return defaultBuildingDecorator; + } + + /** + Set the BuildingDecorator for a set of GMLBuildings. + @param decorator The decorator to set. + @param buildings The buildings to set the decorator for. + */ + public void setBuildingDecorator(BuildingDecorator decorator, GMLBuilding... buildings) { + setBuildingDecorator(decorator, Arrays.asList(buildings)); + } + + /** + Set the BuildingDecorator for a set of GMLBuildings. + @param decorator The decorator to set. + @param buildings The buildings to set the decorator for. + */ + public void setBuildingDecorator(BuildingDecorator decorator, Collection<? extends GMLBuilding> buildings) { + for (GMLBuilding next : buildings) { + buildingDecorators.put(next, decorator); + } + } + + /** + Get the BuildingDecorator for a GMLBuildings. + @param building The building to look up. + @return The BuildingDecorator for that building. This will be the default decorator if no custom decorator has been set for that building. + */ + public BuildingDecorator getBuildingDecorator(GMLBuilding building) { + BuildingDecorator result = buildingDecorators.get(building); + if (result == null) { + result = defaultBuildingDecorator; + } + return result; + } + + /** + Remove any custom BuildingDecorator for a set of GMLBuildings. + @param buildings The buildings to remove any custom decorator for. + */ + public void clearBuildingDecorator(GMLBuilding... buildings) { + clearBuildingDecorator(Arrays.asList(buildings)); + } + + /** + Remove any custom BuildingDecorator for a set of GMLBuildings. + @param buildings The buildings to remove any custom decorator for. + */ + public void clearBuildingDecorator(Collection<? extends GMLBuilding> buildings) { + for (GMLBuilding next : buildings) { + buildingDecorators.remove(next); + } + } + + /** + Remove any custom BuildingDecorators. + */ + public void clearAllBuildingDecorators() { + buildingDecorators.clear(); + } + + /** + Set the default road decorator. + @param defaultDecorator The new default road decorator. + */ + public void setDefaultRoadDecorator(RoadDecorator defaultDecorator) { + defaultRoadDecorator = defaultDecorator; + } + + /** + Get the default road decorator. + @return The default road decorator. + */ + public RoadDecorator getDefaultRoadDecorator() { + return defaultRoadDecorator; + } + + /** + Set the RoadDecorator for a set of GMLRoads. + @param decorator The decorator to set. + @param roads The roads to set the decorator for. + */ + public void setRoadDecorator(RoadDecorator decorator, GMLRoad... roads) { + setRoadDecorator(decorator, Arrays.asList(roads)); + } + + /** + Set the RoadDecorator for a set of GMLRoads. + @param decorator The decorator to set. + @param roads The roads to set the decorator for. + */ + public void setRoadDecorator(RoadDecorator decorator, Collection<? extends GMLRoad> roads) { + for (GMLRoad next : roads) { + roadDecorators.put(next, decorator); + } + } + + /** + Get the RoadDecorator for a GMLRoads. + @param road The road to look up. + @return The RoadDecorator for that road. This will be the default decorator if no custom decorator has been set for that road. + */ + public RoadDecorator getRoadDecorator(GMLRoad road) { + RoadDecorator result = roadDecorators.get(road); + if (result == null) { + result = defaultRoadDecorator; + } + return result; + } + + /** + Remove any custom RoadDecorator for a set of GMLRoads. + @param roads The roads to remove any custom decorator for. + */ + public void clearRoadDecorator(GMLRoad... roads) { + clearRoadDecorator(Arrays.asList(roads)); + } + + /** + Remove any custom RoadDecorator for a set of GMLRoads. + @param roads The roads to remove any custom decorator for. + */ + public void clearRoadDecorator(Collection<? extends GMLRoad> roads) { + for (GMLRoad next : roads) { + roadDecorators.remove(next); + } + } + + /** + Remove any custom RoadDecorators. + */ + public void clearAllRoadDecorators() { + roadDecorators.clear(); + } + + /** + Set the default space decorator. + @param defaultDecorator The new default space decorator. + */ + public void setDefaultSpaceDecorator(SpaceDecorator defaultDecorator) { + defaultSpaceDecorator = defaultDecorator; + } + + /** + Get the default space decorator. + @return The default space decorator. + */ + public SpaceDecorator getDefaultSpaceDecorator() { + return defaultSpaceDecorator; + } + + /** + Set the SpaceDecorator for a set of GMLSpaces. + @param decorator The decorator to set. + @param spaces The spaces to set the decorator for. + */ + public void setSpaceDecorator(SpaceDecorator decorator, GMLSpace... spaces) { + setSpaceDecorator(decorator, Arrays.asList(spaces)); + } + + /** + Set the SpaceDecorator for a set of GMLSpaces. + @param decorator The decorator to set. + @param spaces The spaces to set the decorator for. + */ + public void setSpaceDecorator(SpaceDecorator decorator, Collection<? extends GMLSpace> spaces) { + for (GMLSpace next : spaces) { + spaceDecorators.put(next, decorator); + } + } + + /** + Get the SpaceDecorator for a GMLSpaces. + @param space The space to look up. + @return The SpaceDecorator for that space. This will be the default decorator if no custom decorator has been set for that space. + */ + public SpaceDecorator getSpaceDecorator(GMLSpace space) { + SpaceDecorator result = spaceDecorators.get(space); + if (result == null) { + result = defaultSpaceDecorator; + } + return result; + } + + /** + Remove any custom SpaceDecorator for a set of GMLSpaces. + @param spaces The spaces to remove any custom decorator for. + */ + public void clearSpaceDecorator(GMLSpace... spaces) { + clearSpaceDecorator(Arrays.asList(spaces)); + } + + /** + Remove any custom SpaceDecorator for a set of GMLSpaces. + @param spaces The spaces to remove any custom decorator for. + */ + public void clearSpaceDecorator(Collection<? extends GMLSpace> spaces) { + for (GMLSpace next : spaces) { + spaceDecorators.remove(next); + } + } + + /** + Remove any custom SpaceDecorators. + */ + public void clearAllSpaceDecorators() { + spaceDecorators.clear(); + } + + /** + Set whether to draw the grid or not. + @param b True to draw the grid. + */ + public void setGridEnabled(boolean b) { + grid = b; + } + + /** + Set the grid resolution. + @param resolution The new grid resolution. + */ + public void setGridResolution(double resolution) { + gridResolution = resolution; + } + + /** + Add an overlay to the view. + @param overlay The overlay to add. + */ + public void addOverlay(Overlay overlay) { + overlays.add(overlay); + } + + /** + Remove an overlay from the view. + @param overlay The overlay to remove. + */ + public void removeOverlay(Overlay overlay) { + overlays.remove(overlay); + } + + @Override + public void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + Graphics copy = graphics.create(); + copy.setColor(getBackground()); + copy.fillRect(0, 0, getWidth(), getHeight()); + if (map == null) { + return; + } + Insets insets = getInsets(); + int width = getWidth() - insets.left - insets.right; + int height = getHeight() - insets.top - insets.bottom; + Graphics2D g = (Graphics2D)graphics.create(insets.left, insets.top, width + 1 , height + 1); + transform.rescale(width, height); + Collection<GMLRoad> roads; + Collection<GMLBuilding> buildings; + Collection<GMLSpace> spaces; + Collection<GMLEdge> edges; + Collection<GMLNode> nodes; + synchronized (map) { + roads = new HashSet<GMLRoad>(map.getRoads()); + buildings = new HashSet<GMLBuilding>(map.getBuildings()); + spaces = new HashSet<GMLSpace>(map.getSpaces()); + edges = new HashSet<GMLEdge>(map.getEdges()); + nodes = new HashSet<GMLNode>(map.getNodes()); + } + for (GMLRoad next : roads) { + RoadDecorator d = getRoadDecorator(next); + if (d != null) { + d.decorate(next, (Graphics2D)g.create(), transform); + } + } + for (GMLBuilding next : buildings) { + BuildingDecorator d = getBuildingDecorator(next); + if (d != null) { + d.decorate(next, (Graphics2D)g.create(), transform); + } + } + for (GMLSpace next : spaces) { + SpaceDecorator d = getSpaceDecorator(next); + if (d != null) { + d.decorate(next, (Graphics2D)g.create(), transform); + } + } + for (GMLEdge next : edges) { + EdgeDecorator e = getEdgeDecorator(next); + if (e != null) { + e.decorate(next, (Graphics2D)g.create(), transform); + } + } + for (GMLNode next : nodes) { + NodeDecorator n = getNodeDecorator(next); + if (paintNodes&&n != null) { + n.decorate(next, (Graphics2D)g.create(), transform); + } + } + for (Overlay next : overlays) { + next.render((Graphics2D)g.create(), transform); + } + if (grid) { + double xMin = roundDownToGrid(transform.screenToX(0)); + double xMax = roundUpToGrid(transform.screenToX(width)); + double yMin = roundDownToGrid(transform.screenToY(height)); + double yMax = roundUpToGrid(transform.screenToY(0)); + g.setColor(GRID_COLOUR); + for (double worldX = xMin; worldX <= xMax; worldX += gridResolution) { + int x = transform.xToScreen(worldX); + g.drawLine(x, 0, x, height); + } + for (double worldY = yMin; worldY <= yMax; worldY += gridResolution) { + int y = transform.yToScreen(worldY); + g.drawLine(0, y, width, y); + } + } + } + + @Override + public boolean isOpaque() { + return true; + } + + /** + Enable or disable the pan/zoom feature. + @param enabled Whether pan/zoom should be enabled or not. + */ + public void setPanZoomEnabled(boolean enabled) { + panZoom.setEnabled(enabled); + } + + /** + Get the coordinates of a point on screen. + @param x The screen x coordinate. + @param y The screen y coordinate. + @return The coordinates in the GML map under the screen point. + */ + public GMLCoordinates getCoordinatesAtPoint(int x, int y) { + double cx = transform.screenToX(x); + double cy = transform.screenToY(y); + return new GMLCoordinates(cx, cy); + } + + /** + Get the on-screen coordinates for a point. + @param c The GML coordinates to look up. + @return The on-screen coordinates of the point. + */ + public Point getScreenCoordinates(GMLCoordinates c) { + int x = transform.xToScreen(c.getX()); + int y = transform.yToScreen(c.getY()); + return new Point(x, y); + } + + private double roundDownToGrid(double d) { + return Math.floor(d / gridResolution) * gridResolution; + } + + private double roundUpToGrid(double d) { + return Math.ceil(d / gridResolution) * gridResolution; + } + + public void setPaintNodes(boolean paintNodes) { + this.paintNodes = paintNodes; + + } +} diff --git a/modules/maps/src/maps/gml/view/GMLObjectInspector.java b/modules/maps/src/maps/gml/view/GMLObjectInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..930451aab26395fb1c8d17b8c0355959c104f716 --- /dev/null +++ b/modules/maps/src/maps/gml/view/GMLObjectInspector.java @@ -0,0 +1,381 @@ +package maps.gml.view; + +import java.awt.BorderLayout; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLEdge; +import maps.gml.GMLMap; +import maps.gml.GMLNode; +import maps.gml.GMLObject; +import maps.gml.GMLShape; +import maps.validate.ValidationError; + +/** + A class for inspecting GML objects. +*/ +public class GMLObjectInspector extends JPanel { + private static final int NODE_ROW_ID = 0; + private static final int NODE_ROW_X = 1; + private static final int NODE_ROW_Y = 2; + private static final int NODE_ROW_ATTACHED_EDGES = 3; + private static final int NODE_ROWS = 4; + + private static final int EDGE_ROW_ID = 0; + private static final int EDGE_ROW_START = 1; + private static final int EDGE_ROW_END = 2; + private static final int EDGE_ROW_PASSABLE = 3; + private static final int EDGE_ROW_ATTACHED_SHAPES = 4; + private static final int EDGE_ROWS = 5; + + private static final int SHAPE_ROW_ID = 0; + private static final int SHAPE_ROW_EDGE_COUNT = 1; + private static final int SHAPE_BASE_ROWS = 2; + + private static final TableModel EMPTY_MODEL = new AbstractTableModel() { + @Override + public int getRowCount() { + return 0; + } + + @Override + public int getColumnCount() { + return 0; + } + + @Override + public Object getValueAt(int row, int col) { + return null; + } + }; + + private GMLMap map; + private JTable table; + private NodeTableModel nodeModel; + private EdgeTableModel edgeModel; + private ShapeTableModel shapeModel; + private Map<Integer, List<ValidationError>> errors; + + /** + Construct a new GMLObjectInspector. + @param map The GMLMap to consult for geometry information. + */ + public GMLObjectInspector(GMLMap map) { + super(new BorderLayout()); + this.map = map; + errors = new HashMap<Integer, List<ValidationError>>(); + nodeModel = new NodeTableModel(); + edgeModel = new EdgeTableModel(); + shapeModel = new ShapeTableModel(); + table = new JTable(); + JScrollPane scroll = new JScrollPane(table); + add(scroll, BorderLayout.CENTER); + } + + /** + Set the map this inspector should consult for geometry information. + @param newMap The new map. + */ + public void setMap(GMLMap newMap) { + map = newMap; + } + + /** + Inspect a GMLNode. + @param node The node to inspect. + */ + public void inspect(GMLNode node) { + table.setModel(nodeModel); + List<ValidationError> e = (node == null) ? null : errors.get(node.getID()); + nodeModel.show(node, e); + } + + /** + Inspect a GMLEdge. + @param edge The edge to inspect. + */ + public void inspect(GMLEdge edge) { + table.setModel(edgeModel); + List<ValidationError> e = (edge == null) ? null : errors.get(edge.getID()); + edgeModel.show(edge, e); + } + + /** + Inspect a GMLShape. + @param shape The shape to inspect. + */ + public void inspect(GMLShape shape) { + table.setModel(shapeModel); + List<ValidationError> e = (shape == null) ? null : errors.get(shape.getID()); + shapeModel.show(shape, e); + } + + /** + Inspect a GMLObject. + @param object The object to inspect. + */ + public void inspect(GMLObject object) { + if (object == null) { + table.setModel(EMPTY_MODEL); + } + else if (object instanceof GMLNode) { + inspect((GMLNode)object); + } + else if (object instanceof GMLEdge) { + inspect((GMLEdge)object); + } + else if (object instanceof GMLShape) { + inspect((GMLShape)object); + } + else { + throw new IllegalArgumentException("Don't know how to inspect " + object); + } + } + + /** + * Set the Collection of ValidationErrors for the Inspector to display in the table. + * @param err The collection of errors. + */ + public void setErrors(Collection<ValidationError> err) { + errors.clear(); + for (ValidationError e : err) { + if (!errors.containsKey(e.getId())) { + errors.put(e.getId(), new ArrayList<ValidationError>()); + } + errors.get(e.getId()).add(e); + } + } + + private class NodeTableModel extends AbstractTableModel { + private GMLNode node; + private List<ValidationError> errors; + + void show(GMLNode n, List<ValidationError> err) { + node = n; + errors = err; + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + int errorCount = (errors == null) ? 0 : errors.size(); + return NODE_ROWS + errorCount; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int row, int col) { + if (col == 0) { + switch (row) { + case NODE_ROW_ID: + return "Node ID"; + case NODE_ROW_X: + return "X"; + case NODE_ROW_Y: + return "Y"; + case NODE_ROW_ATTACHED_EDGES: + return "Attached edges"; + default: + return "Error"; + } + } + else if (col == 1) { + if (node == null) { + return null; + } + switch (row) { + case NODE_ROW_ID: + return node.getID(); + case NODE_ROW_X: + return node.getX(); + case NODE_ROW_Y: + return node.getY(); + case NODE_ROW_ATTACHED_EDGES: + if (map == null) { + return ""; + } + Collection<GMLEdge> attached = map.getAttachedEdges(node); + StringBuilder result = new StringBuilder(); + for (GMLEdge next : attached) { + result.append(next.toString()); + result.append(" "); + } + return result.toString(); + default: + int errorCount = (errors == null) ? 0 : errors.size(); + int index = row - NODE_ROWS; + if (index < 0 || index >= errorCount) { + throw new IllegalArgumentException("Invalid row: " + row); + } + return errors.get(index).getMessage(); + } + } + else { + throw new IllegalArgumentException("Unrecognised column: " + col); + } + } + } + + private class EdgeTableModel extends AbstractTableModel { + private GMLEdge edge; + private List<ValidationError> errors; + + void show(GMLEdge e, List<ValidationError> err) { + edge = e; + errors = err; + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + int errorCount = (errors == null) ? 0 : errors.size(); + return EDGE_ROWS + errorCount; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int row, int col) { + if (col == 0) { + switch (row) { + case EDGE_ROW_ID: + return "Edge ID"; + case EDGE_ROW_START: + return "Start node"; + case EDGE_ROW_END: + return "End node"; + case EDGE_ROW_PASSABLE: + return "Passable"; + case EDGE_ROW_ATTACHED_SHAPES: + return "Attached shapes"; + default: + return "Error"; + } + } + else if (col == 1) { + if (edge == null) { + return null; + } + switch (row) { + case EDGE_ROW_ID: + return edge.getID(); + case EDGE_ROW_START: + return edge.getStart().getID(); + case EDGE_ROW_END: + return edge.getEnd().getID(); + case EDGE_ROW_PASSABLE: + return edge.isPassable(); + case EDGE_ROW_ATTACHED_SHAPES: + if (map == null) { + return ""; + } + Collection<GMLShape> attached = map.getAttachedShapes(edge); + StringBuilder result = new StringBuilder(); + for (GMLShape next : attached) { + result.append(next.toString()); + result.append(" "); + } + return result.toString(); + default: + int errorCount = (errors == null) ? 0 : errors.size(); + int index = row - EDGE_ROWS; + if (index < 0 || index >= errorCount) { + throw new IllegalArgumentException("Invalid row: " + row); + } + return errors.get(index).getMessage(); + } + } + else { + throw new IllegalArgumentException("Unrecognised column: " + col); + } + } + } + + private static class ShapeTableModel extends AbstractTableModel { + private GMLShape shape; + private List<ValidationError> errors; + + void show(GMLShape s, List<ValidationError> err) { + shape = s; + errors = err; + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + int edgeCount = (shape == null) ? 0 : shape.getEdges().size(); + int errorCount = (errors == null) ? 0 : errors.size(); + return SHAPE_BASE_ROWS + edgeCount + errorCount; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int row, int col) { + if (col == 0) { + switch (row) { + case SHAPE_ROW_ID: + return "Shape ID"; + case SHAPE_ROW_EDGE_COUNT: + return "Number of edges"; + default: + int edgeCount = (shape == null) ? 0 : shape.getEdges().size(); + if (row < SHAPE_BASE_ROWS + edgeCount) { + return "Edge " + (row - SHAPE_BASE_ROWS + 1); + } + return "Error"; + } + } + else if (col == 1) { + if (shape == null) { + return null; + } + switch (row) { + case SHAPE_ROW_ID: + return shape.getID(); + case SHAPE_ROW_EDGE_COUNT: + return shape.getEdges().size(); + default: + int edgeCount = shape.getEdges().size(); + if (row < SHAPE_BASE_ROWS + edgeCount) { + List<GMLDirectedEdge> edges = shape.getEdges(); + int index = row - SHAPE_BASE_ROWS; + if (index < 0 || index >= edges.size()) { + throw new IllegalArgumentException("Invalid row: " + row); + } + return edges.get(index); + } + int errorCount = (errors == null) ? 0 : errors.size(); + int index = row - SHAPE_BASE_ROWS - edgeCount; + if (index < 0 || index >= errorCount) { + throw new IllegalArgumentException("Invalid row: " + row); + } + return errors.get(index).getMessage(); + } + } + else { + throw new IllegalArgumentException("Unrecognised column: " + col); + } + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/LineEdgeDecorator.java b/modules/maps/src/maps/gml/view/LineEdgeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..88026c921b2a297f5c9d09e3859a89feb88b2fe6 --- /dev/null +++ b/modules/maps/src/maps/gml/view/LineEdgeDecorator.java @@ -0,0 +1,37 @@ +package maps.gml.view; + +import maps.gml.GMLEdge; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Stroke; +import java.awt.BasicStroke; + +/** + An EdgeDecorator that draws a line for each edge. +*/ +public class LineEdgeDecorator implements EdgeDecorator { + private static final Stroke PASSABLE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke IMPASSABLE_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + + private Color colour; + + /** + Construct a LineEdgeDecorator. + @param colour The colour to draw the line. + */ + public LineEdgeDecorator(Color colour) { + this.colour = colour; + } + + @Override + public void decorate(GMLEdge edge, Graphics2D g, ScreenTransform transform) { + int x1 = transform.xToScreen(edge.getStart().getX()); + int y1 = transform.yToScreen(edge.getStart().getY()); + int x2 = transform.xToScreen(edge.getEnd().getX()); + int y2 = transform.yToScreen(edge.getEnd().getY()); + g.setColor(colour); + g.setStroke(edge.isPassable() ? PASSABLE_STROKE : IMPASSABLE_STROKE); + g.drawLine(x1, y1, x2, y2); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/LineOverlay.java b/modules/maps/src/maps/gml/view/LineOverlay.java new file mode 100644 index 0000000000000000000000000000000000000000..8dc644412f27ca104537fca824cc313f275acb5f --- /dev/null +++ b/modules/maps/src/maps/gml/view/LineOverlay.java @@ -0,0 +1,97 @@ +package maps.gml.view; + +import java.awt.Graphics2D; +import java.awt.Color; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.geometry.Point2D; + +/** + A line overlay. + */ +public class LineOverlay implements Overlay { + private Point2D start; + private Point2D end; + private Color colour; + private boolean useWorldCoords; + + /** + Construct a LineOverlay with no coordinates defined.. + @param colour The colour to draw the rectangle. + @param useWorldCoords Whether to convert the coordinates from world to screen. Set to false if you want to directly specify screen coordinates. + */ + public LineOverlay(Color colour, boolean useWorldCoords) { + this(null, null, colour, useWorldCoords); + } + + /** + Construct a LineOverlay. + @param start The start coordinate. + @param end The end coordinate. + @param colour The colour to draw the rectangle. + @param useWorldCoords Whether to convert the coordinates from world to screen. Set to false if you want to directly specify screen coordinates. + */ + public LineOverlay(Point2D start, Point2D end, Color colour, boolean useWorldCoords) { + this.start = start; + this.end = end; + this.colour = colour; + this.useWorldCoords = useWorldCoords; + } + + /** + Set the start coordinate. + @param p The new start coordinate. + */ + public void setStart(Point2D p) { + start = p; + } + + /** + Set the end coordinate. + @param p The new end coordinate. + */ + public void setEnd(Point2D p) { + end = p; + } + + /** + Set the colour. + @param c The new colour. + */ + public void setColour(Color c) { + colour = c; + } + + /** + Set whether to use world coordinates or not. If true, the coordinates will be converted to screen coordinates; if false the coordinates will be used as given. + @param b True to use world coordinates, false otherwise. + */ + public void setUseWorldCoordinates(boolean b) { + useWorldCoords = b; + } + + @Override + public void render(Graphics2D g, ScreenTransform transform) { + if (start == null || end == null) { + return; + } + Graphics2D graphics = (Graphics2D)g.create(); + graphics.setColor(colour); + double x1 = start.getX(); + double x2 = end.getX(); + double y1 = start.getY(); + double y2 = end.getY(); + if (useWorldCoords) { + x1 = transform.xToScreen(x1); + x2 = transform.xToScreen(x2); + /* + double temp = transform.yToScreen(y2); + y2 = transform.yToScreen(y1); + y1 = temp; + */ + y1 = transform.yToScreen(y1); + y2 = transform.yToScreen(y2); + } + graphics.drawLine((int)x1, (int)y1, (int)x2, (int)y2); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NodeDecorator.java b/modules/maps/src/maps/gml/view/NodeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..ad0de3a290d6296eac496f802133d4758aa2e789 --- /dev/null +++ b/modules/maps/src/maps/gml/view/NodeDecorator.java @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLNode; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLNodes. +*/ +public interface NodeDecorator { + /** + Decorate a GMLNode. + @param node The node to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLNode node, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NullBuildingDecorator.java b/modules/maps/src/maps/gml/view/NullBuildingDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..455ba4909e9c1998f083721f91215db4d74a0463 --- /dev/null +++ b/modules/maps/src/maps/gml/view/NullBuildingDecorator.java @@ -0,0 +1,14 @@ +package maps.gml.view; + +import maps.gml.GMLBuilding; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + A no-op BuildingDecorator. +*/ +public class NullBuildingDecorator implements BuildingDecorator { + @Override + public void decorate(GMLBuilding building, Graphics2D g, ScreenTransform transform) { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NullEdgeDecorator.java b/modules/maps/src/maps/gml/view/NullEdgeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..9331075c1f4ed6c016132048a17ee874d14ea21a --- /dev/null +++ b/modules/maps/src/maps/gml/view/NullEdgeDecorator.java @@ -0,0 +1,14 @@ +package maps.gml.view; + +import maps.gml.GMLEdge; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + A no-op EdgeDecorator. +*/ +public class NullEdgeDecorator implements EdgeDecorator { + @Override + public void decorate(GMLEdge edge, Graphics2D g, ScreenTransform transform) { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NullNodeDecorator.java b/modules/maps/src/maps/gml/view/NullNodeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..0f0f866286abecd1a44486a155d059d557553089 --- /dev/null +++ b/modules/maps/src/maps/gml/view/NullNodeDecorator.java @@ -0,0 +1,14 @@ +package maps.gml.view; + +import maps.gml.GMLNode; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + A no-op NodeDecorator. +*/ +public class NullNodeDecorator implements NodeDecorator { + @Override + public void decorate(GMLNode node, Graphics2D g, ScreenTransform transform) { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NullRoadDecorator.java b/modules/maps/src/maps/gml/view/NullRoadDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..c6eb2e5677fbd231de06cd4570b9507c9f53de70 --- /dev/null +++ b/modules/maps/src/maps/gml/view/NullRoadDecorator.java @@ -0,0 +1,14 @@ +package maps.gml.view; + +import maps.gml.GMLRoad; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + A no-op RoadDecorator. +*/ +public class NullRoadDecorator implements RoadDecorator { + @Override + public void decorate(GMLRoad road, Graphics2D g, ScreenTransform transform) { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/NullSpaceDecorator.java b/modules/maps/src/maps/gml/view/NullSpaceDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..c7ddb37af14974b873571c4fcc4008d3a1bc2df2 --- /dev/null +++ b/modules/maps/src/maps/gml/view/NullSpaceDecorator.java @@ -0,0 +1,14 @@ +package maps.gml.view; + +import maps.gml.GMLSpace; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + A no-op SpaceDecorator. +*/ +public class NullSpaceDecorator implements SpaceDecorator { + @Override + public void decorate(GMLSpace space, Graphics2D g, ScreenTransform transform) { + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/Overlay.java b/modules/maps/src/maps/gml/view/Overlay.java new file mode 100644 index 0000000000000000000000000000000000000000..e0ec768ca27b80b00f3489617b49f42e41e92335 --- /dev/null +++ b/modules/maps/src/maps/gml/view/Overlay.java @@ -0,0 +1,17 @@ +package maps.gml.view; + +import java.awt.Graphics2D; + +import rescuecore2.misc.gui.ScreenTransform; + +/** + Interface for overlays that appear on the GML map viewer. +*/ +public interface Overlay { + /** + Render this overlay. + @param g The graphics to draw on. + @param transform The current screen transform. + */ + void render(Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/RectangleOverlay.java b/modules/maps/src/maps/gml/view/RectangleOverlay.java new file mode 100644 index 0000000000000000000000000000000000000000..c81614d95f96b4f3d7b8405f2dfa9535a95fb8c1 --- /dev/null +++ b/modules/maps/src/maps/gml/view/RectangleOverlay.java @@ -0,0 +1,114 @@ +package maps.gml.view; + +import java.awt.Graphics2D; +import java.awt.Color; + +import rescuecore2.misc.gui.ScreenTransform; + +/** + A rectangular overlay. + */ +public class RectangleOverlay implements Overlay { + private double left; + private double right; + private double bottom; + private double top; + private Color colour; + private boolean useWorldCoords; + + /** + Construct a RectangleOverlay with no coordinates defined.. + @param colour The colour to draw the rectangle. + @param useWorldCoords Whether to convert the coordinates from world to screen. Set to false if you want to directly specify screen coordinates. + */ + public RectangleOverlay(Color colour, boolean useWorldCoords) { + this(Double.NaN, Double.NaN, Double.NaN, Double.NaN, colour, useWorldCoords); + } + + /** + Construct a RectangleOverlay. + @param left The left-hand X coordinate. + @param right The right-hand X coordinate. + @param top The top Y coordinate. + @param bottom The bottom Y coordinate. + @param colour The colour to draw the rectangle. + @param useWorldCoords Whether to convert the coordinates from world to screen. Set to false if you want to directly specify screen coordinates. + */ + public RectangleOverlay(double left, double right, double top, double bottom, Color colour, boolean useWorldCoords) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + this.colour = colour; + this.useWorldCoords = useWorldCoords; + } + + /** + Set the left-hand X coordinate. + @param x The new coordinate. + */ + public void setLeft(double x) { + left = x; + } + + /** + Set the right-hand X coordinate. + @param x The new coordinate. + */ + public void setRight(double x) { + right = x; + } + + /** + Set the top Y coordinate. + @param y The new coordinate. + */ + public void setTop(double y) { + top = y; + } + + /** + Set the bottom Y coordinate. + @param y The new coordinate. + */ + public void setBottom(double y) { + bottom = y; + } + + /** + Set the colour. + @param c The new colour. + */ + public void setColour(Color c) { + colour = c; + } + + /** + Set whether to use world coordinates or not. If true, the coordinates will be converted to screen coordinates; if false the coordinates will be used as given. + @param b True to use world coordinates, false otherwise. + */ + public void setUseWorldCoordinates(boolean b) { + useWorldCoords = b; + } + + @Override + public void render(Graphics2D g, ScreenTransform transform) { + if (Double.isNaN(left) || Double.isNaN(right) || Double.isNaN(top) || Double.isNaN(bottom)) { + return; + } + Graphics2D graphics = (Graphics2D)g.create(); + graphics.setColor(colour); + double x1 = left < right ? left : right; + double x2 = left < right ? right : left; + double y1 = bottom < top ? bottom : top; + double y2 = bottom < top ? top : bottom; + if (useWorldCoords) { + x1 = transform.xToScreen(x1); + x2 = transform.xToScreen(x2); + double temp = transform.yToScreen(y2); + y2 = transform.yToScreen(y1); + y1 = temp; + } + graphics.fillRect((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1)); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/RoadDecorator.java b/modules/maps/src/maps/gml/view/RoadDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..c0f03ed258fa05416ac6a13594de5dd48389b9ff --- /dev/null +++ b/modules/maps/src/maps/gml/view/RoadDecorator.java @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLRoad; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLRoads. +*/ +public interface RoadDecorator { + /** + Decorate a GMLRoad. + @param road The road to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLRoad road, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/SpaceDecorator.java b/modules/maps/src/maps/gml/view/SpaceDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..e2824d5eefca2615bef13e69c9dafc2504c35b46 --- /dev/null +++ b/modules/maps/src/maps/gml/view/SpaceDecorator.java @@ -0,0 +1,18 @@ +package maps.gml.view; + +import maps.gml.GMLSpace; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; + +/** + Interface for objects that know how to decorate GMLSpaces. +*/ +public interface SpaceDecorator { + /** + Decorate a GMLSpace. + @param space The space to decorate. + @param g The graphics to draw on. + @param transform The screen transform. + */ + void decorate(GMLSpace space, Graphics2D g, ScreenTransform transform); +} \ No newline at end of file diff --git a/modules/maps/src/maps/gml/view/SquareNodeDecorator.java b/modules/maps/src/maps/gml/view/SquareNodeDecorator.java new file mode 100644 index 0000000000000000000000000000000000000000..4dd43b291e2cb21f6ce5af3d8e6ac1810b7d3b5f --- /dev/null +++ b/modules/maps/src/maps/gml/view/SquareNodeDecorator.java @@ -0,0 +1,32 @@ +package maps.gml.view; + +import maps.gml.GMLNode; +import rescuecore2.misc.gui.ScreenTransform; +import java.awt.Graphics2D; +import java.awt.Color; + +/** + A NodeDecorator that draws a square for each node. +*/ +public class SquareNodeDecorator implements NodeDecorator { + private Color colour; + private int size; + + /** + Construct a SquareNodeDecorator. + @param colour The colour to draw the square. + @param size The size of the square. + */ + public SquareNodeDecorator(Color colour, int size) { + this.colour = colour; + this.size = size; + } + + @Override + public void decorate(GMLNode node, Graphics2D g, ScreenTransform transform) { + int x = transform.xToScreen(node.getX()); + int y = transform.yToScreen(node.getY()); + g.setColor(colour); + g.fillRect(x - (size / 2), y - (size / 2), size + 1, size + 1); + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/legacy/LegacyBuilding.java b/modules/maps/src/maps/legacy/LegacyBuilding.java new file mode 100644 index 0000000000000000000000000000000000000000..a935ff42f59d7ae99fdb1c3da41cf45fb2af412e --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyBuilding.java @@ -0,0 +1,78 @@ +package maps.legacy; + +import static rescuecore2.misc.EncodingTools.readInt32LE; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.InputStream; +import java.io.IOException; + +/** + A legacy building. +*/ +public class LegacyBuilding extends LegacyObject { + private int floors; + private int code; + private int[] entrances; + private int[] apexes; + + @Override + public void read(InputStream in) throws IOException { + // CHECKSTYLE:OFF:MagicNumber + // Skip size + reallySkip(in, 4); + id = readInt32LE(in); + x = readInt32LE(in); + y = readInt32LE(in); + floors = readInt32LE(in); + // Skip attributes, ignition, fieryness, brokenness - 4 * 4 bytes + reallySkip(in, 16); + int numEntrances = readInt32LE(in); + entrances = new int[numEntrances]; + for (int j = 0; j < numEntrances; ++j) { + entrances[j] = readInt32LE(in); + } + // Skip shapeID, ground area, total read - 3 * 4 bytes + reallySkip(in, 12); + code = readInt32LE(in); + int numApexes = readInt32LE(in); + apexes = new int[numApexes * 2]; + for (int j = 0; j < numApexes; ++j) { + // Apexes + apexes[j * 2] = readInt32LE(in); + apexes[j * 2 + 1] = readInt32LE(in); + } + // CHECKSTYLE:ON:MagicNumber + } + + /** + Get the number of floors in this building. + @return The number of floors. + */ + public int getFloors() { + return floors; + } + + /** + Get the building code. + @return The building code. + */ + public int getCode() { + return code; + } + + /** + Get the list of entrance nodes. + @return The entrances. + */ + public int[] getEntrances() { + return entrances; + } + + /** + Get the list of apexes. + @return The apex list. + */ + public int[] getApexes() { + return apexes; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/legacy/LegacyMap.java b/modules/maps/src/maps/legacy/LegacyMap.java new file mode 100644 index 0000000000000000000000000000000000000000..cf8f7630997012920474dc78e45433f0b785ec0b --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyMap.java @@ -0,0 +1,154 @@ +package maps.legacy; + +import static rescuecore2.misc.EncodingTools.readInt32LE; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.File; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; + +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; + +import rescuecore2.log.Logger; + +/** + A legacy (version 0) RoboCup Rescue map. +*/ +public class LegacyMap implements maps.Map { + private Map<Integer, LegacyNode> nodes; + private Map<Integer, LegacyRoad> roads; + private Map<Integer, LegacyBuilding> buildings; + + /** + Construct an empty map. + */ + public LegacyMap() { + nodes = new HashMap<Integer, LegacyNode>(); + roads = new HashMap<Integer, LegacyRoad>(); + buildings = new HashMap<Integer, LegacyBuilding>(); + } + + /** + Construct a map and read from a directory. + @param baseDir The map directory. + @throws IOException If there is a problem reading the map. + */ + public LegacyMap(File baseDir) throws IOException { + this(); + read(baseDir); + } + + /** + Read map data from a directory. + @param baseDir The map directory. + @throws IOException If there is a problem reading the map. + */ + public void read(File baseDir) throws IOException { + nodes.clear(); + roads.clear(); + buildings.clear(); + readNodes(baseDir); + readRoads(baseDir); + readBuildings(baseDir); + } + + /** + Get all roads. + @return All roads. + */ + public Collection<LegacyRoad> getRoads() { + return Collections.unmodifiableCollection(roads.values()); + } + + /** + Get a road by ID. + @param id The ID to look up. + @return The road with the given ID or null if no such road exists. + */ + public LegacyRoad getRoad(int id) { + return roads.get(id); + } + + /** + Get all nodes. + @return All nodes. + */ + public Collection<LegacyNode> getNodes() { + return Collections.unmodifiableCollection(nodes.values()); + } + + /** + Get a node by ID. + @param id The ID to look up. + @return The node with the given ID or null if no such node exists. + */ + public LegacyNode getNode(int id) { + return nodes.get(id); + } + + /** + Get all buildings. + @return All buildings. + */ + public Collection<LegacyBuilding> getBuildings() { + return Collections.unmodifiableCollection(buildings.values()); + } + + /** + Get a building by ID. + @param id The ID to look up. + @return The building with the given ID or null if no such building exists. + */ + public LegacyBuilding getBuilding(int id) { + return buildings.get(id); + } + + private void readNodes(File baseDir) throws IOException { + File f = new File(baseDir, "node.bin"); + InputStream in = new FileInputStream(f); + // CHECKSTYLE:OFF:MagicNumber + reallySkip(in, 12); + // CHECKSTYLE:ON:MagicNumber + int num = readInt32LE(in); + Logger.debug("Reading " + num + " nodes"); + for (int i = 0; i < num; ++i) { + LegacyNode node = new LegacyNode(); + node.read(in); + nodes.put(node.getID(), node); + } + } + + private void readRoads(File baseDir) throws IOException { + File f = new File(baseDir, "road.bin"); + InputStream in = new FileInputStream(f); + // CHECKSTYLE:OFF:MagicNumber + reallySkip(in, 12); + // CHECKSTYLE:ON:MagicNumber + int num = readInt32LE(in); + Logger.debug("Reading " + num + " roads"); + for (int i = 0; i < num; ++i) { + LegacyRoad road = new LegacyRoad(); + road.read(in); + roads.put(road.getID(), road); + } + } + + private void readBuildings(File baseDir) throws IOException { + File f = new File(baseDir, "building.bin"); + InputStream in = new FileInputStream(f); + // CHECKSTYLE:OFF:MagicNumber + reallySkip(in, 12); + // CHECKSTYLE:ON:MagicNumber + int num = readInt32LE(in); + Logger.debug("Reading " + num + " buildings"); + for (int i = 0; i < num; ++i) { + LegacyBuilding building = new LegacyBuilding(); + building.read(in); + buildings.put(building.getID(), building); + } + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/legacy/LegacyMapFormat.java b/modules/maps/src/maps/legacy/LegacyMapFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..adbf5929b5597af22b56fdcc427952a8a6180048 --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyMapFormat.java @@ -0,0 +1,45 @@ +package maps.legacy; + +import maps.Map; +import maps.MapFormat; +import maps.MapException; + +import java.io.File; +import java.io.IOException; + +/** + MapFormat for legacy maps. +*/ +public final class LegacyMapFormat implements MapFormat { + /** Singleton instance. */ + public static final LegacyMapFormat INSTANCE = new LegacyMapFormat(); + + private LegacyMapFormat() {} + + @Override + public LegacyMap read(File file) throws MapException { + try { + return new LegacyMap(file); + } + catch (IOException e) { + throw new MapException(e); + } + } + + @Override + public void write(Map map, File file) throws MapException { + throw new RuntimeException("LegacyMapFormat.write not implemented"); + } + + @Override + public boolean canRead(File file) throws MapException { + if (!file.exists() || !file.isDirectory()) { + return false; + } + // Look for road.bin, node.bin and building.bin files + File road = new File(file, "road.bin"); + File node = new File(file, "node.bin"); + File building = new File(file, "building.bin"); + return road.exists() && node.exists() && building.exists(); + } +} diff --git a/modules/maps/src/maps/legacy/LegacyNode.java b/modules/maps/src/maps/legacy/LegacyNode.java new file mode 100644 index 0000000000000000000000000000000000000000..0d3f28b48f4955042e850b389ce9bdb6e196fab6 --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyNode.java @@ -0,0 +1,40 @@ +package maps.legacy; + +import static rescuecore2.misc.EncodingTools.readInt32LE; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.InputStream; +import java.io.IOException; + +/** + A legacy node. +*/ +public class LegacyNode extends LegacyObject { + private int[] edges; + + @Override + public void read(InputStream in) throws IOException { + // CHECKSTYLE:OFF:MagicNumber + // Skip size + reallySkip(in, 4); + id = readInt32LE(in); + x = readInt32LE(in); + y = readInt32LE(in); + int numEdges = readInt32LE(in); + edges = new int[numEdges]; + for (int j = 0; j < numEdges; ++j) { + edges[j] = readInt32LE(in); + } + // Skip signal flag, timing, pocket to turn across, shortcut to turn + reallySkip(in, (numEdges * 6 + 1) * 4); + // CHECKSTYLE:ON:MagicNumber + } + + /** + Get the list of edges, i.e. roads and buildings adjacent to this node. + @return The edge list. + */ + public int[] getEdges() { + return edges; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/legacy/LegacyObject.java b/modules/maps/src/maps/legacy/LegacyObject.java new file mode 100644 index 0000000000000000000000000000000000000000..2ab34f67044223904cf1c3832ad48f328c6dcea6 --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyObject.java @@ -0,0 +1,47 @@ +package maps.legacy; + +import java.io.InputStream; +import java.io.IOException; + +/** + Abstract base class for legacy objects. +*/ +public abstract class LegacyObject { + /** The ID of this object. */ + protected int id; + /** The X coordinate. */ + protected int x; + /** The Y coordinate. */ + protected int y; + + /** + Read the data for this object. + @param in The InputStream to read. + @throws IOException If there is a problem reading the stream. + */ + public abstract void read(InputStream in) throws IOException; + + /** + Get the ID of this object. + @return The object ID. + */ + public int getID() { + return id; + } + + /** + Get the X coordinate. + @return The X coordinate. + */ + public int getX() { + return x; + } + + /** + Get the Y coordinate. + @return The Y coordinate. + */ + public int getY() { + return y; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/legacy/LegacyRoad.java b/modules/maps/src/maps/legacy/LegacyRoad.java new file mode 100644 index 0000000000000000000000000000000000000000..2751c7795cbfca7e9b36331b5d90c5c81b78f822 --- /dev/null +++ b/modules/maps/src/maps/legacy/LegacyRoad.java @@ -0,0 +1,66 @@ +package maps.legacy; + +import static rescuecore2.misc.EncodingTools.readInt32LE; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.InputStream; +import java.io.IOException; + +/** + A legacy road. +*/ +public class LegacyRoad extends LegacyObject { + private int head; + private int tail; + private int length; + private int width; + + @Override + public void read(InputStream in) throws IOException { + // CHECKSTYLE:OFF:MagicNumber + // Skip size + reallySkip(in, 4); + id = readInt32LE(in); + head = readInt32LE(in); + tail = readInt32LE(in); + length = readInt32LE(in); + // Skip roadkind, cars/humans to head/tail - 5 * 4 bytes + reallySkip(in, 20); + width = readInt32LE(in); + // Skip block, repaircost, median, lines to head/tail, width for walkers - 6 * 4 bytes + reallySkip(in, 24); + // CHECKSTYLE:ON:MagicNumber + } + + /** + Get the ID of the head node. + @return The head node id. + */ + public int getHead() { + return head; + } + + /** + Get the ID of the tail node. + @return The tail node id. + */ + public int getTail() { + return tail; + } + + /** + Get the length of this road in mm. + @return The length. + */ + public int getLength() { + return length; + } + + /** + Get the width of this road in mm. + @return The width. + */ + public int getWidth() { + return width; + } +} \ No newline at end of file diff --git a/modules/maps/src/maps/osm/OSMBuilding.java b/modules/maps/src/maps/osm/OSMBuilding.java new file mode 100644 index 0000000000000000000000000000000000000000..711b32e1456f6ab26e33018d55279d8641f1503b --- /dev/null +++ b/modules/maps/src/maps/osm/OSMBuilding.java @@ -0,0 +1,22 @@ +package maps.osm; + +import java.util.List; + +/** + A building in OSM space. +*/ +public class OSMBuilding extends OSMWay { + /** + Construct an OSMBuilding. + @param id The ID of the building. + @param ids The IDs of the apex nodes of the building. + */ + public OSMBuilding(Long id, List<Long> ids) { + super(id, ids); + } + + @Override + public String toString() { + return "OSMBuilding: id " + getID(); + } +} diff --git a/modules/maps/src/maps/osm/OSMException.java b/modules/maps/src/maps/osm/OSMException.java new file mode 100644 index 0000000000000000000000000000000000000000..05bc1319f440288b196dd699a009f73848d00dd0 --- /dev/null +++ b/modules/maps/src/maps/osm/OSMException.java @@ -0,0 +1,38 @@ +package maps.osm; + +/** + Exceptions related to OpenStreetMap. +*/ +public class OSMException extends Exception { + /** + Construct an OSMException with no error message. + */ + public OSMException() { + super(); + } + + /** + Construct an OSMException with an error message. + @param msg The error message. + */ + public OSMException(String msg) { + super(msg); + } + + /** + Construct an OSMException with an underlying cause. + @param cause The cause. + */ + public OSMException(Throwable cause) { + super(cause); + } + + /** + Construct an OSMException with an error message and underlying cause. + @param msg The error message. + @param cause The cause. + */ + public OSMException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/maps/src/maps/osm/OSMMap.java b/modules/maps/src/maps/osm/OSMMap.java new file mode 100644 index 0000000000000000000000000000000000000000..8bec39918e5d034ce22e45f8f404fd3ad7829cf7 --- /dev/null +++ b/modules/maps/src/maps/osm/OSMMap.java @@ -0,0 +1,415 @@ +package maps.osm; + +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; + +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import java.io.File; +import java.io.IOException; + +/** + An OpenStreetMap map. +*/ +public class OSMMap { + private static final Collection<String> ROAD_MARKERS = new HashSet<String>(); + + static { + ROAD_MARKERS.add("motorway"); + ROAD_MARKERS.add("motorway_link"); + ROAD_MARKERS.add("trunk"); + ROAD_MARKERS.add("trunk_link"); + ROAD_MARKERS.add("primary"); + ROAD_MARKERS.add("primary_link"); + ROAD_MARKERS.add("secondary"); + ROAD_MARKERS.add("secondary_link"); + ROAD_MARKERS.add("tertiary"); + ROAD_MARKERS.add("unclassified"); + ROAD_MARKERS.add("road"); + ROAD_MARKERS.add("residential"); + ROAD_MARKERS.add("living_street"); + ROAD_MARKERS.add("service"); + ROAD_MARKERS.add("track"); + ROAD_MARKERS.add("services"); + ROAD_MARKERS.add("pedestrian"); + } + + private Map<Long, OSMNode> nodes; + private Map<Long, OSMRoad> roads; + private Map<Long, OSMBuilding> buildings; + + private boolean boundsCalculated; + private double minLat; + private double maxLat; + private double minLon; + private double maxLon; + + /** + Construct an empty map. + */ + public OSMMap() { + boundsCalculated = false; + nodes = new HashMap<Long, OSMNode>(); + roads = new HashMap<Long, OSMRoad>(); + buildings = new HashMap<Long, OSMBuilding>(); + } + + /** + Construct a map from an XML document. + @param doc The document to read. + */ + public OSMMap(Document doc) throws OSMException { + this(); + read(doc); + } + + /** + Construct a map from an XML file. + @param file The file to read. + */ + public OSMMap(File file) throws OSMException, DocumentException, IOException { + this(); + SAXReader reader = new SAXReader(); + Document doc = reader.read(file); + read(doc); + } + + /** + Construct a copy of an OSMMap over a bounded area. + @param other The map to copy. + @param minLat The minimum latitude of the new map. + @param minLon The minimum longitude of the new map. + @param maxLat The maximum latitude of the new map. + @param maxLon The maximum longitude of the new map. + */ + public OSMMap(OSMMap other, double minLat, double minLon, double maxLat, double maxLon) { + this.minLat = minLat; + this.minLon = minLon; + this.maxLat = maxLat; + this.maxLon = maxLon; + boundsCalculated = true; + nodes = new HashMap<Long, OSMNode>(); + roads = new HashMap<Long, OSMRoad>(); + buildings = new HashMap<Long, OSMBuilding>(); + // Copy all nodes inside the bounds + for (OSMNode next : other.nodes.values()) { + double lat = next.getLatitude(); + double lon = next.getLongitude(); + long id = next.getID(); + if (lat >= minLat && lat <= maxLat && lon >= minLon && lon <= maxLon) { + this.nodes.put(id, new OSMNode(id, lat, lon)); + } + } + // Now copy the bits of roads and buildings that do not have missing nodes + for (OSMRoad next : other.roads.values()) { + List<Long> ids = new ArrayList<Long>(next.getNodeIDs()); + for (Iterator<Long> it = ids.iterator(); it.hasNext();) { + Long nextID = it.next(); + if (!nodes.containsKey(nextID)) { + it.remove(); + } + } + if (!ids.isEmpty()) { + roads.put(next.getID(), new OSMRoad(next.getID(), ids)); + } + } + for (OSMBuilding next : other.buildings.values()) { + boolean allFound = true; + for (Long nextID : next.getNodeIDs()) { + if (!nodes.containsKey(nextID)) { + allFound = false; + } + } + if (allFound) { + buildings.put(next.getID(), new OSMBuilding(next.getID(), new ArrayList<Long>(next.getNodeIDs()))); + } + } + } + + /** + Read an XML document and populate this map. + @param doc The document to read. + */ + public void read(Document doc) throws OSMException { + boundsCalculated = false; + nodes = new HashMap<Long, OSMNode>(); + roads = new HashMap<Long, OSMRoad>(); + buildings = new HashMap<Long, OSMBuilding>(); + Element root = doc.getRootElement(); + if (!"osm".equals(root.getName())) { + throw new OSMException("Invalid map file: root element must be 'osm', not " + root.getName()); + } + for (Object next : root.elements("node")) { + Element e = (Element)next; + OSMNode node = processNode(e); + } + for (Object next : root.elements("way")) { + Element e = (Element)next; + processWay(e); + } + } + + /** + Turn this map into XML. + @return A new XML document. + */ + public Document toXML() { + Element root = DocumentHelper.createElement("osm"); + Element bounds = root.addElement("bounds"); + calculateBounds(); + bounds.addAttribute("minlat", String.valueOf(minLat)); + bounds.addAttribute("maxlat", String.valueOf(maxLat)); + bounds.addAttribute("minlon", String.valueOf(minLon)); + bounds.addAttribute("maxlon", String.valueOf(maxLon)); + for (OSMNode next : nodes.values()) { + Element node = root.addElement("node"); + node.addAttribute("id", String.valueOf(next.getID())); + node.addAttribute("lat", String.valueOf(next.getLatitude())); + node.addAttribute("lon", String.valueOf(next.getLongitude())); + } + for (OSMRoad next : roads.values()) { + Element node = root.addElement("way"); + node.addAttribute("id", String.valueOf(next.getID())); + for (Long nextID : next.getNodeIDs()) { + node.addElement("nd").addAttribute("ref", String.valueOf(nextID)); + } + node.addElement("tag").addAttribute("k", "highway").addAttribute("v", "primary"); + } + for (OSMBuilding next : buildings.values()) { + Element node = root.addElement("way"); + node.addAttribute("id", String.valueOf(next.getID())); + for (Long nextID : next.getNodeIDs()) { + node.addElement("nd").addAttribute("ref", String.valueOf(nextID)); + } + node.addElement("tag").addAttribute("k", "building").addAttribute("v", "yes"); + } + return DocumentHelper.createDocument(root); + } + + /** + Get the minimum longitude in this map. + @return The minimum longitude. + */ + public double getMinLongitude() { + calculateBounds(); + return minLon; + } + + /** + Get the maximum longitude in this map. + @return The maximum longitude. + */ + public double getMaxLongitude() { + calculateBounds(); + return maxLon; + } + + /** + Get the centre longitude in this map. + @return The centre longitude. + */ + public double getCentreLongitude() { + calculateBounds(); + return (maxLon + minLon) / 2; + } + + /** + Get the minimum latitude in this map. + @return The minimum latitude. + */ + public double getMinLatitude() { + calculateBounds(); + return minLat; + } + + /** + Get the maximum latitude in this map. + @return The maximum latitude. + */ + public double getMaxLatitude() { + calculateBounds(); + return maxLat; + } + + /** + Get the centre latitude in this map. + @return The centre latitude. + */ + public double getCentreLatitude() { + calculateBounds(); + return (maxLat + minLat) / 2; + } + + /** + Get all nodes in the map. + @return All nodes. + */ + public Collection<OSMNode> getNodes() { + return new HashSet<OSMNode>(nodes.values()); + } + + /** + Remove a node. + @param node The node to remove. + */ + public void removeNode(OSMNode node) { + nodes.remove(node.getID()); + } + + /** + Get a node by ID. + @param id The ID of the node. + @return The node with the given ID or null. + */ + public OSMNode getNode(Long id) { + return nodes.get(id); + } + + /** + Get the nearest node to a point. + @param lat The latitude of the point. + @param lon The longitude of the point. + @return The nearest node. + */ + public OSMNode getNearestNode(double lat, double lon) { + double smallest = Double.MAX_VALUE; + OSMNode best = null; + for (OSMNode next : nodes.values()) { + double d1 = next.getLatitude() - lat; + double d2 = next.getLongitude() - lon; + double d = (d1 * d1) + (d2 * d2); + if (d < smallest) { + best = next; + smallest = d; + } + } + return best; + } + + /** + Replace a node and update all references. + @param old The node to replace. + @param replacement The replacement node. + */ + public void replaceNode(OSMNode old, OSMNode replacement) { + for (OSMRoad r : roads.values()) { + r.replace(old.getID(), replacement.getID()); + } + for (OSMBuilding b : buildings.values()) { + b.replace(old.getID(), replacement.getID()); + } + removeNode(old); + } + + /** + Get all roads. + @return All roads. + */ + public Collection<OSMRoad> getRoads() { + return new HashSet<OSMRoad>(roads.values()); + } + + /** + Remove a road. + @param road The road to remove. + */ + public void removeRoad(OSMRoad road) { + roads.remove(road.getID()); + } + + /** + Get all buildings. + @return All buildings. + */ + public Collection<OSMBuilding> getBuildings() { + return new HashSet<OSMBuilding>(buildings.values()); + } + + /** + Remove a building. + @param building The building to remove. + */ + public void removeBuilding(OSMBuilding building) { + buildings.remove(building.getID()); + } + + private void calculateBounds() { + if (boundsCalculated) { + return; + } + minLat = Double.POSITIVE_INFINITY; + maxLat = Double.NEGATIVE_INFINITY; + minLon = Double.POSITIVE_INFINITY; + maxLon = Double.NEGATIVE_INFINITY; + for (OSMNode node : nodes.values()) { + minLat = Math.min(minLat, node.getLatitude()); + maxLat = Math.max(maxLat, node.getLatitude()); + minLon = Math.min(minLon, node.getLongitude()); + maxLon = Math.max(maxLon, node.getLongitude()); + } + boundsCalculated = true; + } + + private OSMNode processNode(Element e) { + long id = Long.parseLong(e.attributeValue("id")); + double lat = Double.parseDouble(e.attributeValue("lat")); + double lon = Double.parseDouble(e.attributeValue("lon")); + OSMNode node = new OSMNode(id, lat, lon); + nodes.put(id, node); + return node; + } + + private void processWay(Element e) { + long id = Long.parseLong(e.attributeValue("id")); + List<Long> ids = new ArrayList<Long>(); + for (Object next : e.elements("nd")) { + Element nd = (Element)next; + Long nextID = Long.parseLong(nd.attributeValue("ref")); + ids.add(nextID); + } + // Is this way a road or a building? + boolean road = false; + boolean building = false; + for (Object next : e.elements("tag")) { + Element tag = (Element)next; + building = building || tagSignifiesBuilding(tag); + road = road || tagSignifiesRoad(tag); + } + if (building) { + buildings.put(id, new OSMBuilding(id, ids)); + } + else if (road) { + roads.put(id, new OSMRoad(id, ids)); + } + } + + private boolean tagSignifiesRoad(Element tag) { + String key = tag.attributeValue("k"); + String value = tag.attributeValue("v"); + if (!"highway".equals(key)) { + return false; + } + return ROAD_MARKERS.contains(value); + } + + private boolean tagSignifiesBuilding(Element tag) { + String key = tag.attributeValue("k"); + String value = tag.attributeValue("v"); + if ("building".equals(key)) { + return "yes".equals(value); + } + if ("rcr:building".equals(key)) { + return "1".equals(value); + } + return false; + } +} diff --git a/modules/maps/src/maps/osm/OSMMapExtractor.java b/modules/maps/src/maps/osm/OSMMapExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..c6f5bde4d26564ce9e82ded4553ff9b06ff0f335 --- /dev/null +++ b/modules/maps/src/maps/osm/OSMMapExtractor.java @@ -0,0 +1,158 @@ +package maps.osm; + +import javax.swing.JFrame; +import javax.swing.JComponent; + +import java.awt.Point; +import java.awt.Insets; +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseEvent; +import java.awt.event.MouseAdapter; + +import java.io.Writer; +import java.io.File; +import java.io.FileWriter; + +import org.dom4j.Document; +import org.dom4j.io.XMLWriter; +import org.dom4j.io.OutputFormat; + +/** + This class extracts a portion of an OSMMap. +*/ +public class OSMMapExtractor extends MouseAdapter { + private static final int VIEWER_SIZE = 500; + private static final Color DRAG_COLOUR = new Color(128, 128, 128, 64); + + private JComponent glass; + private Point press; + private Point drag; + private Point release; + + private OSMMap map; + private OSMMapViewer viewer; + private Writer out; + + /** + Construct an OSMMapExtractor. + @param map The map. + @param viewer The viewer. + @param out The writer to write extracted data to. + */ + public OSMMapExtractor(OSMMap map, OSMMapViewer viewer, Writer out) { + this.map = map; + this.viewer = viewer; + this.out = out; + this.glass = new DragGlass(); + } + + /** + Start the OSMMapExtractor. + @param args Command line arguments: source target. + */ + public static void main(String[] args) { + try { + OSMMap map = new OSMMap(new File(args[0])); + Writer out = new FileWriter(new File(args[1])); + OSMMapViewer viewer = new OSMMapViewer(map); + + OSMMapExtractor extractor = new OSMMapExtractor(map, viewer, out); + viewer.addMouseListener(extractor); + viewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + + JFrame frame = new JFrame(); + frame.setGlassPane(extractor.getGlass()); + frame.setContentPane(viewer); + frame.pack(); + frame.setVisible(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (Exception e) { + e.printStackTrace(); + } + // CHECKSTYLE:ON:IllegalCatch + } + + /** + Get a glass component for drawing the selection overlay. + @return A glass component. + */ + public JComponent getGlass() { + return glass; + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + Point p = e.getPoint(); + Insets insets = viewer.getInsets(); + p.translate(-insets.left, -insets.top); + press = new Point(p); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + Point p = e.getPoint(); + Insets insets = viewer.getInsets(); + p.translate(-insets.left, -insets.top); + drag = new Point(p); + glass.repaint(); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + Point p = e.getPoint(); + Insets insets = viewer.getInsets(); + p.translate(-insets.left, -insets.top); + release = new Point(p); + drag = null; + write(); + } + } + + private void write() { + double pressLat = viewer.getLatitude(press.y); + double pressLon = viewer.getLongitude(press.x); + double releaseLat = viewer.getLatitude(release.y); + double releaseLon = viewer.getLongitude(release.x); + try { + OSMMap newMap = new OSMMap(map, + Math.min(pressLat, releaseLat), + Math.min(pressLon, releaseLon), + Math.max(pressLat, releaseLat), + Math.max(pressLon, releaseLon)); + Document d = newMap.toXML(); + XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); + writer.write(d); + writer.flush(); + writer.close(); + System.out.println("Wrote map"); + } + // CHECKSTYLE:OFF:IllegalCatch + catch (Exception ex) { + ex.printStackTrace(); + } + // CHECKSTYLE:ON:IllegalCatch + } + + private class DragGlass extends JComponent { + public void paintComponent(Graphics g) { + if (drag == null) { + return; + } + g.setColor(DRAG_COLOUR); + int x = Math.min(press.x, drag.x); + int y = Math.max(press.y, drag.y); + int width = (int)Math.abs(press.x - drag.x); + int height = (int)Math.abs(press.y - drag.y); + g.fillRect(x, y, width, height); + } + } +} diff --git a/modules/maps/src/maps/osm/OSMMapViewer.java b/modules/maps/src/maps/osm/OSMMapViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..396041371b35121ae08ceafb1b3a7943755ef55c --- /dev/null +++ b/modules/maps/src/maps/osm/OSMMapViewer.java @@ -0,0 +1,130 @@ +package maps.osm; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JComponent; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.PanZoomListener; + +/** + A component for viewing OSM maps. +*/ +public class OSMMapViewer extends JComponent { + private OSMMap map; + private ScreenTransform transform; + private PanZoomListener panZoom; + + /** + Create an OSMMapViewer. + */ + public OSMMapViewer() { + this(null); + } + + /** + Create an OSMMapViewer. + @param map The map to view. + */ + public OSMMapViewer(final OSMMap map) { + panZoom = new PanZoomListener(this); + setMap(map); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Point p = e.getPoint(); + double lon = transform.screenToX(p.x); + double lat = transform.screenToY(p.y); + OSMNode node = map.getNearestNode(lat, lon); + System.out.println("Click at " + lat + ", " + lon); + System.out.println("Nearest node: " + node); + } + } + }); + } + + /** + Set the map. + @param map The new map to view. + */ + public void setMap(OSMMap map) { + this.map = map; + transform = null; + if (map != null) { + transform = new ScreenTransform(map.getMinLongitude(), map.getMinLatitude(), map.getMaxLongitude(), map.getMaxLatitude()); + } + panZoom.setScreenTransform(transform); + } + + /** + Get the latitude of a screen coordinate. + @param y The screen coordinate. + @return The latitude at that coordinate. + */ + public double getLatitude(int y) { + return transform.screenToY(y); + } + + /** + Get the longitude of a screen coordinate. + @param x The screen coordinate. + @return The longitude at that coordinate. + */ + public double getLongitude(int x) { + return transform.screenToX(x); + } + + @Override + public void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + if (map == null) { + return; + } + Insets insets = getInsets(); + int width = getWidth() - insets.left - insets.right; + int height = getHeight() - insets.top - insets.bottom; + Graphics2D g = (Graphics2D)graphics.create(insets.left, insets.top, width + 1 , height + 1); + transform.rescale(width, height); + g.setColor(Color.black); + for (OSMNode next : map.getNodes()) { + int x = transform.xToScreen(next.getLongitude()); + int y = transform.yToScreen(next.getLatitude()); + g.drawLine(x - 1, y - 1, x + 1, y + 1); + g.drawLine(x + 1, y - 1, x - 1, y + 1); + } + for (OSMRoad next : map.getRoads()) { + int lastX = -1; + int lastY = -1; + for (Long nodeID : next.getNodeIDs()) { + OSMNode node = map.getNode(nodeID); + int x = transform.xToScreen(node.getLongitude()); + int y = transform.yToScreen(node.getLatitude()); + if (lastX != -1) { + g.drawLine(lastX, lastY, x, y); + } + lastX = x; + lastY = y; + } + } + g.setColor(Color.blue); + for (OSMBuilding next : map.getBuildings()) { + int lastX = -1; + int lastY = -1; + for (Long nodeID : next.getNodeIDs()) { + OSMNode node = map.getNode(nodeID); + int x = transform.xToScreen(node.getLongitude()); + int y = transform.yToScreen(node.getLatitude()); + if (lastX != -1) { + g.drawLine(lastX, lastY, x, y); + } + lastX = x; + lastY = y; + } + } + } +} diff --git a/modules/maps/src/maps/osm/OSMNode.java b/modules/maps/src/maps/osm/OSMNode.java new file mode 100644 index 0000000000000000000000000000000000000000..5bf27c0629d01c19f13f3a39006ce1db2be3f4ff --- /dev/null +++ b/modules/maps/src/maps/osm/OSMNode.java @@ -0,0 +1,42 @@ +package maps.osm; + +/** + An OpenStreetMap node. + */ +public class OSMNode extends OSMObject { + private double lat; + private double lon; + + /** + Construct an OSMNode. + @param id The ID of the node. + @param lat The latitude of the node. + @param lon The longitude of the node. + */ + public OSMNode(long id, double lat, double lon) { + super(id); + this.lat = lat; + this.lon = lon; + } + + /** + Get the latitude of this node in degrees. + @return The latitude in degrees. + */ + public double getLatitude() { + return lat; + } + + /** + Get the longitude of this node in degrees. + @return The longitude in degrees. + */ + public double getLongitude() { + return lon; + } + + @Override + public String toString() { + return "OSMNode (" + getID() + ") at lat " + lat + ", lon " + lon; + } +} diff --git a/modules/maps/src/maps/osm/OSMObject.java b/modules/maps/src/maps/osm/OSMObject.java new file mode 100644 index 0000000000000000000000000000000000000000..2682f8dc88a35572ee5b952cb37a269b40472847 --- /dev/null +++ b/modules/maps/src/maps/osm/OSMObject.java @@ -0,0 +1,24 @@ +package maps.osm; + +/** + Abstract base class for OpenStreetMap objects. +*/ +public abstract class OSMObject { + private long id; + + /** + Construct an OSMObject. + @param id The ID of the object. + */ + public OSMObject(long id) { + this.id = id; + } + + /** + Get the ID of this object. + @return The ID of the object. + */ + public long getID() { + return id; + } +} diff --git a/modules/maps/src/maps/osm/OSMRoad.java b/modules/maps/src/maps/osm/OSMRoad.java new file mode 100644 index 0000000000000000000000000000000000000000..03c53d4451e84c1f05b4ae712c81676390c4762f --- /dev/null +++ b/modules/maps/src/maps/osm/OSMRoad.java @@ -0,0 +1,17 @@ +package maps.osm; + +import java.util.List; + +/** + An OpenStreetMap road. + */ +public class OSMRoad extends OSMWay { + /** + Construct an OSMRoad. + @param id The ID of the road. + @param ids The IDs of the apex nodes of the road. + */ + public OSMRoad(Long id, List<Long> ids) { + super(id, ids); + } +} diff --git a/modules/maps/src/maps/osm/OSMWay.java b/modules/maps/src/maps/osm/OSMWay.java new file mode 100644 index 0000000000000000000000000000000000000000..c596ac7d61998eb6ce31acde89b9d7010bf7deca --- /dev/null +++ b/modules/maps/src/maps/osm/OSMWay.java @@ -0,0 +1,47 @@ +package maps.osm; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +/** + An OSM way. +*/ +public abstract class OSMWay extends OSMObject { + private List<Long> ids; + + /** + Construct an OSMWay. + @param id The ID of the way. + @param ids The IDs of the nodes of the way. + */ + public OSMWay(Long id, List<Long> ids) { + super(id); + this.ids = ids; + } + + /** + Get the IDs of the way nodes. + @return The IDs of the nodes of this way. + */ + public List<Long> getNodeIDs() { + return new ArrayList<Long>(ids); + } + + /** + Set the IDs of the way nodes. + @param newIDs The new IDs of the nodes of this way. + */ + public void setNodeIDs(List<Long> newIDs) { + ids = newIDs; + } + + /** + Replace a node ID in this way. + @param oldID The old node ID. + @param newID The new node ID. + */ + public void replace(Long oldID, Long newID) { + Collections.replaceAll(ids, oldID, newID); + } +} diff --git a/modules/maps/src/maps/osm/debug/OSMWayShapeInfo.java b/modules/maps/src/maps/osm/debug/OSMWayShapeInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..7520c2a4fa885a3743225cfcae6416bba463e720 --- /dev/null +++ b/modules/maps/src/maps/osm/debug/OSMWayShapeInfo.java @@ -0,0 +1,108 @@ +package maps.osm.debug; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.Polygon; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import java.util.List; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.ShapeDebugFrame; +import rescuecore2.misc.gui.DrawingTools; + +import maps.osm.OSMWay; +import maps.osm.OSMMap; +import maps.osm.OSMNode; + +/** + A ShapeInfo that knows how to draw OSMWays. +*/ +public class OSMWayShapeInfo extends ShapeDebugFrame.ShapeInfo { + private OSMWay way; + private OSMMap map; + private Color colour; + private boolean drawEdgeDirections; + private Rectangle2D bounds; + + /** + Create a new OSMWayShapeInfo. + @param way The way to draw. + @param map The map the way is part of. + @param name The name of the way. + @param colour The colour to draw the way. + @param drawEdgeDirections Whether to draw edge directions or not. + */ + public OSMWayShapeInfo(OSMWay way, OSMMap map, String name, Color colour, boolean drawEdgeDirections) { + super(way, name); + this.way = way; + this.map = map; + this.colour = colour; + this.drawEdgeDirections = drawEdgeDirections; + if (way != null) { + bounds = findBounds(); + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (way == null) { + return null; + } + List<Long> points = way.getNodeIDs(); + int n = points.size(); + int[] xs = new int[n]; + int[] ys = new int[n]; + int i = 0; + for (long next : points) { + xs[i] = transform.xToScreen(map.getNode(next).getLongitude()); + ys[i] = transform.yToScreen(map.getNode(next).getLatitude()); + ++i; + } + Polygon p = new Polygon(xs, ys, n); + if (colour != null) { + g.setColor(colour); + g.draw(p); + if (drawEdgeDirections) { + for (i = 1; i < n; ++i) { + DrawingTools.drawArrowHeads(xs[i - 1], ys[i - 1], xs[i], ys[i], g); + } + } + } + return p; + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (colour != null) { + g.setColor(colour); + g.drawRect(0, 0, width - 1, height - 1); + } + } + + @Override + public Rectangle2D getBoundsShape() { + return bounds; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return null; + } + + private Rectangle2D findBounds() { + double xMin = Double.POSITIVE_INFINITY; + double xMax = Double.NEGATIVE_INFINITY; + double yMin = Double.POSITIVE_INFINITY; + double yMax = Double.NEGATIVE_INFINITY; + for (long next : way.getNodeIDs()) { + OSMNode n = map.getNode(next); + xMin = Math.min(xMin, n.getLongitude()); + xMax = Math.max(xMax, n.getLongitude()); + yMin = Math.min(yMin, n.getLatitude()); + yMax = Math.max(yMax, n.getLatitude()); + } + return new Rectangle2D.Double(xMin, yMin, xMax - xMin, yMax - yMin); + } +} diff --git a/modules/maps/src/maps/validate/GMLConnectivityValidator.java b/modules/maps/src/maps/validate/GMLConnectivityValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..235f855695d295be65df4fb275ef2212f0955097 --- /dev/null +++ b/modules/maps/src/maps/validate/GMLConnectivityValidator.java @@ -0,0 +1,143 @@ +package maps.validate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; + +import maps.gml.GMLBuilding; +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLMap; +import maps.gml.GMLRoad; +import maps.gml.GMLShape; + +/** + * Checks if a GML map is fully connected and if all individual shaped are + * correctly connected to each other (i.e. there are no dangling or no one-way + * connections). + */ +public class GMLConnectivityValidator implements MapValidator<GMLMap> { + private GMLMap map = null; + + @Override + public Collection<ValidationError> validate(GMLMap mmap) { + this.map = mmap; + List<ValidationError> errors = new LinkedList<ValidationError>(); + + // Check if all shapes are connected correctly (no dangling connections, + // etc...) + Set<GMLShape> toBeChecked = new HashSet<GMLShape>(); + for (GMLShape shape : map.getAllShapes()) { + errors.addAll(validateShape(shape)); + if (shape instanceof GMLBuilding || shape instanceof GMLRoad) { + toBeChecked.add(shape); + } + } + + Queue<GMLShape> open = new LinkedList<GMLShape>(); + GMLShape first = toBeChecked.iterator().next(); + open.add(first); + + // check for connectivity (only simple connectivity needs to be checked, + // as + // we made sure that there are no one-way connections + while (!open.isEmpty()) { + GMLShape next = open.remove(); + toBeChecked.remove(next); + for (GMLShape n : getNeigbours(next)) { + if (toBeChecked.contains(n)) { + open.add(n); + } + if (!(n instanceof GMLBuilding || n instanceof GMLRoad)) { + String message = "Can reach non-building, non-road shape " + + n.getID(); + errors.add(new ValidationError(next.getID(), message)); + + } + } + } + + if (!toBeChecked.isEmpty()) { + for (GMLShape unreachable : toBeChecked) { + String message = "The map is not fully connected. Shape cannot be reached from " + + first.getID(); + errors.add(new ValidationError(unreachable.getID(), message)); + } + } + + return errors; + } + + /** + * Check if all connections to neighbours are reflexive. + * + * @param shape + * @return + */ + private Collection<ValidationError> validateShape(GMLShape shape) { + List<ValidationError> errors = new LinkedList<ValidationError>(); + + for (GMLDirectedEdge e : shape.getEdges()) { + if (shape.hasNeighbour(e)) { + int nId = shape.getNeighbour(e); + GMLShape neighbour = map.getShape(nId); + + if (neighbour == null) { + String message = "Connection to nonexisting id " + nId + + " via Edge " + e.getEdge().getID(); + errors.add(new ValidationError(shape.getID(), message)); + } + else if (neighbour == shape) { + String message = "Shape is connected to itself via Edge" + + e.getEdge().getID(); + errors.add(new ValidationError(shape.getID(), message)); + } + else { + GMLShape backRef = null; + try { + if (neighbour.hasNeighbour(e.getEdge())) { + backRef = map.getShape(neighbour.getNeighbour(e + .getEdge())); + } + if (backRef != shape) { + String message = "Connection to " + neighbour.getID() + + " via Edge " + e.getEdge().getID() + + " is not reflexive."; + errors.add(new ValidationError(shape.getID(), message)); + } + } + catch (IllegalArgumentException ex) { + String message = "Neigbour " + neighbour.getID() + + " does not share Edge " + e.getEdge().getID(); + errors.add(new ValidationError(shape.getID(), message)); + } + } + } + } + + return errors; + } + + /** + * Get all shapes that a shape is connected to. + * @param shape + * @return + */ + private Collection<GMLShape> getNeigbours(GMLShape shape) { + Collection<GMLShape> result = new ArrayList<GMLShape>(); + for (GMLDirectedEdge edge : shape.getEdges()) { + if (shape.hasNeighbour(edge)) { + GMLShape n = map.getShape(shape.getNeighbour(edge)); + if (n != null) { + result.add(n); + } + } + } + + return result; + } + +} diff --git a/modules/maps/src/maps/validate/GMLMapValidator.java b/modules/maps/src/maps/validate/GMLMapValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..fd9df5b092fb2ca21320203e1f87e5d09a09a9e1 --- /dev/null +++ b/modules/maps/src/maps/validate/GMLMapValidator.java @@ -0,0 +1,61 @@ +package maps.validate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import maps.MapException; +import maps.MapReader; +import maps.gml.GMLMap; + +/** + * Load a map and check it for errors. + * + */ +public final class GMLMapValidator { + private static List<MapValidator<GMLMap>> validators; + + static { + validators = new ArrayList<MapValidator<GMLMap>>(); + + validators.add(new GMLConnectivityValidator()); + validators.add(new GMLShapeValidator()); + validators.add(new GMLTraversabilityValidator()); + } + + private GMLMapValidator() {} + + /** + * Returns a list of default MapValidators to use for GML maps. + @return List of default MapValidators. + */ + public static List<MapValidator<GMLMap>> getDefaultValidators() { + return new ArrayList<MapValidator<GMLMap>>(validators); + } + + /** + * @param args The command line arguments. + */ + public static void main(String[] args) { + try { + GMLMap map = (GMLMap)MapReader.readMap(args[0]); + boolean hasErrors = true; + for (MapValidator<GMLMap> val : validators) { + Collection<ValidationError> errors = val.validate(map); + for (ValidationError e : errors) { + System.err.println(e); + hasErrors = true; + } + } + if (!hasErrors) { + System.out.println("No errors have been found."); + } + + } + catch (MapException e) { + e.printStackTrace(); + } + + } + +} diff --git a/modules/maps/src/maps/validate/GMLShapeValidator.java b/modules/maps/src/maps/validate/GMLShapeValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..4848e92be554e9a70ccf2acb8bab29eae960921a --- /dev/null +++ b/modules/maps/src/maps/validate/GMLShapeValidator.java @@ -0,0 +1,69 @@ +package maps.validate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import maps.gml.GMLMap; +import maps.gml.GMLShape; + +import com.vividsolutions.jts.geom.Geometry; + +/** + Validate the correctness of basic shape properties. + @author goebelbe + */ +public class GMLShapeValidator implements MapValidator<GMLMap> { + + @Override + public Collection<ValidationError> validate(GMLMap map) { + List<ValidationError> errors = new ArrayList<ValidationError>(); + List<Geometry> polygons = new ArrayList<Geometry>(); + List<GMLShape> shapes = new ArrayList<GMLShape>(map.getAllShapes()); + + for (GMLShape shape : shapes) { + try { + Geometry polygon = checkShape(shape); + polygons.add(polygon); + } + catch (ValidationException e) { + errors.add(e.getError()); + polygons.add(null); + } + } + + for (int i = 0; i < polygons.size(); i++) { + Geometry s1 = polygons.get(i); + if (s1 == null) { + continue; + } + for (int j = i + 1; j < polygons.size(); j++) { + Geometry s2 = polygons.get(j); + if (s2 != null && s1.intersects(s2) && !s1.touches(s2)) { + int s1Id = shapes.get(i).getID(); + int s2Id = shapes.get(j).getID(); + String message = " Shape overlaps with shape " + s2Id; + errors.add(new ValidationError(s1Id, message)); + } + } + } + return errors; + } + + /** + Check if the given shape is correct. + @param shape + @return + */ + private static Geometry checkShape(GMLShape shape) throws ValidationException { + Geometry polygon = JTSTools.shapeToPolygon(shape); + if (!polygon.isValid()) { + throw new ValidationException(shape.getID(), "invalid shape"); + } + if (!polygon.contains(polygon.getCentroid())) { + throw new ValidationException(shape.getID(), "Shape doesn't contain centroid."); + } + return polygon; + } + +} diff --git a/modules/maps/src/maps/validate/GMLTraversabilityValidator.java b/modules/maps/src/maps/validate/GMLTraversabilityValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..3aabbaddacb6be1a89194d18091e8ab85f0c7b85 --- /dev/null +++ b/modules/maps/src/maps/validate/GMLTraversabilityValidator.java @@ -0,0 +1,198 @@ +package maps.validate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLMap; +import maps.gml.GMLRoad; +import maps.gml.GMLShape; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.operation.linemerge.LineSequencer; + +/** + * Validator to check if the shapes of the map are traversable. + * + * For all shaped we check if they can be entered via their entrances. + * + * For road we also check if each entrance can reach the other ones via this + * road. + * + */ +public class GMLTraversabilityValidator implements MapValidator<GMLMap> { + private static final double MIN_ROAD_WIDTH = 1.0; + private static final double SHAPE_PADDING = 0.01; + + @Override + public Collection<ValidationError> validate(GMLMap map) { + Collection<ValidationError> errors = new ArrayList<ValidationError>(); + for (GMLShape shape : map.getRoads()) { + ValidationError error = checkTraversability(shape, MIN_ROAD_WIDTH); + if (error != null) { + errors.add(error); + } + } + for (GMLShape shape : map.getBuildings()) { + ValidationError error = checkTraversability(shape, MIN_ROAD_WIDTH); + if (error != null) { + errors.add(error); + } + } + return errors; + } + + /** + * Check if this shape can be traversed by an agent of width + * <tt>minWidth</tt>. + * + * @param shape + * @param agentWidth + * @return + */ + private ValidationError checkTraversability(GMLShape shape, double minWidth) { + // To check for traversability, we shrink the non-traversable edges + // of the shape by the radius of the agent. + // We then check, if all entrance edges are part of the same part + // of the resulting polygon + + try { + Geometry polygon = JTSTools.shapeToPolygon(shape); + if (!polygon.isValid()) { + return new ValidationError(shape.getID(), "invalid shape"); + } + Geometry boundary = impassableLines(shape); + Geometry buffer = boundary.buffer(((double) minWidth) / 2); + Geometry result = polygon.difference(buffer); + // make sure the intersection tests succeed + result = result.buffer(SHAPE_PADDING); + + Coordinate centroid = JTSTools.pointToCoordinate(shape.getCentroid()); + + // Build list of adjacent entrance edges + List<GMLDirectedEdge> edges = shape.getEdges(); + List<List<GMLDirectedEdge>> entrances = new ArrayList<List<GMLDirectedEdge>>(); + List<GMLDirectedEdge> entrance = new ArrayList<GMLDirectedEdge>(); + for (GMLDirectedEdge e : edges) { + if (shape.hasNeighbour(e)) { + entrance.add(e); + //Check if we have a line of sight to the centroid + LineString edge = JTSTools.edgeToLine(e); + Coordinate edgeCenter = edge.getCentroid().getCoordinate(); + Coordinate[] coords = new Coordinate[]{centroid, edgeCenter}; + LineString lineOfSight = JTSTools.getFactory().createLineString(coords); + if (lineOfSight.intersects(boundary)) { + String message = "Edge " + e.getEdge().getID() + + " has no line of sight to shape center."; + return new ValidationError(shape.getID(), message); + } + } + else { + if (!entrance.isEmpty()) { + entrances.add(entrance); + } + entrance = new ArrayList<GMLDirectedEdge>(); + } + } + if (!entrance.isEmpty()) { + // Merge first and last sequences if neccessary + if (shape.hasNeighbour(edges.get(0)) && !entrances.isEmpty()) { + entrances.get(0).addAll(entrance); + } + else { + entrances.add(entrance); + } + } + + // Check in which part of the polygon the entrances lie + GMLDirectedEdge firstEdge = null; + int firstPolygon = -1; + + for (List<GMLDirectedEdge> etr : entrances) { + int polyIndex = -1; + for (GMLDirectedEdge e : etr) { + polyIndex = findPolygonPartOfEdge(e, result); + if (polyIndex != -1) { + break; + } + } + + if (polyIndex == -1) { + // Entrance edge no longer in polygon + String message = "Edge is too narrow to pass through."; + return new ValidationError(etr.get(0).getEdge().getID(), + message); + } + if (firstEdge == null) { + firstEdge = etr.get(0); + firstPolygon = polyIndex; + } + else if (firstPolygon != polyIndex + && (shape instanceof GMLRoad)) { + // Only check traversability for roads + String message = "Can't reach edge " + + firstEdge.getEdge().getID() + " from " + + etr.get(0).getEdge().getID(); + return new ValidationError(shape.getID(), message); + } + + } + + return null; + } + catch (ValidationException e) { + return e.getError(); + } + } + + /** + * Find the index of the subgeometry the given edge is part of. Return -1 if + * the edge is not contained in the geometry at all. + * @param edge + * @param geom + * @return + */ + private static int findPolygonPartOfEdge(GMLDirectedEdge edge, Geometry geom) { + for (int i = 0; i < geom.getNumGeometries(); i++) { + if (edgePartOfPolygon(edge, geom.getGeometryN(i))) { + return i; + } + } + return -1; + } + + /** + * Checks if an edge is part (i.e intersects) of a given polygon. + * @param edge + * @param polygon + * @return + */ + private static boolean edgePartOfPolygon(GMLDirectedEdge edge, + Geometry polygon) { + // No idea if this works... + return polygon.intersects(JTSTools.edgeToLine(edge)); + } + + /** + * Return a LineString or MultiLineString of the impassable edges of a + * shape. + * @param shape + * @return + */ + private static Geometry impassableLines(GMLShape shape) { + LineSequencer seq = new LineSequencer(); + for (GMLDirectedEdge e : shape.getEdges()) { + if (!shape.hasNeighbour(e)) { + Coordinate[] coord = new Coordinate[2]; + coord[0] = JTSTools.nodeToCoordinate(e.getStartNode()); + coord[1] = JTSTools.nodeToCoordinate(e.getEndNode()); + seq.add(JTSTools.getFactory().createLineString(coord)); + } + } + + return seq.getSequencedLineStrings(); + } +} diff --git a/modules/maps/src/maps/validate/JTSTools.java b/modules/maps/src/maps/validate/JTSTools.java new file mode 100644 index 0000000000000000000000000000000000000000..3a7c62fa8d618f4d91b6dbb4dfa7856886c4453b --- /dev/null +++ b/modules/maps/src/maps/validate/JTSTools.java @@ -0,0 +1,114 @@ +package maps.validate; + +import maps.gml.GMLDirectedEdge; +import maps.gml.GMLNode; +import maps.gml.GMLShape; +import rescuecore2.misc.geometry.Point2D; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.CoordinateList; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.geom.LinearRing; +import com.vividsolutions.jts.operation.linemerge.LineSequencer; +import com.vividsolutions.jts.util.AssertionFailedException; + +/** + * This class provides some conversion functions from GML map classes to JTS + * Geometry classes. + */ +public final class JTSTools { + private static GeometryFactory geomFactory = new GeometryFactory(); + + private JTSTools() { + }; + + /** + * Create a LineString from a GMLDirectedEdge. + * @param edge + * The edge to convert. + * @return LineString or MultLineString. + */ + public static LineString edgeToLine(GMLDirectedEdge edge) { + Coordinate[] coord = new Coordinate[2]; + coord[0] = nodeToCoordinate(edge.getStartNode()); + coord[1] = nodeToCoordinate(edge.getEndNode()); + return geomFactory.createLineString(coord); + } + + /** + * Create a JTS Polygon form a GMLShape. + * @param shape + * The shape to convert. + * @return Polygon geometry. + * @throws ValidationException + */ + public static Geometry shapeToPolygon(GMLShape shape) + throws ValidationException { + LineSequencer seq = new LineSequencer(); + for (GMLDirectedEdge e : shape.getEdges()) { + Coordinate[] coord = new Coordinate[2]; + coord[0] = nodeToCoordinate(e.getStartNode()); + coord[1] = nodeToCoordinate(e.getEndNode()); + if (coord[0].equals(coord[1])) { + throw new ValidationException(e.getEdge().getID(), + "Zero length edge."); + } + seq.add(geomFactory.createLineString(coord)); + } + + try { + if (!seq.isSequenceable()) { + throw new ValidationException(shape.getID(), + "Outline is not a single line."); + } + } + catch (AssertionFailedException e) { + throw new ValidationException(shape.getID(), + "Could not get outline: " + e.getMessage()); + } + Geometry line = seq.getSequencedLineStrings(); + + CoordinateList coord = new CoordinateList(line.getCoordinates()); + coord.closeRing(); + + // CHECKSTYLE:OFF:MagicNumber + if (coord.size() < 4) { + // CHECKSTYLE:ON:MagicNumber + throw new ValidationException(shape.getID(), "Degenerate Shape"); + } + + Geometry ring = geomFactory.createLinearRing(coord.toCoordinateArray()); + return geomFactory.createPolygon((LinearRing) ring, null); + } + + /** + * Create a Coordinate from a GMLNode. + * @param node + * Node to convert. + * @return Coordinate object. + */ + public static Coordinate nodeToCoordinate(GMLNode node) { + return new Coordinate(node.getX(), node.getY()); + } + + /** + * Create a Coordinate from a Point2D. + * @param point + * Point to convert. + * @return Coordinate object. + */ + public static Coordinate pointToCoordinate(Point2D point) { + return new Coordinate(point.getX(), point.getY()); + } + + /** + * Get the default GeometryFactory. + * @return The default GeometryFactory. + */ + public static GeometryFactory getFactory() { + return geomFactory; + } + +} diff --git a/modules/maps/src/maps/validate/MapValidator.java b/modules/maps/src/maps/validate/MapValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..679ed6433e9a44a13f7a569bfe75aa7e25842a8f --- /dev/null +++ b/modules/maps/src/maps/validate/MapValidator.java @@ -0,0 +1,20 @@ +package maps.validate; + +import java.util.Collection; + +import maps.Map; + +/** + * Interface for classes that can validate maps. + * + * @param <T> + */ +public interface MapValidator<T extends Map> { + + /** + * Check if the given map is valid. If not, return a collection of errors. + * @param map Map that should be checked. + * @return Collection of errors. + */ + Collection<ValidationError> validate(T map); +} diff --git a/modules/maps/src/maps/validate/ValidationError.java b/modules/maps/src/maps/validate/ValidationError.java new file mode 100644 index 0000000000000000000000000000000000000000..7ac295f70d902ef3d3e4e28148ab7c9080ed6d40 --- /dev/null +++ b/modules/maps/src/maps/validate/ValidationError.java @@ -0,0 +1,41 @@ +package maps.validate; + +/** + * This class encapsulates a validation error. It contains the object id and an + * error message. + */ +public class ValidationError { + private int id; + private String message; + + /** + * Create a new ValidationError object. + * @param id The id of the GMLObject containing the error. + * @param message The error message. + */ + public ValidationError(int id, String message) { + this.id = id; + this.message = message; + } + + /** + * Get the id of the object this error refers to. + * @return The id of the GMLObject containing the error. + */ + public int getId() { + return id; + } + + /** + * Get the error message of this ValidationError. + * @return The error message. + */ + public String getMessage() { + return message; + } + + @Override + public String toString() { + return "Error in object " + id + ": " + message; + } +} diff --git a/modules/maps/src/maps/validate/ValidationException.java b/modules/maps/src/maps/validate/ValidationException.java new file mode 100644 index 0000000000000000000000000000000000000000..d3f3f3d999c1c1843d20f493eada0def3d740add --- /dev/null +++ b/modules/maps/src/maps/validate/ValidationException.java @@ -0,0 +1,29 @@ +package maps.validate; + +/** + * Exception that is raised when an error in a map is detected during + * validation. + * + */ +public class ValidationException extends Exception { + private ValidationError error; + + /** + * Create a new ValidationException. + * @param id The id of the GMLObject containing the error. + * @param message The error message. + */ + public ValidationException(int id, String message) { + super(message); + error = new ValidationError(id, message); + } + + /** + * Get the underlying ValidationError for this exception. + * @return The ValidationError causing this exception. + */ + public ValidationError getError() { + return error; + } + +} diff --git a/modules/misc/src/misc/DamageType.java b/modules/misc/src/misc/DamageType.java new file mode 100755 index 0000000000000000000000000000000000000000..140c1ebdc8bdff00cc434f7c2a68cbdb4bffaf6e --- /dev/null +++ b/modules/misc/src/misc/DamageType.java @@ -0,0 +1,94 @@ +package misc; + +import rescuecore2.config.Config; + +import java.util.Random; + +import org.uncommons.maths.random.GaussianGenerator; +import org.uncommons.maths.number.NumberGenerator; + +/** + Container for information about different damage types. + */ +/* + * Implementation of Refuge Bed Capacity + * @author Farshid Faraji + * May 2020 During Covid-19 :-))) + * */ +public class DamageType { + private String type; + private double k; + private double l; + private NumberGenerator<Double> noise; + + private double damage; + + /** + Construct a DamageType. + @param type The name of this type. + @param config The system configuration. + @param Random sequence proprietary. + */ + public DamageType(String type, Config config, Random random) { + this.type = type; + k = config.getFloatValue("misc.injury." + type + ".k"); + l = config.getFloatValue("misc.injury." + type + ".l"); + double mean = config.getFloatValue("misc.injury." + type + ".noise.mean"); + double sd = config.getFloatValue("misc.injury." + type + ".noise.sd"); + noise = new GaussianGenerator(mean, sd, random); + damage = 0; + } + + /** + Get the type name. + @return The type name. + */ + public String getType() { + return type; + } + + /** + Compute damage progression for this type. + @return The new damage. + */ + public double progress() { + if (damage <= 0) { + return damage; + } + double n = noise.nextValue(); + damage = damage + (k * damage * damage) + l + n; + return damage; + } + + public double progressInRefuge() { + if (damage <= 0) { + return damage; + } + double n = noise.nextValue(); + damage = damage - (k * damage * damage) - l - (2*n); + return damage; + } + /** + Get the current damage. + @return The current damage. + */ + public double getDamage() { + return damage; + } + + /** + Set the current damage. + @param d The current damage. + */ + public void setDamage(double d) { + damage = d; + } + + /** + Add some damage. + @param d The amount to add. + */ + public void addDamage(double d) { + damage += d; + } +} diff --git a/modules/misc/src/misc/HumanAttributes.java b/modules/misc/src/misc/HumanAttributes.java new file mode 100755 index 0000000000000000000000000000000000000000..71fb6cc58ff08c66d996aba77cba5fa088c735c9 --- /dev/null +++ b/modules/misc/src/misc/HumanAttributes.java @@ -0,0 +1,170 @@ +package misc; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.EntityID; + +import rescuecore2.standard.entities.Human; + +import java.util.Random; + +/** + Class for holding information about humans. + */ +/* + * Implementation of Refuge Bed Capacity + * @author Farshid Faraji + * May 2020 During Covid-19 :-))) + * */ +public class HumanAttributes { + private Human human; + private EntityID id; + private DamageType damageFire; + private DamageType damageCollapse; + private DamageType damageBury; + private Random random; + + /** + Construct a HumanAttributes object that wraps a Human. + @param h The Human to wrap. + @param config The system configuration. + */ + public HumanAttributes(Human h, Config config) { + this.human = h; + this.id = h.getID(); + // Generate Random for each Human + this.random = new Random(config.getRandom().nextLong()); + damageFire = new DamageType("fire", config, random); + damageCollapse = new DamageType("collapse", config, random); + damageBury = new DamageType("bury", config, random); + } + + /** + Get the ID of the wrapped human. + @return The human ID. + */ + public EntityID getID() { + return id; + } + + /** + Get the wrapped human. + @return The wrapped human. + */ + public Human getHuman() { + return human; + } + + /** + Get the random sequence of the wrapped human. + @return The random sequence. + */ + public Random getRandom(){ + return random; + } + /** + Add some collapse damage. + @param d The amount of damage to add. + */ + public void addCollapseDamage(double d) { + damageCollapse.addDamage(d); + } + + /** + Get the amount of collapse damage this human has. + @return The amount of collapse damage. + */ + public double getCollapseDamage() { + return damageCollapse.getDamage(); + } + + /** + Set the amount of collapse damage this human has. + @param d The new collapse damage. + */ + public void setCollapseDamage(double d) { + damageCollapse.setDamage(d); + } + + /** + Add some buriedness damage. + @param d The amount of damage to add. + */ + public void addBuriednessDamage(double d) { + damageBury.addDamage(d); + } + + /** + Get the amount of buriedness damage this human has. + @return The amount of buriedness damage. + */ + public double getBuriednessDamage() { + return damageBury.getDamage(); + } + + /** + Set the amount of buriedness damage this human has. + @param d The new buriedness damage. + */ + public void setBuriednessDamage(double d) { + damageBury.setDamage(d); + } + + /** + Add some fire damage. + @param d The amount of damage to add. + */ + public void addFireDamage(double d) { + damageFire.addDamage(d); + } + + /** + Get the amount of fire damage this human has. + @return The amount of fire damage. + */ + public double getFireDamage() { + return damageFire.getDamage(); + } + + /** + Set the amount of fire damage this human has. + @param d The new fire damage. + */ + public void setFireDamage(double d) { + damageFire.setDamage(d); + } + + /** + Get the total damage of this human, rounded to the nearest integer. + @return The total damage. + */ + public int getTotalDamage() { + return (int)Math.round(damageCollapse.getDamage() + damageFire.getDamage() + damageBury.getDamage()); + } + + /** + Progress all damage types. + */ + public void progressDamage() { + damageCollapse.progress(); + damageFire.progress(); + damageBury.progress(); + } + + public void progressDamageInRefuge() + { + //int damage = getTotalDamage(); + damageCollapse.progressInRefuge(); + damageFire.progressInRefuge(); + damageBury.progressInRefuge(); + } + + /** + Clear all damage. + */ + public void clearDamage() { + damageCollapse.setDamage(0); + damageBury.setDamage(0); + damageFire.setDamage(0); + } +} + diff --git a/modules/misc/src/misc/MiscParameters.java b/modules/misc/src/misc/MiscParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..ae9260e9a915191cbe5b1c8270a2bdf2c6e51510 --- /dev/null +++ b/modules/misc/src/misc/MiscParameters.java @@ -0,0 +1,277 @@ +package misc; + +import java.util.Random; +import java.util.Map; +import java.util.EnumMap; + +import rescuecore2.config.Config; + +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.Refuge; + +/** + * Container for all misc simulator parameters. + * @author Maitreyi Nanjanath + * @author Cameron Skinner + */ +public class MiscParameters { + private Config config; + + // All building classes indexed by building code and degree of collapse. The BuildingClass class knows about buriedness rates, injury rates etc. + private Map<BuildingCode, Map<BrokennessDegree, BuildingClass>> buildingClasses; + + private DamageType collapseDamage; + private DamageType buryDamage; + private DamageType fireDamage; + + /** + Create a new MiscParameters object based on a Config. + @param config The Config to read. + */ + public MiscParameters(Config config) { + this.config = config; + initBuildingData(); + initInjuryData(); + } + + /** + Find out if an agent inside a building should be buried due to collapse. + @param b The building to check. + @param hA Human Attribute for calc should bury. + @return True if an agent inside the building should be buried, false otherwise. + */ + public boolean shouldBuryAgent(Building b, HumanAttributes hA) { + if (!b.isBuildingCodeDefined() || !b.isBrokennessDefined() || b.getBrokenness() == 0||b instanceof Refuge) { + return false; + } + BuildingClass clazz = getBuildingClass(b); + return clazz.shouldBury(hA); + } + + /** + Get the buriedness of an agent inside a building. + @param b The building to check. + @return The buriedness of an agent inside the building. + */ + public int getBuriedness(Building b) { + if (!b.isBuildingCodeDefined() || !b.isBrokennessDefined() || b.getBrokenness() == 0) { + return 0; + } + BuildingClass clazz = getBuildingClass(b); + return clazz.getAgentBuriedness(); + } + + /** + Get the amount of damage an agent should take as a result of being in a collapsing building. + @param b The building. + @param hA The HumanAttributes. + @return The amount of damage to add to the agent. + */ + public int getCollapseDamage(Building b, HumanAttributes hA) { + if (!b.isBuildingCodeDefined() || !b.isBrokennessDefined() || b.getBrokenness() == 0) { + return 0; + } + BuildingClass clazz = getBuildingClass(b); + Injury injury = clazz.getCollapseInjury(hA.getRandom()); + return collapseDamage.getDamage(injury, hA.getHuman()); + } + + /** + Get the amount of damage an agent should take as a result of being buried in a collapsed building. + @param b The building. + @param hA The HumanAttributes. + @return The amount of damage to add to the agent. + */ + public int getBuryDamage(Building b, HumanAttributes hA) { + if (!b.isBuildingCodeDefined() || !b.isBrokennessDefined() || b.getBrokenness() == 0) { + return 0; + } + BuildingClass clazz = getBuildingClass(b); + Injury injury = clazz.getBuryInjury(hA.getRandom()); + return buryDamage.getDamage(injury, hA.getHuman()); + } + + /** + Get the amount of damage an agent should take as a result of being in a burning building. + @param b The building. + @param hA The HumanAttributes. + @return The amount of damage to add to the agent. + */ + public int getFireDamage(Building b, HumanAttributes hA) { + if (!b.isBuildingCodeDefined()) { + return 0; + } + BuildingClass clazz = getBuildingClass(b); + Injury injury = clazz.getFireInjury(hA.getRandom()); + return fireDamage.getDamage(injury, hA.getHuman()); + } + + private void initBuildingData() { + buildingClasses = new EnumMap<BuildingCode, Map<BrokennessDegree, BuildingClass>>(BuildingCode.class); + for (BuildingCode code : BuildingCode.values()) { + Map<BrokennessDegree, BuildingClass> codeMap = new EnumMap<BrokennessDegree, BuildingClass>(BrokennessDegree.class); + for (BrokennessDegree degree : BrokennessDegree.values()) { + codeMap.put(degree, new BuildingClass(config, code, degree)); + } + buildingClasses.put(code, codeMap); + } + } + + private void initInjuryData() { + collapseDamage = new DamageType(config, "collapse"); + buryDamage = new DamageType(config, "bury"); + fireDamage = new DamageType(config, "fire"); + } + + private BuildingClass getBuildingClass(Building b) { + BuildingCode code = BuildingCode.values()[b.getBuildingCode()]; + BrokennessDegree degree = BrokennessDegree.getBrokennessDegree(b); + return buildingClasses.get(code).get(degree); + } + + private class BuildingClass { + private double buriedProbability; + private int initialBuriedness; + private Map<Injury, Double> collapseInjuryProbability; + private Map<Injury, Double> buryInjuryProbability; + private Map<Injury, Double> fireInjuryProbability; + + public BuildingClass(Config config, BuildingCode code, BrokennessDegree degree) { + buriedProbability = config.getFloatValue("misc.buriedness." + code + "." + degree + ".rate"); + initialBuriedness = config.getIntValue("misc.buriedness." + code + "." + degree + ".value"); + collapseInjuryProbability = new EnumMap<Injury, Double>(Injury.class); + buryInjuryProbability = new EnumMap<Injury, Double>(Injury.class); + fireInjuryProbability = new EnumMap<Injury, Double>(Injury.class); + collapseInjuryProbability.put(Injury.SLIGHT, config.getFloatValue("misc.injury.collapse." + code + "." + degree + ".slight")); + collapseInjuryProbability.put(Injury.SERIOUS, config.getFloatValue("misc.injury.collapse." + code + "." + degree + ".serious")); + collapseInjuryProbability.put(Injury.CRITICAL, config.getFloatValue("misc.injury.collapse." + code + "." + degree + ".critical")); + buryInjuryProbability.put(Injury.SLIGHT, config.getFloatValue("misc.injury.bury." + code + "." + degree + ".slight")); + buryInjuryProbability.put(Injury.SERIOUS, config.getFloatValue("misc.injury.bury." + code + "." + degree + ".serious")); + buryInjuryProbability.put(Injury.CRITICAL, config.getFloatValue("misc.injury.bury." + code + "." + degree + ".critical")); + fireInjuryProbability.put(Injury.SLIGHT, config.getFloatValue("misc.injury.fire." + code + "." + degree + ".slight")); + fireInjuryProbability.put(Injury.SERIOUS, config.getFloatValue("misc.injury.fire." + code + "." + degree + ".serious")); + fireInjuryProbability.put(Injury.CRITICAL, config.getFloatValue("misc.injury.fire." + code + "." + degree + ".critical")); + } + + public int getAgentBuriedness() { + return initialBuriedness ;//TODO Genrate Random; + } + + public boolean shouldBury(HumanAttributes hA) { + return hA.getRandom().nextDouble() < buriedProbability; + } + + public Injury getCollapseInjury(Random random) { + return getInjury(collapseInjuryProbability, random); + } + + public Injury getBuryInjury(Random random) { + return getInjury(buryInjuryProbability, random); + } + + public Injury getFireInjury(Random random) { + return getInjury(fireInjuryProbability, random); + } + + private Injury getInjury(Map<Injury, Double> table, Random random) { + double d = random.nextDouble(); + + double d1 = table.get(Injury.SLIGHT); + double d2 = table.get(Injury.SERIOUS) + d1; + double d3 = table.get(Injury.CRITICAL) + d2; + if (d < d1) { + return Injury.SLIGHT; + } + if (d < d2) { + return Injury.SERIOUS; + } + if (d < d3) { + return Injury.CRITICAL; + } + return Injury.NONE; + } + } + + private enum BuildingCode { + WOOD, + STEEL, + CONCRETE; + + public String toString() { + return super.toString().toLowerCase(); + } + } + + private enum BrokennessDegree { + NONE(0, 0), + PARTIAL(1, 25), + HALF(26, 50), + ALL(51, 100); + + private int min; + private int max; + + private BrokennessDegree(int min, int max) { + this.min = min; + this.max = max; + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } + + public static BrokennessDegree getBrokennessDegree(Building b) { + int brokenness = b.isBrokennessDefined() ? b.getBrokenness() : 0; + for (BrokennessDegree next : BrokennessDegree.values()) { + if (brokenness >= next.min && brokenness <= next.max) { + return next; + } + } + return BrokennessDegree.NONE; + } + } + + private enum Injury { + NONE, + SLIGHT, + SERIOUS, + CRITICAL; + } + + private static class DamageType { + private Map<Injury, Integer> damage; + private double ambulanceMultiplier; + private double policeMultiplier; + private double fireMultiplier; + + public DamageType(Config config, String type) { + damage = new EnumMap<Injury, Integer>(Injury.class); + damage.put(Injury.NONE, 0); + damage.put(Injury.SLIGHT, config.getIntValue("misc.injury." + type + ".slight")); + damage.put(Injury.SERIOUS, config.getIntValue("misc.injury." + type + ".serious")); + damage.put(Injury.CRITICAL, config.getIntValue("misc.injury." + type + ".critical")); + ambulanceMultiplier = config.getFloatValue("misc.injury." + type + ".multiplier.ambulance"); + policeMultiplier = config.getFloatValue("misc.injury." + type + ".multiplier.police"); + fireMultiplier = config.getFloatValue("misc.injury." + type + ".multiplier.fire"); + } + + public int getDamage(Injury injury, Human agent) { + int result = damage.get(injury); + if (agent instanceof AmbulanceTeam) { + return (int)(result * ambulanceMultiplier); + } + if (agent instanceof PoliceForce) { + return (int)(result * policeMultiplier); + } + if (agent instanceof FireBrigade) { + return (int)(result * fireMultiplier); + } + return result; + } + } +} diff --git a/modules/misc/src/misc/MiscSimulator.java b/modules/misc/src/misc/MiscSimulator.java new file mode 100755 index 0000000000000000000000000000000000000000..d0d7c95337a62dd7270a8e52c5bc90b6eaa4ef9e --- /dev/null +++ b/modules/misc/src/misc/MiscSimulator.java @@ -0,0 +1,666 @@ +package misc; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.Formatter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +import javax.swing.JComponent; + +import rescuecore2.GUIComponent; +import rescuecore2.log.Logger; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.messages.control.KSUpdate; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.GasStation; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardPropertyURN; +import rescuecore2.standard.messages.AKLoad; +import rescuecore2.standard.messages.AKRescue; +import rescuecore2.standard.messages.AKUnload; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.EntityListener; +import rescuecore2.worldmodel.Property; + +/** + * Implementation of the legacy misc simulator. + * + * @author Maitreyi Nanjanath + * @author Cameron Skinner + * + * Implementation of Refuge Bed Capacity + * @author Farshid Faraji + */ +public class MiscSimulator extends StandardSimulator implements GUIComponent { + + private Map<EntityID, HumanAttributes> humans; + private Set<EntityID> newlyBrokenBuildings; + private Map<EntityID, Integer> oldBrokenBuildingsBuriedness = new HashMap<>(); + private MiscParameters parameters; + private MiscSimulatorGUI gui; + private int GAS_STATION_EXPLOSION_RANG; + private int GAS_STATION_Buriedness_Bound; + private int GAS_STATION_Buriedness_MIN; + private int GAS_STATION_Damage_Bound; + private int GAS_STATION_Damage_MIN; + + private Set<EntityID> notExplosedGasStations; + private Map<EntityID, Deque<EntityID>> waitingList; + private Map<EntityID, Deque<EntityID>> beds; + + @Override + public JComponent getGUIComponent() { + if (gui == null) { + gui = new MiscSimulatorGUI(); + } + return gui; + } + + @Override + public String getGUIComponentName() { + return "Misc simulator"; + } + + @Override + protected void postConnect() { + super.postConnect(); + notExplosedGasStations = new HashSet<>(); + waitingList = new HashMap<EntityID, Deque<EntityID>>(); + beds = new HashMap<EntityID, Deque<EntityID>>(); + + parameters = new MiscParameters(config); + GAS_STATION_EXPLOSION_RANG = config.getIntValue("ignition.gas_station.explosion.range", 0); + GAS_STATION_Buriedness_Bound = config.getIntValue("misc.gas_station.Buriedness.bound", 30); + GAS_STATION_Buriedness_MIN = config.getIntValue("misc.gas_station.Buriedness.min", 0); + GAS_STATION_Damage_Bound = config.getIntValue("misc.gas_station.Damage.bound", 50); + GAS_STATION_Damage_MIN = config.getIntValue("misc.gas_station.Damage.min", 15); + + humans = new HashMap<EntityID, HumanAttributes>(); + newlyBrokenBuildings = new HashSet<EntityID>(); + Logger.info("MiscSimulator connected. World has " + model.getAllEntities().size() + " entities."); + BuildingChangeListener buildingListener = new BuildingChangeListener(); + // HumanChangeListener humanListener = new HumanChangeListener(); + for (Entity et : model.getAllEntities()) { + if (et instanceof GasStation) { + notExplosedGasStations.add(et.getID()); + } + if (et instanceof Refuge) { + Deque<EntityID> wlist = new LinkedList<EntityID>(); + waitingList.put(et.getID(), wlist); + + Deque<EntityID> blist = new LinkedList<EntityID>(); + beds.put(et.getID(), blist); + } + if (et instanceof Building) { + et.addEntityListener(buildingListener); + } else if (et instanceof Human) { + // et.addEntityListener(humanListener); + Human human = (Human) et; + HumanAttributes ha = new HumanAttributes(human, config); + humans.put(ha.getID(), ha); + } + } + } + + @Override + protected void processCommands(KSCommands c, ChangeSet changes) { + long start = System.currentTimeMillis(); + int time = c.getTime(); + Logger.info("Timestep " + time); + + for (Command com : c.getCommands()) { + + if (checkValidity(com)) { + if (com instanceof AKRescue) { + Human human = (Human) (model.getEntity(((AKRescue) com).getTarget())); + handleRescue(human, changes); + } + /* + * For the implementation of Refuge Bed Capacity + **/ + if (com instanceof AKUnload) { + handleUnload(com, changes); + } + + if (com instanceof AKLoad) { + handleLoad((AKLoad) com, changes); + } + + } else { + Logger.debug("Ignoring " + com); + } + } + + updateRefuges(); + + processBrokenBuildings(changes); + // processBurningBuildings( changes ); + // processExplodedGasStations( changes ); + updateDamage(changes); + + updateChangeSet(changes); + + // Clean up + newlyBrokenBuildings.clear(); + writeDebugOutput(c.getTime()); + if (gui != null) { + gui.refresh(humans.values()); + } + long end = System.currentTimeMillis(); + Logger.info("Timestep " + time + " took " + (end - start) + " ms"); + } + + private void processExplodedGasStations(ChangeSet changes) { + Logger.info("processExplodedGasStations for " + notExplosedGasStations); + for (Iterator<EntityID> iterator = notExplosedGasStations.iterator(); iterator.hasNext();) { + GasStation gasStation = (GasStation) model.getEntity(iterator.next()); + if (gasStation.isFierynessDefined() && gasStation.getFieryness() == 1) { + for (HumanAttributes hA : humans.values()) { + + Human human = hA.getHuman(); + if (!human.isXDefined() || !human.isYDefined()) + continue; + if (GeometryTools2D.getDistance(new Point2D(human.getX(), human.getY()), + new Point2D(gasStation.getX(), gasStation.getY())) < GAS_STATION_EXPLOSION_RANG) { + Logger.info(human + " getting damage from explosion..." + human); + int oldBuriedness = human.isBuriednessDefined() ? human.getBuriedness() : 0; + human.setBuriedness( + oldBuriedness + hA.getRandom().nextInt(GAS_STATION_Buriedness_Bound) + GAS_STATION_Buriedness_MIN); + changes.addChange(human, human.getBuriednessProperty()); + // Check for injury from being exploded + int damage = hA.getRandom().nextInt(GAS_STATION_Damage_Bound) + GAS_STATION_Damage_MIN; + if (damage != 0) { + hA.addCollapseDamage(damage); + } + } + + } + + iterator.remove(); + } + } + + } + + private void processBrokenBuildings(ChangeSet changes) { + for (HumanAttributes hA : humans.values()) { + Human human = hA.getHuman(); + EntityID positionID = human.getPosition(); + if (!newlyBrokenBuildings.contains(positionID)) { + continue; + } + // Human is in a newly collapsed building + // Check for buriedness + Logger.trace("Checking if human should be buried in broken building"); + Building b = (Building) human.getPosition(model); + if (parameters.shouldBuryAgent(b, hA)) { + int buriedness = parameters.getBuriedness(b) - oldBrokenBuildingsBuriedness.get(b.getID()); + + if (buriedness != 0) { + int oldBuriedness = human.isBuriednessDefined() ? human.getBuriedness() : 0; + human.setBuriedness(oldBuriedness + buriedness); + changes.addChange(human, human.getBuriednessProperty()); + // Check for injury from being buried + int damage = parameters.getBuryDamage(b, hA); + if (damage != 0) { + hA.addBuriednessDamage(damage); + } + } + } + // Now check for injury from the collapse + int damage = parameters.getCollapseDamage(b, hA); + if (damage != 0) { + hA.addCollapseDamage(damage); + } + } + } + + private void processBurningBuildings(ChangeSet changes) { + for (HumanAttributes hA : humans.values()) { + Human human = hA.getHuman(); + Entity position = human.getPosition(model); + if (position instanceof Building && ((Building) position).isOnFire()) { + // Human is in a burning building + int damage = parameters.getFireDamage((Building) position, hA); + if (damage != 0) { + hA.addFireDamage(damage); + } + } + } + } + + private void writeDebugOutput(int time) { + StringBuilder builder = new StringBuilder(); + Formatter format = new Formatter(builder); + format.format("Agents damaged or buried at timestep %1d%n", time); + format.format(" ID | HP | Damage | Bury | Collapse | Fire | Buriedness%n"); + for (HumanAttributes ha : humans.values()) { + Human h = ha.getHuman(); + int hp = h.isHPDefined() ? h.getHP() : 0; + int damage = ha.getTotalDamage(); + int buriedness = h.isBuriednessDefined() ? h.getBuriedness() : 0; + boolean isAlive = hp > 0; + boolean hasDamage = damage > 0; + boolean isBuried = buriedness > 0; + if ((hasDamage || isBuried) && isAlive) { + format.format("%1$9d | %2$6d | %3$6d | %4$8.3f | %5$8.3f | %6$8.3f | %7$6d%n", ha.getID().getValue(), hp, + damage, ha.getBuriednessDamage(), ha.getCollapseDamage(), ha.getFireDamage(), buriedness); + } + } + format.close(); + Logger.debug(builder.toString()); + } + + private void updateDamage(ChangeSet changes) { + for (HumanAttributes ha : humans.values()) { + Human h = ha.getHuman(); + int oldDamage = ha.getTotalDamage(); + if (h.isPositionDefined() && !(h.getPosition(model) instanceof Refuge)) { + updateDamage(ha); + int hp = h.isHPDefined() ? h.getHP() : 0; + int damage = ha.getTotalDamage(); + + h.setDamage(damage); + changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); + + // Update HP + boolean isAlive = hp > 0; + boolean hasDamage = damage > 0; + + if (isAlive && hasDamage) { + int newHP = Math.max(0, hp - damage); + h.setHP(newHP); + changes.addChange(ha.getHuman(), ha.getHuman().getHPProperty()); + } + } + /* + * For the implementation of Refuge Bed Capacity Damage increases and HP + * decreases while victim is in waiting list in Refuge While victim is on the + * bed, Damage is reducing but HP is fix human will not die while on the bed but + * it takes time to get damage to 0 + */ + + else if (h.isPositionDefined() && (h.getPosition(model) instanceof Refuge) && h.isHPDefined() && h.getHP() > 0) { + if (h instanceof FireBrigade || h instanceof AmbulanceTeam || h instanceof PoliceForce) { + ha.clearDamage(); + h.setDamage(0); + changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); + continue; + } + if (waitingList.get(h.getPosition()).size() > 0 && waitingList.get(h.getPosition()).contains(h.getID())) { + updateDamage(ha); + int hp = h.isHPDefined() ? h.getHP() : 0; + int damage = ha.getTotalDamage(); + + h.setDamage(damage); + changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); + + // Update HP + boolean isAlive = hp > 0; + boolean hasDamage = damage > 0; + + if (isAlive && hasDamage) { + int newHP = Math.max(0, hp - damage); + h.setHP(newHP); + changes.addChange(ha.getHuman(), ha.getHuman().getHPProperty()); + } + } else if (beds.get(h.getPosition()).size() > 0 && beds.get(h.getPosition()).contains(h.getID())) { + updateDamageInRefuge(ha); + h.setDamage(ha.getTotalDamage()); + changes.addChange(ha.getHuman(), ha.getHuman().getDamageProperty()); + + if (oldDamage > 0 && h.getDamage() <= 0) { + + if (beds.get(h.getPosition()).remove(h.getID())) { + ((Refuge) h.getPosition(model)).decreaseOccupiedBeds(); + } + + if (waitingList.get(h.getPosition()).size() > 0) { + beds.get(h.getPosition()).add(waitingList.get(h.getPosition()).remove()); + ((Refuge) h.getPosition(model)).increaseOccupiedBeds(); + } + } + } + } + } + } + + private void updateDamage(HumanAttributes ha) { + Human h = ha.getHuman(); + if (h.getHP() <= 0) { + return; // Agent is already dead. + } + ha.progressDamage(); + } + + /* + * For the implementation of Refuge Bed Capacity + **/ + private void updateDamageInRefuge(HumanAttributes ha) { + Human h = ha.getHuman(); + if (h.getHP() <= 0) { + return; // Agent is already dead. + } + ha.progressDamageInRefuge(); + } + + private boolean checkValidity(Command command) { + Entity e = model.getEntity(command.getAgentID()); + if (e == null) { + Logger.warn("Received a " + command.getURN() + " command from an unknown agent: " + command.getAgentID()); + return false; + } + if (command instanceof AKRescue) { + return checkRescue((AKRescue) command, e); + } + if (command instanceof AKUnload) + return checkUnload((AKUnload) command, e); + + if (command instanceof AKLoad) + return checkLoad((AKLoad) command, e); + + return false; + } + + private boolean checkRescue(AKRescue rescue, Entity agent) { + EntityID targetID = rescue.getTarget(); + Entity target = model.getEntity(targetID); + // || agent instanceof AmbulanceTeam + if (!(agent instanceof FireBrigade)) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + " who is of type " + agent.getURN()); + return false; + } + if (target == null) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-existent target " + targetID); + return false; + } + if (!(target instanceof Human)) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-human target: " + targetID + + " is of type " + target.getURN()); + return false; + } + Human h = (Human) target; + if (!h.isBuriednessDefined() || h.getBuriedness() == 0) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-buried target " + targetID); + return false; + } + + // || agent instanceof AmbulanceTeam + if (agent instanceof FireBrigade) { + Human ag = (Human) agent; + if (ag.isHPDefined() && ag.getHP() <= 0) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": agent is dead"); + return false; + } + if (ag.isBuriednessDefined() && ag.getBuriedness() > 0) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": agent is buried"); + return false; + } + if (!h.isPositionDefined() || !ag.isPositionDefined() || !h.getPosition().equals(ag.getPosition())) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + " for a non-adjacent target " + targetID); + return false; + } + if (h.getID().equals(ag.getID())) { + Logger.warn("Rejecting rescue command from agent " + agent.getID() + ": tried to rescue self"); + return false; + } + } + return true; + } + + private boolean checkUnload(AKUnload unload, Entity agent) { + if (!(agent instanceof AmbulanceTeam)) { + Logger.warn("Rejecting unload command from agent " + agent.getID() + " who is of type " + agent.getURN()); + return false; + } + + AmbulanceTeam at = (AmbulanceTeam) agent; + if (at.isHPDefined() && at.getHP() <= 0) { + Logger.warn("Rejecting Unload command from agent " + agent.getID() + ": agent is dead"); + return false; + } + + return true; + } + + private boolean checkLoad(AKLoad unload, Entity agent) { + if (!(agent instanceof AmbulanceTeam)) { + Logger.warn("Rejecting load command from agent " + agent.getID() + " who is of type " + agent.getURN()); + return false; + } + + AmbulanceTeam at = (AmbulanceTeam) agent; + if (at.isHPDefined() && at.getHP() <= 0) { + Logger.warn("Rejecting Unload command from agent " + agent.getID() + ": agent is dead"); + return false; + } + + return true; + } + + private void handleRescue(Human target, ChangeSet changes) { + target.setBuriedness(Math.max(0, target.getBuriedness() - 1)); + changes.addChange(target, target.getBuriednessProperty()); + } + + /* + * For the implementation of Refuge Bed Capacity + **/ + private void handleUnload(Command command, ChangeSet changes) { + EntityID agentID = command.getAgentID(); + Entity agent = model.getEntity(agentID); + Civilian target = null; + for (Entity e : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) { + Civilian civ = (Civilian) e; + if (civ.isPositionDefined() && agentID.equals(civ.getPosition())) { + target = civ; + break; + } + } + if (target != null) { + Entity AgentPosition = ((Human) agent).getPosition(model); + if (AgentPosition != null && AgentPosition instanceof Refuge) { + addVictimToWaitingList(AgentPosition, target); + } + } + } + + private void handleLoad(AKLoad load, ChangeSet changes) { + EntityID agentID = load.getAgentID(); + Entity agent = model.getEntity(agentID); + + EntityID targetID = load.getTarget(); + Entity target = model.getEntity(targetID); + + // --------------- + if (agent == null) { + Logger.warn("Rejecting load command from agent " + agentID + ": agent does not exist"); + return; + } + if (!(agent instanceof AmbulanceTeam)) { + Logger.warn("Rejecting load command from agent " + agentID + ": agent type is " + agent.getURN()); + return; + } + if (target == null) { + Logger.warn("Rejecting load command from agent " + agentID + ": target does not exist " + targetID); + return; + } + + 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; + } + + if (!h.isPositionDefined() || !at.isPositionDefined() || !h.getPosition().equals(at.getPosition())) { + Logger.warn("Rejecting load command from agent " + agentID + ": target is non-adjacent " + targetID); + return; + } + if (h.getID().equals(at.getID())) { + Logger.warn("Rejecting load command from agent " + agentID + ": tried to load self"); + return; + } + // 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; + } + } + // -------------------- + + Entity AgentPosition = ((Human) agent).getPosition(model); + if (AgentPosition != null && AgentPosition instanceof Refuge) { + + if (waitingList.get(h.getPosition()).size() > 0 && waitingList.get(h.getPosition()).contains(h.getID())) { + waitingList.get(h.getPosition()).remove(h.getID()); + } + + if (beds.get(h.getPosition()).size() > 0 && beds.get(h.getPosition()).contains(h.getID())) { + beds.get(h.getPosition()).remove(h.getID()); + ((Refuge) h.getPosition(model)).decreaseOccupiedBeds(); + + if (waitingList.get(h.getPosition()).size() > 0) { + beds.get(h.getPosition()).add(waitingList.get(h.getPosition()).remove()); + ((Refuge) h.getPosition(model)).increaseOccupiedBeds(); + } + } + + } + } + + private class BuildingChangeListener implements EntityListener { + + @Override + public void propertyChanged(Entity e, Property p, Object oldValue, Object newValue) { + if (!(e instanceof Building)) { + return; // we want to only look at buildings + } + + if (p.getURN() == (StandardPropertyURN.BROKENNESS.getURNId())) + checkBrokenness(e, oldValue, newValue); + + } + + private void checkBrokenness(Entity e, Object oldValue, Object newValue) { + double old = oldValue == null ? 0 : (Integer) oldValue; + double next = newValue == null ? 0 : (Integer) newValue; + if (next > old) { + newlyBrokenBuildings.add(e.getID()); + + } + } + + } + + @Override + protected void handleUpdate(KSUpdate u) { + for (StandardEntity entity : model) { + if (entity instanceof Building) { + oldBrokenBuildingsBuriedness.put(entity.getID(), parameters.getBuriedness((Building) entity)); + } + } + super.handleUpdate(u); + } + + private void addVictimToWaitingList(Entity refuge, Civilian victim) { + if (victim.getDamage() > 0) + waitingList.get(refuge.getID()).add(victim.getID()); + } + + /* + * For the implementation of Refuge Bed Capacity + **/ + private void updateRefuges() { + for (Map.Entry<EntityID, Deque<EntityID>> e : waitingList.entrySet()) { + ArrayList<EntityID> tempList = new ArrayList<EntityID>(); + for (EntityID civ : (Deque<EntityID>) e.getValue()) { + if (model.getEntity(civ) instanceof Human) { + if (((Human) model.getEntity(civ)).getDamage() <= 0) { + tempList.add(civ); + } + if (((Human) model.getEntity(civ)).getHP() <= 0) { + tempList.add(civ); + } + } + } + if (tempList.size() > 0) { + ((Deque<EntityID>) e.getValue()).removeAll(tempList); + } + } + + for (Map.Entry<EntityID, Deque<EntityID>> e : beds.entrySet()) { + ArrayList<EntityID> tempList = new ArrayList<EntityID>(); + for (EntityID civ : (Deque<EntityID>) e.getValue()) { + if (model.getEntity(civ) instanceof Human) { + if (((Human) model.getEntity(civ)).getDamage() <= 0) { + tempList.add(civ); + } + if (((Human) model.getEntity(civ)).getHP() <= 0) { + tempList.add(civ); + } + } + } + if (tempList.size() > 0) { + // ( (Deque<EntityID>) e.getValue() ).removeAll( tempList ); + for (EntityID id : tempList) { + if (((Deque<EntityID>) e.getValue()).remove(id)) { + ((Refuge) model.getEntity(e.getKey())).decreaseOccupiedBeds(); + Logger.warn("decreaseOccupiedBeds in update Refuge"); + } + } + + } + } + + for (StandardEntity e : model.getEntitiesOfType(StandardEntityURN.REFUGE)) { + if (e instanceof Refuge) { + // Logger.warn("Refuge = " + e.getID() + " bed cap = " + ( (Refuge) e + // ).getOccupiedBeds() + " beds size = " + beds.get(e.getID()).size() + " wait + // size = " + waitingList.get(e.getID()).size()); + while (((Refuge) e).getOccupiedBeds() < ((Refuge) e).getBedCapacity()) { + if (waitingList.get(e.getID()).size() > 0) { + beds.get(e.getID()).add(waitingList.get(e.getID()).remove()); + ((Refuge) e).increaseOccupiedBeds(); + } else + break; + } + } + } + } + + /* + * For the implementation of Refuge Bed Capacity + **/ + private void updateChangeSet(ChangeSet changes) { + for (StandardEntity e : model.getEntitiesOfType(StandardEntityURN.REFUGE)) + if (e instanceof Refuge) { + int size = waitingList.get(e.getID()).size(); + ((Refuge) e).setWaitingListSize(size); + changes.addChange((Refuge) e, ((Refuge) e).getOccupiedBedsProperty()); + changes.addChange((Refuge) e, ((Refuge) e).getWaitingListSizeProperty()); + } + } +} \ No newline at end of file diff --git a/modules/misc/src/misc/MiscSimulatorGUI.java b/modules/misc/src/misc/MiscSimulatorGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..c77adc18259010a969ed27c0e5e52a76a4c9c2b8 --- /dev/null +++ b/modules/misc/src/misc/MiscSimulatorGUI.java @@ -0,0 +1,105 @@ +package misc; + +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JScrollPane; +import javax.swing.table.AbstractTableModel; +import java.awt.BorderLayout; +import java.util.Collection; + +/** + A GUI component for viewing the misc simulator state. + */ +public class MiscSimulatorGUI extends JPanel { + private static final int COLUMN_ID = 0; + private static final int COLUMN_HP = 1; + private static final int COLUMN_DAMAGE = 2; + private static final int COLUMN_BURY = 3; + private static final int COLUMN_COLLAPSE = 4; + private static final int COLUMN_FIRE = 5; + private static final int COLUMN_BURIEDNESS = 6; + private static final int COLUMNS = 7; + + private MiscTableModel model; + + /** + Create a MiscSimulatorGUI. + */ + public MiscSimulatorGUI() { + super(new BorderLayout()); + model = new MiscTableModel(); + add(new JScrollPane(new JTable(model)), BorderLayout.CENTER); + } + + /** + Refresh the UI. + @param data The set of human data to show. + */ + public void refresh(Collection<HumanAttributes> data) { + model.setData(data.toArray(new HumanAttributes[data.size()])); + } + + private static class MiscTableModel extends AbstractTableModel { + private HumanAttributes[] data; + + void setData(HumanAttributes[] data) { + this.data = data; + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return data == null ? 0 : data.length; + } + + @Override + public int getColumnCount() { + return COLUMNS; + } + + @Override + public Object getValueAt(int row, int col) { + HumanAttributes att = data[row]; + switch (col) { + case COLUMN_ID: + return att.getID(); + case COLUMN_HP: + return att.getHuman().isHPDefined() ? String.valueOf(att.getHuman().getHP()) : "undefined"; + case COLUMN_DAMAGE: + return att.getHuman().isDamageDefined() ? String.valueOf(att.getHuman().getDamage()) : "undefined"; + case COLUMN_BURY: + return att.getBuriednessDamage(); + case COLUMN_COLLAPSE: + return att.getCollapseDamage(); + case COLUMN_FIRE: + return att.getFireDamage(); + case COLUMN_BURIEDNESS: + return att.getHuman().isBuriednessDefined() ? String.valueOf(att.getHuman().getBuriedness()) : "undefined"; + default: + throw new IllegalArgumentException("Unrecognised column: " + col); + } + } + + @Override + public String getColumnName(int col) { + switch (col) { + case COLUMN_ID: + return "ID"; + case COLUMN_HP: + return "HP"; + case COLUMN_DAMAGE: + return "Total damage"; + case COLUMN_BURY: + return "Buriedness damage"; + case COLUMN_COLLAPSE: + return "Collapse damage"; + case COLUMN_FIRE: + return "Fire damage"; + case COLUMN_BURIEDNESS: + return "Buriedness"; + default: + throw new IllegalArgumentException("Unrecognised column: " + col); + } + } + } +} diff --git a/modules/oldsims/firesimulator/kernel/Kernel.java b/modules/oldsims/firesimulator/kernel/Kernel.java new file mode 100644 index 0000000000000000000000000000000000000000..dc5cba03bcb9de24938b635a81ca95c5fc80154d --- /dev/null +++ b/modules/oldsims/firesimulator/kernel/Kernel.java @@ -0,0 +1,25 @@ +package firesimulator.kernel; + +import firesimulator.simulator.Simulator; + + + +/** + * @author tn + * + */ +public interface Kernel { + + public void register(Simulator sim); + + public void establishConnection(); + + public void signalReadyness(); + + public boolean waitForNextCycle(); + + public void sendUpdate(); + + public void receiveUpdate(); + +} diff --git a/modules/oldsims/firesimulator/simulator/EnergyHistory.java b/modules/oldsims/firesimulator/simulator/EnergyHistory.java new file mode 100644 index 0000000000000000000000000000000000000000..754f3ba290f429d8b675ac7308f776d990053fa9 --- /dev/null +++ b/modules/oldsims/firesimulator/simulator/EnergyHistory.java @@ -0,0 +1,85 @@ +package firesimulator.simulator; + +import java.util.Map; +import java.util.HashMap; + +import firesimulator.world.World; +import firesimulator.world.Building; +import org.apache.log4j.Logger; + +public class EnergyHistory { + private static final Logger LOG = Logger.getLogger(EnergyHistory.class); + + private int time; + private Map<Building, Double> initialEnergy; + private Map<Building, Double> initialTemperature; + private Map<Building, Double> burnEnergy; + private Map<Building, Double> coolEnergy; + private Map<Building, Double> exchangedWithAir; + private Map<Building, Double> lostToRadiation; + private Map<Building, Double> gainedByRadiation; + private Map<Building, Double> finalEnergy; + private Map<Building, Double> finalTemperature; + + public EnergyHistory(World world, int time) { + initialEnergy = new HashMap<Building, Double>(); + initialTemperature = new HashMap<Building, Double>(); + burnEnergy = new HashMap<Building, Double>(); + coolEnergy = new HashMap<Building, Double>(); + exchangedWithAir = new HashMap<Building, Double>(); + lostToRadiation = new HashMap<Building, Double>(); + gainedByRadiation = new HashMap<Building, Double>(); + finalEnergy = new HashMap<Building, Double>(); + finalTemperature = new HashMap<Building, Double>(); + this.time = time; + for (Building next : world.getBuildings()) { + initialEnergy.put(next, next.getEnergy()); + initialTemperature.put(next, next.getTemperature()); + } + } + + public void registerBurn(Building b, double energy) { + burnEnergy.put(b, energy); + } + + public void registerCool(Building b, double energy) { + coolEnergy.put(b, energy); + } + + public void registerAir(Building b, double energy) { + exchangedWithAir.put(b, energy); + } + + public void registerRadiationLoss(Building b, double energy) { + lostToRadiation.put(b, energy); + } + + public void registerRadiationGain(Building b, double energy) { + double old = gainedByRadiation.containsKey(b) ? gainedByRadiation.get(b) : 0; + gainedByRadiation.put(b, old + energy); + } + + public void registerFinalEnergy(World world) { + for (Building next : world.getBuildings()) { + finalEnergy.put(next, next.getEnergy()); + finalTemperature.put(next, next.getTemperature()); + } + } + + public void logSummary() { + LOG.debug("Energy summary at time " + time); + for (Building next : initialEnergy.keySet()) { + boolean changed = burnEnergy.containsKey(next) || coolEnergy.containsKey(next) || exchangedWithAir.containsKey(next) || lostToRadiation.containsKey(next) || gainedByRadiation.containsKey(next); + if (changed && !initialEnergy.get(next).equals(finalEnergy.get(next))) { + LOG.debug("Building " + next.getID()); + LOG.debug(" Initial energy / temperature: " + initialEnergy.get(next) + " / " + initialTemperature.get(next)); + LOG.debug(" Burn energy : " + burnEnergy.get(next)); + LOG.debug(" Cool energy : " + coolEnergy.get(next)); + LOG.debug(" Exchanged with air : " + exchangedWithAir.get(next)); + LOG.debug(" Lost to radiation : " + lostToRadiation.get(next)); + LOG.debug(" Gained by radiation : " + gainedByRadiation.get(next)); + LOG.debug(" Final energy / temperature : " + finalEnergy.get(next) + " / " + finalTemperature.get(next)); + } + } + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/simulator/ExtinguishRequest.java b/modules/oldsims/firesimulator/simulator/ExtinguishRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..79b86a8072819b0a1a83bcb90ab466220ca6e923 --- /dev/null +++ b/modules/oldsims/firesimulator/simulator/ExtinguishRequest.java @@ -0,0 +1,128 @@ +package firesimulator.simulator; + +import firesimulator.util.Configuration; +import firesimulator.world.Building; +import firesimulator.world.FireBrigade; + +import org.apache.log4j.Logger; + +/** + * @author tn + * + */ +public class ExtinguishRequest { + private static final Logger LOG = Logger.getLogger(ExtinguishRequest.class); + + public static final int REASON_OK=1; + public static final int REASON_OK_VIRTUAL=2; + public static final int REASON_FB_WAS_NULL=-1; + public static final int REASON_TO_MUCH_WATER=-2; + public static final int REASON_TANK_EMPTY=-3; + public static final int REASON_OUT_OF_RANGE=-4; + public static final int REASON_NEGATIVE_WATER=-5;//Add by Ali Modaresi To fix using kernel bug + + public static final String OK="passed all tests"; + public static final String OK_VIRT="is virtual mode"; + public static final String ER_FB_NULL="firebrigade is null"; + public static final String ER_TO_MUCH="firebrigade is exceeding limt"; + public static final String ER_EMPTY="tank is empty"; + public static final String ER_RANGE="target is out of range"; + public static final String ER_NEGATIVE_WATER="negative water request";//Add by Ali Modaresi To fix using kernel bug + public static final String UNKNOWN="unknown code "; + + public static int MAX_WATER_PER_CYCLE; + public static int MAX_DISTANCE; + private FireBrigade source; + private Building target; + private int quantity; + private static boolean DEBUG_VERBOSE=true; + + public ExtinguishRequest(FireBrigade source, Building target,int quantity){ + this.target=target; + this.source=source; + this.quantity=quantity; + } + + public void verbose(String msg){ + if(DEBUG_VERBOSE) + LOG.debug(msg); + } + + public int validate(){ + if(source==null && Configuration.isActive("virtual"))return REASON_OK_VIRTUAL; + if(source==null)return REASON_FB_WAS_NULL; + if(source.getWaterUsed()+quantity>MAX_WATER_PER_CYCLE){ + return REASON_TO_MUCH_WATER; + } + if(source.getWaterQuantity()<quantity){ + return REASON_TANK_EMPTY; + } + if(distance(source,target)>MAX_DISTANCE) + return REASON_OUT_OF_RANGE; + + if(quantity<0){//Added by Ali Modaresi to fix using kernel bug + LOG.warn("Using kernel bug.... Extinguish with negative water");//Added by Ali Modaresi to fix using kernel bug + return REASON_NEGATIVE_WATER;//Added by Ali Modaresi to fix using kernel bug + } + return REASON_OK; + } + + public String getReason(int code){ + switch (code) { + case REASON_OK: + return OK; + case REASON_OK_VIRTUAL: + return OK_VIRT; + case REASON_FB_WAS_NULL: + return ER_FB_NULL; + case REASON_OUT_OF_RANGE: + return ER_RANGE; + case REASON_TANK_EMPTY: + return ER_EMPTY; + case REASON_TO_MUCH_WATER: + return ER_TO_MUCH; + case REASON_NEGATIVE_WATER://Added by Ali Modaresi to fix using kernel bug + return ER_NEGATIVE_WATER;//Added by Ali Modaresi to fix using kernel bug + default: + return UNKNOWN+code; + } + } + + + private double distance(FireBrigade source2, Building target2) { + double x=source2.getX()-target2.getX(); + double y=source2.getY()-target2.getY(); + return Math.sqrt((x*x)+(y*y)); + } + + public boolean execute(){ + verbose(toString()); + int result; + if((result=validate())<0){ + verbose("ERROR reason = "+getReason(result)+"\n"); + return false; + } + if(source!=null){ + source.addWaterUsed(quantity); + source.setWaterQuantity(source.getWaterQuantity()-quantity); + } + target.setWaterQuantity(target.getWaterQuantity()+quantity); + verbose("OK reason = "+getReason(result)+"\n"); + return true; + } + + public FireBrigade getSource() { + return source; + } + + public String toString(){ + String fb; + try{ + fb="fb="+source.getID(); + }catch (Exception e) { + fb="fb=null"; + } + return "extinguish request; "+fb+", target="+target.getID()+", quantity="+quantity+" -> "; + } + +} diff --git a/modules/oldsims/firesimulator/simulator/Monitor.java b/modules/oldsims/firesimulator/simulator/Monitor.java new file mode 100644 index 0000000000000000000000000000000000000000..412643484a9e73a61a41e580cb35c7114f2ddcf0 --- /dev/null +++ b/modules/oldsims/firesimulator/simulator/Monitor.java @@ -0,0 +1,14 @@ +package firesimulator.simulator; + +import firesimulator.world.World; + + +public interface Monitor { + + public void step(World world); + + public void reset(World world); + + public void done(World world); + +} diff --git a/modules/oldsims/firesimulator/simulator/Simulator.java b/modules/oldsims/firesimulator/simulator/Simulator.java new file mode 100644 index 0000000000000000000000000000000000000000..71f421f92b38ab601b5481bcafbf0a53b9f2e2ed --- /dev/null +++ b/modules/oldsims/firesimulator/simulator/Simulator.java @@ -0,0 +1,387 @@ +package firesimulator.simulator; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; +import firesimulator.util.Configuration; +import firesimulator.util.Rnd; +import firesimulator.world.Building; +import firesimulator.world.FireBrigade; +import firesimulator.world.World; +import org.apache.log4j.Logger; + +public class Simulator { + + private static final Logger LOG = Logger.getLogger( Simulator.class ); + + private World world; + + private WindShift windShift; + + public static float GAMMA = 0.5f; + + public static float AIR_TO_AIR_COEFFICIENT = 0.5f; + + public static float AIR_TO_BUILDING_COEFFICIENT = 45f; + + public static float WATER_COEFFICIENT = 0.5f; + + public static float ENERGY_LOSS = 0.9f; + + public static float WIND_DIRECTION = 0.9f; + + public static float WIND_RANDOM = 0f; + + public static int WIND_SPEED = 0; + + public static float RADIATION_COEFFICENT = 1.0f; + + public static float TIME_STEP_LENGTH = 1f; + + public static float WEIGHT_GRID = 0.2f; + + public static float AIR_CELL_HEAT_CAPACITY = 1f; + + private Set monitors; + + private static boolean verbose; + + private static Simulator me; + + private EnergyHistory energyHistory; + + + public Simulator( World world ) { + me = this; + monitors = new HashSet(); + verbose = true; + this.world = world; + windShift = null; + } + + + public static Simulator getSimulator() { + return me; + } + + + public void addMonitor( Monitor monitor ) { + monitors.add( monitor ); + } + + + public void removeMonitor( Monitor monitor ) { + monitors.remove( monitor ); + } + + + private void informStep() { + for ( Iterator i = monitors.iterator(); i.hasNext(); ) { + ((Monitor) i.next()).step( world ); + } + } + + + private void informDone() { + for ( Iterator i = monitors.iterator(); i.hasNext(); ) { + ((Monitor) i.next()).done( world ); + } + } + + + private void informReset() { + for ( Iterator i = monitors.iterator(); i.hasNext(); ) { + ((Monitor) i.next()).reset( world ); + } + } + + + public void step( int timestep ) { + energyHistory = new EnergyHistory( world, timestep ); + refill(); + executeExtinguishRequests(); + burn(); + cool(); + updateGrid(); + exchangeBuilding(); + // FIXED + cool(); + energyHistory.registerFinalEnergy( world ); + energyHistory.logSummary(); + } + + + private void cool() { + for ( Iterator i = world.getBuildings().iterator(); i.hasNext(); ) { + Building b = (Building) i.next(); + waterCooling( b ); + } + } + + + private void refill() { + for ( Iterator i = world.getFirebrigades().iterator(); i.hasNext(); ) { + FireBrigade fb = ((FireBrigade) i.next()); + if ( fb.refill() ) { + LOG.debug( "refilling fire brigade " + fb.getID() ); + } + } + } + + + private void executeExtinguishRequests() { + for ( Iterator i = world.getExtinguishIterator(); i.hasNext(); ) { + ExtinguishRequest er = (ExtinguishRequest) i.next(); + er.execute(); + } + world.clearExtinguishRequests(); + } + + + private void burn() { + for ( Building b : world.getBuildings() ) { + if ( b.getTemperature() >= b.getIgnitionPoint() && b.fuel > 0 + && b.isInflameable() ) { + float consumed = b.getConsum(); + if ( consumed > b.fuel ) { + consumed = b.fuel; + } + double oldFuel = b.fuel; + double oldEnergy = b.getEnergy(); + double oldTemp = b.getTemperature(); + b.setEnergy( b.getEnergy() + consumed ); + energyHistory.registerBurn( b, consumed ); + b.fuel -= consumed; + b.setPrevBurned( consumed ); + } else { + b.setPrevBurned( 0f ); + } + } + } + + + private void waterCooling( Building b ) { + double lWATER_COEFFICIENT = (b.getFieryness() > 0 && b.getFieryness() < 4 + ? WATER_COEFFICIENT + : WATER_COEFFICIENT * GAMMA); + boolean cond = false; + if ( b.getWaterQuantity() > 0 ) { + double oldEnergy = b.getEnergy(); + double oldTemp = b.getTemperature(); + double oldWater = b.getWaterQuantity(); + double dE = b.getTemperature() * b.getCapacity(); + if ( dE <= 0 ) { + return; + } + double effect = b.getWaterQuantity() * lWATER_COEFFICIENT; + int consumed = b.getWaterQuantity(); + if ( effect > dE ) { + cond = true; + double pc = 1 - ((effect - dE) / effect); + effect *= pc; + consumed *= pc; + } + b.setWaterQuantity( b.getWaterQuantity() - consumed ); + b.setEnergy( b.getEnergy() - effect ); + energyHistory.registerCool( b, effect ); + LOG.debug( "Building " + b.getID() + " water cooling" ); + LOG.debug( "Old energy: " + oldEnergy + ", old temperature: " + oldTemp + + ", old water: " + oldWater ); + LOG.debug( "Consumed " + consumed + " water: effect = " + effect ); + LOG.debug( "New energy: " + b.getEnergy() + ", new temperature: " + + b.getTemperature() + ", new water: " + b.getWaterQuantity() ); + } + } + + + private void exchangeBuilding() { + for ( Iterator i = world.getBuildings().iterator(); i.hasNext(); ) { + Building b = (Building) i.next(); + exchangeWithAir( b ); + } + double sumdt = 0; + Map<Building, Double> radiation = new HashMap<Building, Double>(); + for ( Iterator i = world.getBuildings().iterator(); i.hasNext(); ) { + Building b = (Building) i.next(); + double radEn = b.getRadiationEnergy(); + radiation.put( b, radEn ); + } + for ( Iterator i = world.getBuildings().iterator(); i.hasNext(); ) { + Building b = (Building) i.next(); + double radEn = radiation.get( b ); + Building[] bs = b.connectedBuilding; + float[] vs = b.connectedValues; + + for ( int c = 0; c < vs.length; c++ ) { + double oldEnergy = bs[c].getEnergy(); + double connectionValue = vs[c]; + double a = radEn * connectionValue; + double sum = oldEnergy + a; + bs[c].setEnergy( sum ); + energyHistory.registerRadiationGain( bs[c], a ); + } + b.setEnergy( b.getEnergy() - radEn ); + energyHistory.registerRadiationLoss( b, -radEn ); + } + } + + + private void exchangeWithAir( Building b ) { + // Give/take heat to/from air cells + double oldTemperature = b.getTemperature(); + double oldEnergy = b.getEnergy(); + double energyDelta = 0; + + for ( int[] nextCell : b.cells ) { + int cellX = nextCell[0]; + int cellY = nextCell[1]; + double cellCover = nextCell[2] / 100.0; + double cellTemp = world.getAirCellTemp( cellX, cellY ); + double dT = cellTemp - b.getTemperature(); + double energyTransferToBuilding = dT * AIR_TO_BUILDING_COEFFICIENT + * TIME_STEP_LENGTH * cellCover * world.SAMPLE_SIZE; + energyDelta += energyTransferToBuilding; + double newCellTemp = cellTemp - energyTransferToBuilding + / (AIR_CELL_HEAT_CAPACITY * world.SAMPLE_SIZE); + world.setAirCellTemp( cellX, cellY, newCellTemp ); + } + b.setEnergy( oldEnergy + energyDelta ); + energyHistory.registerAir( b, energyDelta ); + } + + + private void updateGrid() { + LOG.debug( "Updating air grid" ); + double[][] airtemp = world.getAirTemp(); + double[][] newairtemp = new double[airtemp.length][airtemp[0].length]; + for ( int x = 0; x < airtemp.length; x++ ) { + for ( int y = 0; y < airtemp[0].length; y++ ) { + double dt = (averageTemp( x, y ) - airtemp[x][y]); + double change = (dt * AIR_TO_AIR_COEFFICIENT * TIME_STEP_LENGTH); + newairtemp[x][y] = relTemp( airtemp[x][y] + change ); + if ( !(newairtemp[x][y] > -Double.MAX_VALUE + && newairtemp[x][y] < Double.MAX_VALUE) ) { + LOG.warn( "Value is not sensible: " + newairtemp[x][y] ); + newairtemp[x][y] = Double.MAX_VALUE * 0.75; + } + if ( newairtemp[x][y] == Double.NEGATIVE_INFINITY + || newairtemp[x][y] == Double.POSITIVE_INFINITY ) { + LOG.warn( "aha" ); + } + } + } + world.setAirTemp( newairtemp ); + // Disable on October 21, 2018 because the wind direction and speed was not + // correctly implemented. + // world.setAirTemp( getWindShift().shift( world.getAirTemp(), this ) ); + } + + + private double relTemp( double deltaT ) { + return Math.max( 0, deltaT * ENERGY_LOSS * TIME_STEP_LENGTH ); + } + + + private double averageTemp( int x, int y ) { + double rv = neighbourCellAverage( x, y ) / weightSummCells( x, y ); + return rv; + } + + + private double neighbourCellAverage( int x, int y ) { + double total = getTempAt( x + 1, y - 1 ); + total += getTempAt( x + 1, y ); + total += getTempAt( x + 1, y + 1 ); + total += getTempAt( x, y - 1 ); + total += getTempAt( x, y + 1 ); + total += getTempAt( x - 1, y - 1 ); + total += getTempAt( x - 1, y ); + total += getTempAt( x - 1, y + 1 ); + return total * WEIGHT_GRID; + } + + + private float weightSummCells( int x, int y ) { + return 8 * WEIGHT_GRID; + } + + + protected double getTempAt( int x, int y ) { + if ( x < 0 || y < 0 || x >= world.getAirTemp().length + || y >= world.getAirTemp()[0].length ) + return 0; + return world.getAirTemp()[x][y]; + } + + + public void setWind( float direction, float speed ) { + windShift = new WindShift( direction, speed, world.SAMPLE_SIZE ); + } + + + public WindShift getWindShift() { + if ( WIND_RANDOM > 0 && windShift != null ) { + float nd = (float) (windShift.direction + + windShift.direction * WIND_RANDOM * Rnd.get01()); + float ns = (float) (windShift.speed + + windShift.speed * WIND_RANDOM * Rnd.get01()); + setWind( nd, ns ); + } + if ( windShift == null || windShift.direction != WIND_DIRECTION + || windShift.speed != WIND_SPEED ) + setWind( WIND_DIRECTION, WIND_SPEED ); + return windShift; + } + + + private void loadVars() { + AIR_TO_BUILDING_COEFFICIENT = Float.parseFloat( + Configuration.getValue( "resq-fire.air_to_building_flow" ) ); + AIR_TO_AIR_COEFFICIENT = Float + .parseFloat( Configuration.getValue( "resq-fire.air_to_air_flow" ) ); + ENERGY_LOSS = Float + .parseFloat( Configuration.getValue( "resq-fire.energy_loss" ) ); + WATER_COEFFICIENT = Float.parseFloat( + Configuration.getValue( "resq-fire.water_thermal_capacity" ) ); + // Disable on October 21, 2018 because the wind direction and speed was not + // correctly implemented. + // WIND_SPEED = Integer + // .parseInt( Configuration.getValue( "resq-fire.wind_speed" ) ); + // WIND_DIRECTION = Float + // .parseFloat( Configuration.getValue( "resq-fire.wind_direction" ) ); + // WIND_RANDOM = Float + // .parseFloat( Configuration.getValue( "resq-fire.wind_random" ) ); + RADIATION_COEFFICENT = Float.parseFloat( + Configuration.getValue( "resq-fire.radiation_coefficient" ) ); + AIR_CELL_HEAT_CAPACITY = Float.parseFloat( + Configuration.getValue( "resq-fire.air_cell_heat_capacity" ) ); + ExtinguishRequest.MAX_WATER_PER_CYCLE = Integer.parseInt( + Configuration.getValue( "resq-fire.max_extinguish_power_sum" ) ); + ExtinguishRequest.MAX_DISTANCE = Integer + .parseInt( Configuration.getValue( "resq-fire.water_distance" ) ); + GAMMA = Float.parseFloat( Configuration.getValue( "resq-fire.gamma" ) ); + Rnd.setSeed( Long.parseLong( Configuration.getValue( "random.seed" ) ) ); + + } + + + public void initialize() { + try { + loadVars(); + } catch ( Exception e ) { + LOG.fatal( "invalid configuration, aborting", e ); + System.exit( -1 ); + } + + world.initialize(); + } + + + public void reset() { + loadVars(); + world.reset(); + informReset(); + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/simulator/WindShift.java b/modules/oldsims/firesimulator/simulator/WindShift.java new file mode 100644 index 0000000000000000000000000000000000000000..a27705ca19b56673984b922c9bc88f15bd18e293 --- /dev/null +++ b/modules/oldsims/firesimulator/simulator/WindShift.java @@ -0,0 +1,71 @@ +package firesimulator.simulator; + +/** + * @author Timo N�ssle + * + */ +public class WindShift { + + float speed; + + float direction; + + float directionDg; + + int[][] grid = new int[4][2]; + + float[] weights = new float[4]; + + + public WindShift( float direction, float speed, int gridSize ) { + this.speed = speed % gridSize; + directionDg = direction; + direction = direction % 360; + direction = (float) (direction / (360 / (2 * Math.PI))); + this.direction = direction; + float v_y = -((float) Math.cos( direction ) * speed); + float v_x = -((float) Math.sin( direction ) * speed); + float[][] points = new float[4][2]; + points[0][0] = v_x; + points[0][1] = v_y; + points[1][0] = v_x + gridSize; + points[1][1] = v_y; + points[2][0] = v_x + gridSize; + points[2][1] = v_y + gridSize; + points[3][0] = v_x; + points[3][1] = v_y + gridSize; + float areaTotal = gridSize * gridSize; + for ( int c = 0; c < 4; c++ ) { + float tx = grid[c][0] * gridSize; + float ty = grid[c][1] * gridSize; + float sx = points[0][0]; + float sy = points[0][1]; + float dx = gridSize - Math.abs( sx - tx ); + float dy = gridSize - Math.abs( sy - ty ); + float weight = (dx * dy) / areaTotal; + weights[c] = weight; + } + } + + + public float getDirection() { + return directionDg; + } + + + public double[][] shift( double[][] source, Simulator sim ) { + if ( speed == 0 ) + return source; + double[][] result = new double[source.length][source[0].length]; + for ( int x = 0; x < source.length; x++ ) + for ( int y = 0; y < source[0].length; y++ ) { + float temp = 0; + for ( int c = 0; c < 4; c++ ) { + temp += sim.getTempAt( x - grid[c][0], y - grid[c][1] ) * weights[c]; + System.out.println( weights[c] ); + } + result[x][y] = temp; + } + return result; + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/util/Configuration.java b/modules/oldsims/firesimulator/util/Configuration.java new file mode 100644 index 0000000000000000000000000000000000000000..70b1def77caa586215a80411edf86dfb50085770 --- /dev/null +++ b/modules/oldsims/firesimulator/util/Configuration.java @@ -0,0 +1,312 @@ +package firesimulator.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import firesimulator.world.Wall; + +import org.apache.log4j.Logger; + +public class Configuration { + private static final Logger LOG = Logger.getLogger(Configuration.class); + + private static final String PREFIX = "resq-fire."; + + + public class Prop{ + + private String name; + private String command; + private String description; + private String paramName; + private boolean paramReq; + private String value; + boolean active; + + public Prop(String name, String command, String description , String paramName, boolean paramReq,String def){ + this.name=name; + this.command=command; + this.description=description; + this.paramName=paramName; + this.paramReq=paramReq; + if(def!=null){ + value=def; + active=true; + }else{ + value=null; + active=false; + } + } + + public String getValue(){ + return value; + } + + public boolean isActive(){ + return active; + } + + public boolean validate(){ + if(paramReq) + if(value==null||value.length()==0)return false; + return true; + } + + public String getDescription(){ + if(description==null)return ""; + return "\n"+name+": "+command+" "+(paramName!=null?(!paramReq?"[":"")+"<"+paramName+">"+(!paramReq?"]":""):"")+"\n"+description+"\n"; + } + + } + + private static LinkedList Props =new LinkedList(); + private static final String CONFIG_TXT_PATH="."; + public static String VERSION="06.08.2005"; + + public void initialize(){ + Props.add(new Prop(PREFIX + "store","s","Stores the intial data from the kernel in the given file.","filename",true,null)); + Props.add(new Prop(PREFIX + "virtual","v","Use the virtual kernel instead of the rescue kernel.\nRequires a .scn file.","filename",true,null)); + Props.add(new Prop(PREFIX + "host","h","The host to connect to. Default host is localhost.","host",true,"localhost")); + Props.add(new Prop(PREFIX + "port","p","The port to connect to. Default port is 6000","port",true,"6000")); + Props.add(new Prop(PREFIX + "setup","stp","Uses the given setup file","filename",true,null)); + Props.add(new Prop(PREFIX + "csetup","cstp","Uses the given config.txt file","filename",true,null)); + Props.add(new Prop(PREFIX + "ray_rate","ray_rate","Number of emitted rays per mm while sampling. Default rate is "+Wall.RAY_RATE,"rate",true,Wall.RAY_RATE+"")); + Props.add(new Prop(PREFIX + "help","help","Prints this text and exits",null,false,null)); + //hidden parameters + Props.add(new Prop(PREFIX + "cell_size","cell_size",null,null,true,null)); + Props.add(new Prop(PREFIX + "max_ray_distance","max_ray_distance",null,null,true,null)); + Props.add(new Prop(PREFIX + "energy_loss","energy_loss",null,null,true,null)); + Props.add(new Prop(PREFIX + "air_to_air_flow","air_to_air_flow",null,null,true,null)); + Props.add(new Prop(PREFIX + "air_to_building_flow","air_to_building_flow",null,null,true,null)); + Props.add(new Prop(PREFIX + "air_cell_heat_capacity","air_cell_heat_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "wooden_capacity","wooden_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "wooden_energy","wooden_energy",null,null,true,null)); + Props.add(new Prop(PREFIX + "wooden_ignition","wooden_ignition",null,null,true,null)); + Props.add(new Prop(PREFIX + "wooden_burning","wooden_burning",null,null,true,null)); + Props.add(new Prop(PREFIX + "wooden_speed","wooden_speed",null,null,true,null)); + Props.add(new Prop(PREFIX + "steel_capacity","steel_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "steel_energy","steel_energy",null,null,true,null)); + Props.add(new Prop(PREFIX + "steel_ignition","steel_ignition",null,null,true,null)); + Props.add(new Prop(PREFIX + "steel_burning","steel_burning",null,null,true,null)); + Props.add(new Prop(PREFIX + "steel_speed","steel_speed",null,null,true,null)); + Props.add(new Prop(PREFIX + "concrete_capacity","concrete_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "concrete_energy","concrete_energy",null,null,true,null)); + Props.add(new Prop(PREFIX + "concrete_ignition","concrete_ignition",null,null,true,null)); + Props.add(new Prop(PREFIX + "concrete_burning","concrete_burning",null,null,true,null)); + Props.add(new Prop(PREFIX + "concrete_speed","concrete_speed",null,null,true,null)); + Props.add(new Prop(PREFIX + "max_extinguish_power_sum","max_extinguish_power_sum",null,null,true,null)); + Props.add(new Prop(PREFIX + "water_refill_rate","water_refill_rate",null,null,true,null)); + Props.add(new Prop(PREFIX + "water_hydrant_refill_rate","water_hydrant_refill_rate",null,null,true,null)); + Props.add(new Prop(PREFIX + "water_capacity","water_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "water_thermal_capacity","water_thermal_capacity",null,null,true,null)); + Props.add(new Prop(PREFIX + "water_distance","water_distance",null,null,true,null)); + Props.add(new Prop(PREFIX + "radiation_coefficient","radiation_coefficient",null,null,true,null)); + Props.add(new Prop(PREFIX + "wind_speed","wind_speed",null,null,true,null)); + Props.add(new Prop(PREFIX + "wind_direction","wind_direction",null,null,true,null)); + Props.add(new Prop(PREFIX + "wind_random","wind_random",null,null,true,null)); + Props.add(new Prop(PREFIX + "randomseed","randomseed",null,null,true,null)); + Props.add(new Prop("random.seed","random.seed",null,null,true,null)); + Props.add(new Prop(PREFIX + "refuge_inflammable","refuge_inflammable",null,null,true,null)); + Props.add(new Prop(PREFIX + "fire_station_inflammable","firestation_inflammable",null,null,true,null)); + Props.add(new Prop(PREFIX + "police_office_inflammable","policeoffice_inflammable",null,null,true,null)); + Props.add(new Prop(PREFIX + "ambulance_center_inflammable","ambulancecenter_inflammable",null,null,true,null)); + Props.add(new Prop(PREFIX + "gamma","gamma",null,null,true,null)); + Props.add(new Prop(PREFIX + "rays.dir","rays",null,null,true,"rays")); + Props.add(new Prop(PREFIX + "burn-rate-average","burn-rate-average",null,null,true,"0.2")); + Props.add(new Prop(PREFIX + "burn-rate-variance","burn-rate-variance",null,null,true,"0")); + } + + public void parse(String cmdLine){ + StringTokenizer st=new StringTokenizer(cmdLine,"-"); + try{ + while(st.hasMoreTokens()){ + String tok=st.nextToken(); + int index=tok.indexOf(" "); + String cmd; + if(index==-1){ + cmd=tok.trim(); + if(cmd.length()==0)continue; + Prop p =propForCmd(cmd); + p.active=true; + }else{ + cmd=tok.substring(0,index).trim(); + if(cmd.length()==0)continue; + Prop p =propForCmd(cmd); + p.active=true; + p.value=tok.substring(index).trim(); + } + } + }catch(Exception e){printHelpAndExit();} + if(isActive("help")){ + printHelpAndExit(); + } + } + + private void printHelpAndExit(){ + System.out.println("ResQ Firesimulator"); + System.out.println(VERSION); + System.out.println("author: Timo N�ssle\nemail: nuessle@informatik.uni-freiburg.de\n"); + System.out.println("java Main [-<option> <value>]*"); + for(Iterator i=Props.iterator();i.hasNext();) + System.out.print(((Prop)i.next()).getDescription()); + System.exit(0); + } + + private static Configuration.Prop propForCmd(String cmd){ + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + if(p.command.compareTo(cmd)==0)return p; + } + return null; + } + + public static boolean isActive(String name){ + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + if(p.name.compareTo(name)==0) return p.isActive(); + } + return false; + } + + public static String getValue(String name){ + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + if(p.name.compareTo(name)==0) return p.getValue(); + } + return null; + } + + + public void parse(String[] args) { + if(args.length<1)return; + String s=""; + for(int i=0;i<args.length;s+=" "+args[i],i++); + parse(s); + } + + + public static boolean loadSetup(String fileName) { + try { + FileInputStream fis=new FileInputStream(new File(fileName)); + Properties prop=new Properties(); + prop.load(fis); + fis.close(); + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + String val=prop.getProperty(p.command); + if(val!=null){ + p.value=val; + p.active=true; + } + } + } catch (Exception e) { + return false; + } + return true; + } + + public static void loadConfigTXT(String filename) { + String fname=CONFIG_TXT_PATH+File.separator+"config.txt"; + if(filename!=null) + fname=filename; + LOG.info("loading values from \""+fname+"\""); + try{ + Pattern comment=Pattern.compile("([^#]*)(#(.*))*",Pattern.DOTALL); + Pattern keyValue=Pattern.compile("([^:]*):(.*)",Pattern.DOTALL); + BufferedReader br=new BufferedReader(new FileReader(fname)); + Hashtable lines=new Hashtable(); + String line; + String key; + String value; + while((line=br.readLine())!=null){ + line=line.trim(); + Matcher m=comment.matcher(line); + if(m.matches()){ + Matcher gm=keyValue.matcher(m.group(1)); + if(gm.matches()&&gm.groupCount()==2){ + key=gm.group(1).trim(); + value=gm.group(2).trim(); + lines.put(key,value); + } + } + } + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + String name=p.name; + value=(String) lines.get(name); + if(value!=null){ + p.active=true; + p.value=value; + } + } + + }catch (Exception e) { + LOG.error("unable to load \""+fname+"\"", e); + } + + + } + + public static void setProperty(String name, String value, boolean state){ + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + if(p.name.compareTo(name)==0){ + p.value=value; + p.active=state; + break; + } + } + } + + public static List<String> getPropertyNames() { + List<String> result = new ArrayList<String>(); + for(Iterator i = Props.iterator(); i.hasNext();){ + Prop next = (Prop)i.next(); + result.add(next.name); + } + return result; + } + + public static void dump(){ + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + LOG.debug(p.command+"="+p.value+ "["+p.active+"]"); + } + } + + + public static void storeHiddenProps(String fileName) throws IOException { + Properties prop= new Properties(); + for(Iterator i=Props.iterator();i.hasNext();){ + Prop p=(Prop)i.next(); + if(p.description==null){ + if(p.value==null) { + LOG.debug(p.command); + } + prop.put(p.command,p.value); + } + } + if(!fileName.endsWith(".stp"))fileName+=".stp"; + File f=new File(fileName); + if(f.exists())f.delete(); + f.createNewFile(); + FileOutputStream fos=new FileOutputStream(f); + prop.store(fos,"fire simulator setup file"); + fos.close(); + } + + +} diff --git a/modules/oldsims/firesimulator/util/GeoTest.java b/modules/oldsims/firesimulator/util/GeoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..12afb036c44b89cdb71b340be1ca5b0a3a82ad3f --- /dev/null +++ b/modules/oldsims/firesimulator/util/GeoTest.java @@ -0,0 +1,103 @@ +package firesimulator.util; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JFrame; +import javax.swing.WindowConstants; + +/** + * @author tn + * + */ +public class GeoTest extends JFrame implements MouseListener{ + + private static final long serialVersionUID = 1L; + float p[]; + float[] cross; + int current=0; + Point c; + Point d; + + public static void main(String[] args) { + GeoTest gt=new GeoTest(); + gt.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + gt.setVisible(true); + } + + public GeoTest(){ + super("geotest"); + p=new float[]{50f,50f,300f,300f,100f,100f,200f,200f}; + addMouseListener(this); + setSize(500,500); + cross=null; + } + + public void update(Graphics g){ + g.setColor(Color.BLACK); + g.fillRect(0,0,getWidth(),getHeight()); + g.setColor(Color.RED); + g.drawLine((int)p[0],(int)p[1],(int)p[2],(int)p[3]); + g.setColor(Color.GREEN); + g.drawLine((int)p[4],(int)p[5],(int)p[6],(int)p[7]); + if(d!=null&&c!=null){ + g.setColor(Color.YELLOW); + g.drawLine(c.x,c.y,d.x,d.y); + } + if(cross!=null){ + g.setColor(Color.CYAN); + g.drawLine((int)cross[0]-10,(int)cross[1]-10,(int)cross[0]+10,(int)cross[1]+10); + g.drawLine((int)cross[0]-10,(int)cross[1]+10,(int)cross[0]+10,(int)cross[1]-10); + } + if(c!=null){ + g.setColor(Color.YELLOW); + g.drawOval(c.x-5,c.y-5,10,10); + } + } + + public void mouseClicked(MouseEvent e) { + if(e.getButton()==MouseEvent.BUTTON1){ + if(current>7)current=0; + p[current]=(float)e.getX(); + p[++current]=(float)e.getY(); + if(e.isControlDown()) p[current-1]=200f; + if(e.isShiftDown()) p[current-1]=400f; + current++; + } + findPoint(); + d=Geometry.getRndPoint(c,100d); + findIntersect(); + update(getGraphics()); + } + + private void findPoint() { + Point a=new Point((int)p[0],(int)p[1]); + Point b=new Point((int)p[2],(int)p[3]); + c=Geometry.getRndPoint(a,b); + } + + private void findIntersect() { + cross=Geometry.intersect(p); + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + + } + + + + + +} diff --git a/modules/oldsims/firesimulator/util/Geometry.java b/modules/oldsims/firesimulator/util/Geometry.java new file mode 100644 index 0000000000000000000000000000000000000000..a9d6e529d7cc3cf83e09e3a3e2e01e8e062f8615 --- /dev/null +++ b/modules/oldsims/firesimulator/util/Geometry.java @@ -0,0 +1,117 @@ +package firesimulator.util; + +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.geom.Point2D; + +import firesimulator.world.StationaryObject; + +/** + * @author tn + * + */ +public class Geometry { + + private static Rectangle rect=new Rectangle(0,0,0,0); + + public static int percent(float x1,float y1, float width, float height,Polygon p){ + int counter=0; + double dx=width/10; + double dy=height/10; + for(int i=0;i<10;i++) + for(int j=0;j<10;j++){ + if(p.contains(dx*i+x1,dy*j+y1))counter++; + } + return counter; + } + + public static boolean boundingTest(Polygon p,int x,int y,int w,int h){ + rect.setBounds(x,y,w,h); + return p.intersects(rect); + } + + public static Point intersect(Point a, Point b, Point c, Point d){ + float[] rv=intersect(new float[]{a.x,a.y,b.x,b.y,c.x,c.y,d.x,d.y}); + if(rv==null)return null; + return new Point((int)rv[0],(int)rv[1]); + } + + public static float[] intersect(float[] points){ + float dx1 = points[2] - points[0]; + float dy1 = points[3] - points[1]; + float dx2 = points[6] - points[4]; + float dy2 = points[7] - points[5]; + float s = (dx1 * (points[1] - points[5]) - dy1 * (points[0] - points[4])) / (dx1 * dy2 - dx2 * dy1); + float t = (dx2 * (points[1] - points[5]) - dy2 * (points[0] - points[4])) / (dx1 * dy2 - dx2 * dy1); + if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { + return new float[] {points[0] + (t * dx1), points[1] + (t * dy1)}; + } + return null; + } + + public static float[] getAffineFunction(float x1,float y1,float x2,float y2){ + if(x1==x2)return null; + float m=(y1-y2)/(x1-x2); + float b=y1-m*x1; + return new float[]{m,b}; + } + + public static float getLength(Point2D a, Point2D b){ + a.distance(b); + return 0; + } + + public static boolean inBounds(float bx1,float by1,float bx2, float by2, float x, float y){ + if(bx1<bx2){ + if(x<bx1||x>bx2)return false; + }else{ + if(x>bx1||x<bx2)return false; + } + if(by1<by2){ + if(y<by1||y>by2)return false; + }else{ + if(y>by1||y<by2)return false; + } + return true; + } + + /** + * Returns a random point on a line + * @param a One point defineing the line + * @param b The other point defineing the line + * @return A point between a and b + */ + public static Point getRndPoint(Point a, Point b){ + float[] mb=Geometry.getAffineFunction((float)a.x,(float)a.y,(float)b.x,(float)b.y); + float dx=(Math.max((float)a.x,(float)b.x)-Math.min((float)a.x,(float)b.x)); + dx*=Rnd.get01(); + dx+=Math.min((float)a.x,(float)b.x); + if(mb==null){ + //vertical line + int p = Math.max(a.y,b.y)-Math.min(a.y,b.y); + p = (int) (p*Rnd.get01()); + p = p + Math.min(a.y,b.y); + return new Point(a.x,p); + } + float y=mb[0]*dx+mb[1]; + Point rtv=new Point((int)dx,(int)y); + if(rtv == null){ + System.currentTimeMillis(); + } + return rtv; + } + + public static Point getRndPoint(Point a, double length){ + double angel=Rnd.get01()*2d*Math.PI; + double x=Math.sin(angel)*length; + double y=Math.cos(angel)*length; + return new Point((int)x+a.x,(int)y+a.y); + } + + public static int dist(StationaryObject o1, StationaryObject o2){ + double x = o1.getX()-o2.getX(); + double y = o1.getY()-o2.getY(); + return (int )Math.sqrt(Math.pow(x,2)+Math.pow(y,2)); + } +} diff --git a/modules/oldsims/firesimulator/util/Rnd.java b/modules/oldsims/firesimulator/util/Rnd.java new file mode 100644 index 0000000000000000000000000000000000000000..3540254c81549b37e7ec4f728c2f6e640ff801d6 --- /dev/null +++ b/modules/oldsims/firesimulator/util/Rnd.java @@ -0,0 +1,29 @@ +package firesimulator.util; + +import java.util.Random; + +/** + * @author tn + * + */ +public class Rnd { + + private static Random rnd; + + + public static void setSeed( long seed ) { + if ( seed <= 0 ) + rnd = new Random( System.currentTimeMillis() ); + else + rnd = new Random( seed ); + } + + + public static double get01() { + if ( rnd == null ) { + rnd = new Random(); + } + return rnd.nextDouble(); + } + +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/util/TempView.java b/modules/oldsims/firesimulator/util/TempView.java new file mode 100644 index 0000000000000000000000000000000000000000..e6a168be5178190ab89b42782236edd502a21b38 --- /dev/null +++ b/modules/oldsims/firesimulator/util/TempView.java @@ -0,0 +1,62 @@ +package firesimulator.util; + +import java.awt.Color; +import java.awt.Graphics; + +import javax.swing.JFrame; +import javax.swing.WindowConstants; + +/** + * @author tn + * + */ +public class TempView extends JFrame{ + + /** + * + */ + private static final long serialVersionUID = 1L; + static float[][] keyColors={{1000,1,0,0}, + {300,1,1,0}, + {100,0,1,0}, + {50,0,0,1}, + {20,0,0,0.8f}, + {0,0,0,0}}; + + public static void main(String[] args) { + TempView gt=new TempView(); + gt.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + gt.setVisible(true); + } + + public void update(Graphics g){ + g.setColor(Color.WHITE); + g.fillRect(0,0,getWidth(),getHeight()); + for(int x=0;x<=200;x++){ + Color c=interpolator(x*5,keyColors); + g.setColor(c); + g.drawLine(50,250-x,100,250-x); + } + g.setColor(Color.BLACK); + for(int x=0;x<=200;x+=40){ + g.drawLine(105,250-x,110,250-x); + g.drawString(""+x*5+"�C",115,255-x); + } + } + + private Color interpolator(float temp,float[][]keys){ + float[][] keyColors=keys; + int pos=0; + do{ + pos++; + }while(keyColors[pos][0]>temp&&pos<keyColors.length); + if(pos>=keyColors.length) + return Color.BLUE; + float pc=(temp-keyColors[pos][0])/(keyColors[pos-1][0]-keyColors[pos][0]); + float red=(keyColors[pos-1][1]-keyColors[pos][1])*pc+keyColors[pos][1]; + float green=(keyColors[pos-1][2]-keyColors[pos][2])*pc+keyColors[pos][2]; + float blue=(keyColors[pos-1][3]-keyColors[pos][3])*pc+keyColors[pos][3]; + return new Color(red,green,blue); + } + +} diff --git a/modules/oldsims/firesimulator/world/AmbulanceCenter.java b/modules/oldsims/firesimulator/world/AmbulanceCenter.java new file mode 100644 index 0000000000000000000000000000000000000000..467142f1ee262ea44ec59c20cd7e0d5916333d92 --- /dev/null +++ b/modules/oldsims/firesimulator/world/AmbulanceCenter.java @@ -0,0 +1,32 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class AmbulanceCenter extends Building { + + /** + * @param id + */ + public AmbulanceCenter(int id) { + super(id); + } + + public String getType(){ + return "AMBULANCE_CENTRE"; + } + + + public boolean isInflameable(){ + return Building.AMBULANCE_INFALMEABLE; + } + + public int getFieryness(){ + if(isInflameable()) + return super.getFieryness(); + return 0; + } +} diff --git a/modules/oldsims/firesimulator/world/AmbulanceTeam.java b/modules/oldsims/firesimulator/world/AmbulanceTeam.java new file mode 100644 index 0000000000000000000000000000000000000000..25e0113150864330d69c7979751785e83466bd45 --- /dev/null +++ b/modules/oldsims/firesimulator/world/AmbulanceTeam.java @@ -0,0 +1,19 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class AmbulanceTeam extends MovingObject { + + public AmbulanceTeam(int id) { + super(id); + } + + public String getType(){ + return "AMBULANCE_TEAM"; + } + +} diff --git a/modules/oldsims/firesimulator/world/Building.java b/modules/oldsims/firesimulator/world/Building.java new file mode 100644 index 0000000000000000000000000000000000000000..5adf0b4fbe4759a74be7c895b1bdd5a7b77388b2 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Building.java @@ -0,0 +1,540 @@ +package firesimulator.world; + +import java.awt.Polygon; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.log4j.Logger; +import org.uncommons.maths.number.NumberGenerator; +import org.uncommons.maths.random.GaussianGenerator; + +import firesimulator.simulator.Simulator; +import firesimulator.util.Configuration; +import firesimulator.util.Geometry; + +/** + * @author tn + * + */ +public class Building extends StationaryObject { + private static final Logger LOG = Logger.getLogger(Building.class); + + private static final double STEFAN_BOLTZMANN_CONSTANT = 0.000000056704; + + public static int WATER_EXTINGUISH_PARAM; + public static float woodSpeed; + public static float steelSpeed; + public static float concreteSpeed; + public static float WATER_CAPCITY; + public static boolean POLICE_INFALMEABLE = false; + public static boolean AMBULANCE_INFALMEABLE = false; + public static boolean FIRE_INFALMEABLE = false; + public static boolean REFUGE_INFALMEABLE = false; + public NumberGenerator<Double> burnRate; + public boolean fierynessChanged; + private int waterQuantity; + private int floors = 1; + private int attributes = 0; + private int ignition = 0; + protected int fieryness = 0; + private int brokenness = 0; + public int[][] cells; + private int[] entrances; + private int code = 0; + private float buildingAreaGround = 0; + private float buildingAreaTotal = 0; + private int[] apexes; + private Polygon polygon; + public float fuel; + private float initFuel; + private float prevBurned; + public float volume; + public float capacity; + private double energy; + static final int FLOOR_HEIGHT = 3; + public float cooling = 0; + public Collection walls; + public Hashtable connectedBuildings; + public Building[] connectedBuilding; + public float[] connectedValues; + public double totalWallArea; + private int lwater = 0; + private int lwTime = -1; + private boolean wasEverWatered = false; + public boolean inflameable = true; + + public static float woodCapacity = 4; + public static float steelCapacity = 4; + public static float concreteCapacity = 4; + public static float woodIgnition = 400; + public static float steelIgnition = 400; + public static float concreteIgnition = 400; + public static float woodEnergie = 1; + public static float steelEnergie = 1; + public static float concreteEnergie = 1; + public static float woodBurning = 800; + public static float steelBurning = 800; + public static float concreteBurning = 800; + + public static final int NORMAL = 0; + public static final int HEATING = 1; + public static final int BURNING = 2; + public static final int COOLING_DOWN = 3; + public static final int EXTINGUISHED = 5; + public static final int BURNED_DOWN = 4; + + public Building(int id) { + super(id); + polygon = null; + connectedBuildings = new Hashtable(30); + initFuel = -1; + prevBurned = 0; + java.util.Random random = new java.util.Random(Long.valueOf(Configuration.getValue("random.seed")).longValue()); + burnRate = new GaussianGenerator( + Double.valueOf(Configuration.getValue("resq-fire.burn-rate-average")).doubleValue(), + Double.valueOf(Configuration.getValue("resq-fire.burn-rate-variance")).doubleValue(), random); + } + + public float getBurningTemp() { + switch (code) { + case 0: + return woodBurning; + case 1: + return steelBurning; + default: + return concreteBurning; + } + } + + public float getIgnitionPoint() { + switch (code) { + case 0: + return woodIgnition; + case 1: + return steelIgnition; + default: + return concreteIgnition; + } + } + + public void ignite() { + energy = getCapacity() * getIgnitionPoint() * 1.5; + } + + public float getBuildingAreaGround() { + return buildingAreaGround; + } + + public int getFloors() { + return floors; + } + + public void initialize(World world) { + initWalls(world); + setFieryness(0); + setWaterQuantity(0); + fierynessChanged = false; + if (polygon == null) { + polygon = new Polygon(); + for (int n = 0; n < apexes.length; n++) + polygon.addPoint(apexes[n], apexes[++n]); + } + volume = buildingAreaGround * floors * FLOOR_HEIGHT; + fuel = getInitialFuel(); + setCapacity(volume * getThermoCapacity()); + energy = 0; + initFuel = -1; + prevBurned = 0; + lwTime = -1; + lwater = 0; + wasEverWatered = false; + LOG.debug("Initialised building " + id + ": ground area = " + buildingAreaGround + ", floors = " + floors + + ", volume = " + volume + ", initial fuel = " + fuel + ", energy capacity = " + getCapacity()); + } + + public void reset(World w) { + setFieryness(0); + setWaterQuantity(0); + initialize(w); + } + + private void initWalls(World world) { + if (walls != null) + return; + totalWallArea = 0; + walls = new LinkedList(); + int fx = apexes[0]; + int fy = apexes[1]; + int lx = fx; + int ly = fy; + for (int n = 2; n < apexes.length; n++) { + int tx = apexes[n]; + int ty = apexes[++n]; + Wall w = new Wall(lx, ly, tx, ty, this); + if (w.validate()) { + walls.add(w); + totalWallArea += FLOOR_HEIGHT * 1000 * w.length; + } else + LOG.warn("Ignoring odd wall at building " + getID()); + lx = tx; + ly = ty; + } + Wall w = new Wall(lx, ly, fx, fy, this); + walls.add(w); + world.allWalls.addAll(walls); + totalWallArea = totalWallArea / 1000000d; + } + + public int hashCode() { + return id; + } + + public void initWallValues(World world) { + int totalHits = 0; + int totalRays = 0; + int selfHits = 0; + int strange = 0; + for (Iterator w = walls.iterator(); w.hasNext();) { + Wall wall = (Wall) w.next(); + wall.findHits(world); + totalHits += wall.hits; + selfHits += wall.selfHits; + totalRays += wall.rays; + strange = wall.strange; + } + int c = 0; + connectedBuilding = new Building[connectedBuildings.size()]; + connectedValues = new float[connectedBuildings.size()]; + float base = totalRays; + for (Enumeration e = connectedBuildings.keys(); e.hasMoreElements(); c++) { + Building b = (Building) e.nextElement(); + Integer value = (Integer) connectedBuildings.get(b); + connectedBuilding[c] = b; + connectedValues[c] = value.floatValue() / base; + } + LOG.debug("{" + (((float) totalHits) * 100 / ((float) totalRays)) + "," + totalRays + "," + totalHits + "," + + selfHits + "," + strange + "}"); + } + + public float getInitialFuel() { + if (initFuel < 0) { + initFuel = (float) (getFuelDensity() * volume); + } + return initFuel; + } + + private float getFuelDensity() { + switch (code) { + case 0: + return woodEnergie; + case 1: + return steelEnergie; + default: + return concreteEnergie; + } + } + + private float getThermoCapacity() { + switch (code) { + case 0: + return woodCapacity; + case 1: + return steelCapacity; + default: + return concreteCapacity; + } + } + + public Polygon getPolygon() { + return polygon; + } + + public String getType() { + return "BUILDING"; + } + + public void setAttributes(int atrb) { + this.attributes = atrb; + } + + public int getCode() { + return code; + } + + public void setIgnition(int ignition) { + this.ignition = ignition; + } + + public int getIgnition() { + return ignition; + } + + public void setFieryness(int fieryness) { + this.fieryness = fieryness; + } + + public float getFuel() { + return fuel; + } + + public int getFieryness() { + if (!isInflameable()) + return 0; + if (getTemperature() >= getIgnitionPoint()) { + if (fuel >= getInitialFuel() * 0.66) + return 1; // burning, slightly damaged + if (fuel >= getInitialFuel() * 0.33) + return 2; // burning, more damaged + if (fuel > 0) + return 3; // burning, severly damaged + } + if (fuel == getInitialFuel()) + if (wasEverWatered) + return 4; // not burnt, but watered-damaged + else + return 0; // not burnt, no water damage + if (fuel >= getInitialFuel() * 0.66) + return 5; // extinguished, slightly damaged + if (fuel >= getInitialFuel() * 0.33) + return 6; // extinguished, more damaged + if (fuel > 0) + return 7; // extinguished, severely damaged + return 8; // completely burnt down + } + + public void setBrokenness(int brk) { + this.brokenness = brk; + } + + public void setEntrances(int[] ent) { + this.entrances = ent; + } + + public void setCode(int code) { + this.code = code; + } + + public void setBuildingAreaGround(float area) { + this.buildingAreaGround = area; + } + + public void setBuildingAreaTotal(float area) { + this.buildingAreaTotal = area; + } + + public void setApexes(int[] apx) { + this.apexes = apx; + } + + public int[] getApexes() { + return apexes; + } + + public void setFloors(int floors) { + this.floors = floors; + } + + public void findCells(World w) { + LinkedList tmp = new LinkedList(); + for (int x = 0; x < w.getAirTemp().length; x++) + for (int y = 0; y < w.getAirTemp()[0].length; y++) { + int xv = x * w.SAMPLE_SIZE + w.getMinX(); + int yv = y * w.SAMPLE_SIZE + w.getMinY(); + if (Geometry.boundingTest(polygon, xv, yv, w.SAMPLE_SIZE, w.SAMPLE_SIZE)) { + int pc = Geometry.percent((float) xv, (float) yv, (float) w.SAMPLE_SIZE, (float) w.SAMPLE_SIZE, polygon); + if (pc > 0) { + tmp.add(Integer.valueOf(x)); + tmp.add(Integer.valueOf(y)); + tmp.add(Integer.valueOf(pc)); + Object[] o = new Object[] { this, Float.valueOf(pc) }; + w.gridToBuilding[x][y].add(o); + } + } + } + if (tmp.size() > 0) { + cells = new int[tmp.size() / 3][3]; + Iterator i = tmp.iterator(); + for (int c = 0; c < cells.length; c++) { + cells[c][0] = ((Integer) i.next()).intValue(); + cells[c][1] = ((Integer) i.next()).intValue(); + cells[c][2] = ((Integer) i.next()).intValue(); + } + } else { + LOG.warn(getID() + " has no cell"); + LOG.warn("Sample size: " + w.SAMPLE_SIZE); + LOG.warn("World min X, Y: " + w.getMinX() + ", " + w.getMinY()); + LOG.warn("Air grid size: " + w.getAirTemp().length + " x " + w.getAirTemp()[0].length); + LOG.warn("Building polygon: "); + for (int i = 0; i < apexes.length; i += 2) { + LOG.warn(apexes[i] + ", " + apexes[i + 1]); + } + int expectedCellX = (apexes[0] - w.getMinX()) / w.SAMPLE_SIZE; + int expectedCellY = (apexes[1] - w.getMinY()) / w.SAMPLE_SIZE; + LOG.warn("Building should be in cell " + expectedCellX + ", " + expectedCellY); + for (int x = 0; x < w.getAirTemp().length; x++) { + for (int y = 0; y < w.getAirTemp()[0].length; y++) { + int xv = x * w.SAMPLE_SIZE + w.getMinX(); + int yv = y * w.SAMPLE_SIZE + w.getMinY(); + if (Geometry.boundingTest(polygon, xv, yv, w.SAMPLE_SIZE, w.SAMPLE_SIZE)) { + LOG.warn("Cell " + x + ", " + y); + LOG.warn("boundingTest(polygon, " + xv + ", " + yv + ", " + w.SAMPLE_SIZE + ", " + w.SAMPLE_SIZE + ") = " + + Geometry.boundingTest(polygon, xv, yv, w.SAMPLE_SIZE, w.SAMPLE_SIZE)); + LOG.warn("pc = " + + Geometry.percent((float) xv, (float) yv, (float) w.SAMPLE_SIZE, (float) w.SAMPLE_SIZE, polygon)); + int counter = 0; + double dx = w.SAMPLE_SIZE / 100; + double dy = w.SAMPLE_SIZE / 100; + for (int i = 0; i < 100; i++) { + for (int j = 0; j < 100; j++) { + double testX = dx * i + xv; + double testY = dy * j + yv; + if (polygon.contains(dx * i + xv, dy * j + yv)) { + counter++; + LOG.warn("Point " + testX + ", " + testY + " is inside"); + } + } + } + LOG.warn("Counted " + counter + " interior points"); + } + } + } + } + } + + public double getTemperature() { + double rv = energy / getCapacity(); + if (Double.isNaN(rv)) { + LOG.warn("Building " + id + " getTemperature returned NaN"); + new RuntimeException().printStackTrace(); + LOG.warn("Energy: " + energy); + LOG.warn("Capacity: " + getCapacity()); + LOG.warn("Volume: " + volume); + LOG.warn("Thermal capacity: " + getThermoCapacity()); + LOG.warn("Ground area: " + buildingAreaGround); + LOG.warn("Floors: " + floors); + } + if (rv == Double.NaN || rv == Double.POSITIVE_INFINITY || rv == Double.NEGATIVE_INFINITY) + rv = Double.MAX_VALUE * 0.75; + return rv; + } + + public String codeToString() { + switch (code) { + case 0: + return "wooden"; + case 1: + return "steelframe"; + default: + return "concret"; + } + } + + public int getLastWater() { + return lwater; + } + + public boolean getLastWatered() { + return lwTime == World.getWorld().getTime(); + } + + public boolean wasEverWatered() { + return wasEverWatered; + } + + public int getWaterQuantity() { + return waterQuantity; + } + + public void setWaterQuantity(int i) { + if (i > waterQuantity) { + lwTime = World.getWorld().getTime(); + lwater = i - waterQuantity; + wasEverWatered = true; + } + waterQuantity = i; + } + + public float getCapacity() { + return capacity; + } + + public void setCapacity(float f) { + capacity = f; + } + + public String toString() { + String rv = "building " + getID() + "\n"; + for (Iterator i = walls.iterator(); i.hasNext(); rv += i.next() + "\n") + ; + return rv; + } + + public double getRadiationEnergy() { + double t = getTemperature() + 293; // Assume ambient temperature is 293 Kelvin. + double radEn = (t * t * t * t) * totalWallArea * Simulator.RADIATION_COEFFICENT * STEFAN_BOLTZMANN_CONSTANT; + if (id == 23545) { + LOG.debug("Getting radiation energy for building " + id); + LOG.debug("t = " + t); + LOG.debug("t^4 = " + (t * t * t * t)); + LOG.debug("Total wall area: " + totalWallArea); + LOG.debug("Radiation coefficient: " + Simulator.RADIATION_COEFFICENT); + LOG.debug("Stefan-Boltzmann constant: " + STEFAN_BOLTZMANN_CONSTANT); + LOG.debug("Radiation energy: " + radEn); + LOG.debug("Building energy: " + getEnergy()); + } + if (radEn == Double.NaN || radEn == Double.POSITIVE_INFINITY || radEn == Double.NEGATIVE_INFINITY) + radEn = Double.MAX_VALUE * 0.75; + if (radEn > getEnergy()) { + radEn = getEnergy(); + } + return radEn; + } + + public boolean isBuilding(int x, int y) { + return getX() == x && getY() == y; + } + + public double getEnergy() { + if (energy == Double.NaN || energy == Double.POSITIVE_INFINITY || energy == Double.NEGATIVE_INFINITY) + energy = Double.MAX_VALUE * 0.75d; + return energy; + } + + public void setEnergy(double energy) { + if (energy == Double.NaN || energy == Double.POSITIVE_INFINITY || energy == Double.NEGATIVE_INFINITY) { + energy = Double.MAX_VALUE * 0.75d; + } + this.energy = energy; + } + + public float getConsum() { + if (fuel == 0) { + return 0; + } + float tf = (float) (getTemperature() / 1000f); + float lf = getFuel() / getInitialFuel(); + float f = (float) (tf * lf * burnRate.nextValue()); + if (f < 0.005f) + f = 0.005f; + return getInitialFuel() * f; + } + + public float getPrevBurned() { + return prevBurned; + } + + public void setPrevBurned(float prevBurned) { + this.prevBurned = prevBurned; + } + + public void setInflameable(boolean inflameable) { + this.inflameable = inflameable; + } + + public boolean isInflameable() { + return inflameable; + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/world/Car.java b/modules/oldsims/firesimulator/world/Car.java new file mode 100644 index 0000000000000000000000000000000000000000..657e849a52f2a601a3c81f904ddec190d639c986 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Car.java @@ -0,0 +1,19 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class Car extends MovingObject { + + public Car(int id) { + super(id); + } + + public String getType(){ + return "CAR"; + } + +} diff --git a/modules/oldsims/firesimulator/world/Civilian.java b/modules/oldsims/firesimulator/world/Civilian.java new file mode 100644 index 0000000000000000000000000000000000000000..2585d9b26416bbba01c00ba51a7906c0313d6828 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Civilian.java @@ -0,0 +1,20 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class Civilian extends MovingObject { + + + public Civilian(int id) { + super(id); + } + + public String getType(){ + return "CIVILIAN"; + } + +} diff --git a/modules/oldsims/firesimulator/world/Edge.java b/modules/oldsims/firesimulator/world/Edge.java new file mode 100644 index 0000000000000000000000000000000000000000..b1d5e6e903ff7b38e7ee0554d85f08965e1aadac --- /dev/null +++ b/modules/oldsims/firesimulator/world/Edge.java @@ -0,0 +1,43 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public abstract class Edge extends StationaryObject { + + int headID; + int tailID; + int Length; + + public Edge(int id) { + super(id); + } + + public void setHead(int id){ + headID=id; + } + + public void setTail(int id){ + tailID=id; + } + + public void setLength(int length){ + this.Length=length; + } + + protected int getTailID() { + return tailID; + } + + protected int getHeadID() { + return headID; + } + + protected int length() { + return Length; + } + +} diff --git a/modules/oldsims/firesimulator/world/FireBrigade.java b/modules/oldsims/firesimulator/world/FireBrigade.java new file mode 100644 index 0000000000000000000000000000000000000000..239253ecdc20178d22b3d5628f0b2c4e1f06fa52 --- /dev/null +++ b/modules/oldsims/firesimulator/world/FireBrigade.java @@ -0,0 +1,116 @@ +package firesimulator.world; + +import java.io.DataOutputStream; +import java.io.IOException; + +import rescuecore.OutputBuffer; + +public class FireBrigade extends MovingObject { + + public static int REFILL_QUANTITY; + public static int REFILL_HYDRANT_QUANTITY; + public static int MAX_WATER_QUANTITY; + private int initialWaterQuantity; + private int waterQuantity; + private int waterUsed; + private boolean changed; + + public FireBrigade(int id) { + super(id); + initialWaterQuantity = 0; + waterQuantity = 0; + waterUsed = 0; + changed = false; + } + + public String getType() { + return "FIRE_BRIGADE"; + } + + public void setInitialWaterQuantity(int quantity) { + initialWaterQuantity = quantity; + waterQuantity = quantity; + } + + public int getWaterQuantity() { + return waterQuantity; + } + + public int getWaterUsed() { + return waterUsed; + } + + public void setWaterQuantity(int quantity) { + waterQuantity = quantity; + changed = true; + } + + public void addWaterUsed(int quantity) { + waterUsed += quantity; + } + + public void nextCycle() { + waterUsed = 0; + changed = false; + } + + public void reset() { + waterQuantity = initialWaterQuantity; + waterUsed = 0; + changed = false; + } + + public boolean hasChanged() { + return changed; + } + public boolean refillInHydrant() { + //System.out.println(getID()+" Location is:"+getLocation()+" "+getLocation().getType()); + if(!(getLocation().getType().equals("HYDRANT"))) + return false; + + for (Object next : world.getFirebrigades()) { + FireBrigade firebrigade = (FireBrigade)next; + if(firebrigade.getLocationID()==this.getLocationID()&& + firebrigade.getID()<this.getID()) + return false; + } + + if (getWaterQuantity() + REFILL_HYDRANT_QUANTITY> MAX_WATER_QUANTITY) + setWaterQuantity(MAX_WATER_QUANTITY); + else + setWaterQuantity(getWaterQuantity() + REFILL_HYDRANT_QUANTITY); + return true; + } + public boolean refillInRefuge() { + if (!(getLocation().isRefuge())) + return false; + + int fr = ((Refuge) getLocation()).getFieryness(); + if (fr == 3 || fr == 6 || fr == 7) + return false; + + + if (getWaterQuantity() + REFILL_QUANTITY > MAX_WATER_QUANTITY) { + setWaterQuantity(MAX_WATER_QUANTITY); + } else { + setWaterQuantity(getWaterQuantity() + REFILL_QUANTITY); + } + return true; + } + public boolean refill() { +// if (!getCurrentAction().equals("AK_REST")){ +// return false; +// }unusable because the old simulator don't know the last action + + if (getLocation() == null) + return false; + + if(refillInHydrant()) + return true; + + if(refillInRefuge()) + return true; + + return false; + } +} diff --git a/modules/oldsims/firesimulator/world/FireStation.java b/modules/oldsims/firesimulator/world/FireStation.java new file mode 100644 index 0000000000000000000000000000000000000000..c062fc99c3365fd10d74522c1f24411e6e6af8eb --- /dev/null +++ b/modules/oldsims/firesimulator/world/FireStation.java @@ -0,0 +1,29 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class FireStation extends Building { + + public FireStation(int id) { + super(id); + } + + public String getType(){ + return "FIRE_STATION"; + } + + public boolean isInflameable(){ + return Building.FIRE_INFALMEABLE; + } + + public int getFieryness(){ + if(isInflameable()) + return super.getFieryness(); + return 0; + } + +} diff --git a/modules/oldsims/firesimulator/world/Hydrant.java b/modules/oldsims/firesimulator/world/Hydrant.java new file mode 100644 index 0000000000000000000000000000000000000000..077d1e0bde824159ba95a8d3913318b99236a057 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Hydrant.java @@ -0,0 +1,14 @@ +package firesimulator.world; + +public class Hydrant extends StationaryObject { + + public Hydrant(int id) { + super(id); + } + + @Override + public String getType() { + return "HYDRANT"; + } + +} diff --git a/modules/oldsims/firesimulator/world/MovingObject.java b/modules/oldsims/firesimulator/world/MovingObject.java new file mode 100644 index 0000000000000000000000000000000000000000..bdd2e032aa20881fb522ad27b30a8abebb498ebd --- /dev/null +++ b/modules/oldsims/firesimulator/world/MovingObject.java @@ -0,0 +1,117 @@ +package firesimulator.world; + +/** + * @author tn + * + */ +public abstract class MovingObject extends RealObject { + + private int stamina=0; + private int hp=0; + private int damage=0; + private int buriedness=0; + private int positionId=0; + private int positionExtra=0; + private RescueObject position; + protected World world = null; + private String currentAction; + private int currentActionLastChange; + private int x; + private int y; + + public MovingObject(int id) { + super(id); + currentAction = "AK_REST"; + currentActionLastChange = 0; + } + + public void setWorld(World w) { + world = w; + } + + + public void setPositionId(int id){ + positionId=id; + if (world != null) { + position = world.getObject(positionId); + } + } + + public int getPositionId(){ + return positionId; + } + + public void setPositionExtra(int pos){ + positionExtra=pos; + } + + public int getPositionExtra(){ + return positionExtra; + } + + public void setPosition(){ + + } + + public StationaryObject getLocation(){ + if (position instanceof MovingObject) + return ((MovingObject) position).getLocation(); + else if (position instanceof StationaryObject) + return (StationaryObject) position; + else + return null; + } + + public int getLocationID(){ + if (position instanceof MovingObject) + return ((MovingObject) position).getLocationID(); + else if (position instanceof StationaryObject) + return position.id; + else + return -1; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void setX(int x) { + this.x = x; + } + + public void setY(int y) { + this.y = y; + } + + public void setStamina(int stamina){ + this.stamina=stamina; + } + + public void setHp(int hp){ + this.hp=hp; + } + + public void setDamage(int damage){ + this.damage=damage; + } + + public void setBuriedness(int buriedness){ + this.buriedness=buriedness; + } + + public String getCurrentAction() { + if(world.getTime()>currentActionLastChange) + return "AK_REST"; + return currentAction; + } + + public void setCurrentAction(String action) { + currentActionLastChange = world.getTime(); + currentAction = action; + } + +} diff --git a/modules/oldsims/firesimulator/world/Node.java b/modules/oldsims/firesimulator/world/Node.java new file mode 100644 index 0000000000000000000000000000000000000000..87845a8e5d41cf679769e0493d213c23cc8c13c0 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Node.java @@ -0,0 +1,20 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public abstract class Node extends StationaryObject { + + int[] edgesID; + + public Node(int id) { + super(id); + } + + public void setEdges(int[] value){ + edgesID=value; + } +} diff --git a/modules/oldsims/firesimulator/world/PoliceForce.java b/modules/oldsims/firesimulator/world/PoliceForce.java new file mode 100644 index 0000000000000000000000000000000000000000..6f9dbb39ce8648de53e425458e61430fe810c6f6 --- /dev/null +++ b/modules/oldsims/firesimulator/world/PoliceForce.java @@ -0,0 +1,19 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class PoliceForce extends MovingObject { + + public PoliceForce(int id) { + super(id); + } + + public String getType(){ + return "POLICE_FORCE"; + } + +} diff --git a/modules/oldsims/firesimulator/world/PoliceOffice.java b/modules/oldsims/firesimulator/world/PoliceOffice.java new file mode 100644 index 0000000000000000000000000000000000000000..396e075f5239a9f1d2abc3736757f77c04834a98 --- /dev/null +++ b/modules/oldsims/firesimulator/world/PoliceOffice.java @@ -0,0 +1,30 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class PoliceOffice extends Building { + + + public PoliceOffice(int id) { + super(id); + } + + public String getType(){ + return "POLICE_OFFICE"; + } + + + public boolean isInflameable(){ + return Building.POLICE_INFALMEABLE; + } + + public int getFieryness(){ + if(isInflameable()) + return super.getFieryness(); + return 0; + } +} diff --git a/modules/oldsims/firesimulator/world/RealObject.java b/modules/oldsims/firesimulator/world/RealObject.java new file mode 100644 index 0000000000000000000000000000000000000000..85547c4d92eb70790f46d830d4680dd26c5f7efc --- /dev/null +++ b/modules/oldsims/firesimulator/world/RealObject.java @@ -0,0 +1,20 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public abstract class RealObject extends RescueObject{ + + + public RealObject(int id) { + super(id); + } + + abstract public int getX(); + + abstract public int getY(); + +} diff --git a/modules/oldsims/firesimulator/world/Refuge.java b/modules/oldsims/firesimulator/world/Refuge.java new file mode 100644 index 0000000000000000000000000000000000000000..3d63b53f6501a84bb08b30ca4c4197339e76fd29 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Refuge.java @@ -0,0 +1,34 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class Refuge extends Building { + + public Refuge(int id) { + super(id); + } + + public String getType(){ + return "REFUGE"; + } + + public boolean isRefuge() { + return true; + } + + + public boolean isInflameable(){ + return Building.REFUGE_INFALMEABLE; + } + + public int getFieryness(){ + if(isInflameable()) + return super.getFieryness(); + return 0; + } + +} diff --git a/modules/oldsims/firesimulator/world/RescueObject.java b/modules/oldsims/firesimulator/world/RescueObject.java new file mode 100644 index 0000000000000000000000000000000000000000..f29ef8f01747d6fba4c7c487cb630bcdc1a3e447 --- /dev/null +++ b/modules/oldsims/firesimulator/world/RescueObject.java @@ -0,0 +1,38 @@ +package firesimulator.world; + +import org.apache.log4j.Logger; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public abstract class RescueObject implements WorldConstants { + int id; + + public RescueObject(int id){ + this.id=id; + } + + public boolean isStationary(){ + return false; + } + + public int hashCode(){ + return id; + } + + public boolean equals( Object o ) { + if (!(o instanceof RescueObject)) { + return false; + } + return this.id == ((RescueObject)o).id; + } + + public abstract String getType(); + + public int getID(){ + return id; + } +} diff --git a/modules/oldsims/firesimulator/world/Road.java b/modules/oldsims/firesimulator/world/Road.java new file mode 100644 index 0000000000000000000000000000000000000000..73bf2c2d28d1703a6c111e91e4dcd828d82c8cde --- /dev/null +++ b/modules/oldsims/firesimulator/world/Road.java @@ -0,0 +1,71 @@ +package firesimulator.world; + +import org.apache.log4j.Logger; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class Road extends Edge { + private static final Logger LOG = Logger.getLogger(Road.class); + + int width; + int block; + int linesToHead; + int linesToTail; + StreetNode head; + StreetNode tail; + + public Road(int id) { + super(id); + head=null; + tail=null; + } + + public void initialize(World world){ + head=((StreetNode)world.getObject(getHeadID())); + tail=((StreetNode)world.getObject(getTailID())); + if(head==null||tail==null){ + LOG.fatal("Error: head or tail of an streetnode did not exist. exiting"); + System.exit(1); + } + } + + public void setHead(StreetNode node){ + head=node; + } + + public void setTail(StreetNode node){ + tail=node; + } + + public StreetNode getHead(){ + return head; + } + + public StreetNode getTail(){ + return tail; + } + + public String getType(){ + return "ROAD"; + } + + public void setWidth(int width){ + this.width=width; + } + + public void setBlock(int block){ + this.block=block; + } + + public void setLinesToHead(int lines){ + linesToHead=lines; + } + + public void setLinesToTail(int lines){ + linesToTail=lines; + } +} diff --git a/modules/oldsims/firesimulator/world/StationaryObject.java b/modules/oldsims/firesimulator/world/StationaryObject.java new file mode 100644 index 0000000000000000000000000000000000000000..93c69613a6357728eaeb2c0c94110e064337c403 --- /dev/null +++ b/modules/oldsims/firesimulator/world/StationaryObject.java @@ -0,0 +1,35 @@ +package firesimulator.world; + +public abstract class StationaryObject extends RealObject { + + private int x; + private int y; + + public StationaryObject(int id) { + super(id); + } + + public boolean isStationary(){ + return true; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void setX(int x){ + this.x=x; + } + + public void setY(int y){ + this.y=y; + } + + public boolean isRefuge() { + return false; + } +} diff --git a/modules/oldsims/firesimulator/world/StreetNode.java b/modules/oldsims/firesimulator/world/StreetNode.java new file mode 100644 index 0000000000000000000000000000000000000000..4f0a15083bd7e9953212d9fafee7d5fe1cc72fad --- /dev/null +++ b/modules/oldsims/firesimulator/world/StreetNode.java @@ -0,0 +1,14 @@ +package firesimulator.world; + + +public class StreetNode extends Node { + + public StreetNode(int id) { + super(id); + } + + public String getType(){ + return "NODE"; + } + +} diff --git a/modules/oldsims/firesimulator/world/Wall.java b/modules/oldsims/firesimulator/world/Wall.java new file mode 100644 index 0000000000000000000000000000000000000000..5b01118e653bb54a8e2338626096be41df3d1ba1 --- /dev/null +++ b/modules/oldsims/firesimulator/world/Wall.java @@ -0,0 +1,98 @@ +package firesimulator.world; + +import java.awt.Point; +import java.util.Iterator; + +import org.apache.log4j.Logger; + +/** + * @author tn + * + */ +public class Wall { + private static final Logger LOG = Logger.getLogger(Wall.class); + + public static int MAX_SAMPLE_DISTANCE = 50000; + public int x1; + public int y1; + public int x2; + public int y2; + public Building owner; + public int rays; + public int hits; + public int selfHits; + public int strange; + public static float RAY_RATE = 0.01f; + public double length; + Point a; + Point b; + + public Wall(int x1, int y1, int x2, int y2, Building owner) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + a = new Point(x1, y1); + b = new Point(x2, y2); + length = a.distance(b); + rays = (int) Math.ceil(length * RAY_RATE); + hits = 0; + this.owner = owner; + } + + public boolean validate() { + return !(a.x == b.x && a.y == b.y); + } + + public void findHits(World world) { + selfHits = 0; + strange = 0; + for (int emitted = 0; emitted < rays; emitted++) { + // creating ray + Point start = firesimulator.util.Geometry.getRndPoint(a, b); + if (start == null) { + strange++; + LOG.debug("strange -> " + a.x + "," + a.y + "/" + b.x + "," + b.y); + continue; + } + Point end = firesimulator.util.Geometry.getRndPoint(start, MAX_SAMPLE_DISTANCE); + // intersect + Wall closest = null; + double minDist = Double.MAX_VALUE; + for (Iterator it = world.allWalls.iterator(); it.hasNext();) { + Wall other = (Wall) it.next(); + if (other == this) + continue; + Point cross = firesimulator.util.Geometry.intersect(start, end, other.a, other.b); + if (cross != null) { + if (cross.distance(start) < minDist) { + minDist = cross.distance(start); + closest = other; + } + } + } + if (closest == null) { + // Nothing was hit + continue; + } + if (closest.owner == this.owner) { + // The source building was hit + selfHits++; + } + if (closest != this && closest != null && closest.owner != owner) { + hits++; + Integer value = (Integer) owner.connectedBuildings.get(closest.owner); + int temp = 0; + if (value != null) { + temp = value.intValue(); + } + temp++; + owner.connectedBuildings.put(closest.owner, Integer.valueOf(temp)); + } + } + } + + public String toString() { + return "wall (" + a.x + "," + a.y + ")-(" + b.x + "," + b.y + "), length=" + length + "mm, rays=" + rays; + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/world/World.java b/modules/oldsims/firesimulator/world/World.java new file mode 100644 index 0000000000000000000000000000000000000000..2442b29b36a016d4f66c9bf233ca24baab2ed8d0 --- /dev/null +++ b/modules/oldsims/firesimulator/world/World.java @@ -0,0 +1,390 @@ +package firesimulator.world; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.log4j.Logger; + +import firesimulator.util.Configuration; + +/** + * @author tn + * + */ +public class World implements WorldConstants { + private static final Logger LOG = Logger.getLogger(World.class); + + private Hashtable pool; + private Collection extinguishRequests; + private int time; + private Collection updatelist; + private Collection<Building> buildings; + private Collection firebrigades; + private int maxX; + private int maxY; + private int minX; + private int minY; + private double[][] airTemp; + public ArrayList[][] gridToBuilding; + public int SAMPLE_SIZE = 5000; + public float AIR_CAPACITY = 0.2f; + public float AIR_HEIGHT = 30; + public int CAPACITY; + public float maxDist; + private boolean isInitialized; + public Collection allWalls; + private Long hashValue; + private static World me; + + public World() { + me = this; + hashValue = null; + pool = new Hashtable(); + allWalls = new LinkedList(); + extinguishRequests = new LinkedList(); + updatelist = new LinkedList(); + firebrigades = new HashSet(); + buildings = new HashSet<Building>(); + maxX = Integer.MIN_VALUE; + maxY = Integer.MIN_VALUE; + minX = Integer.MAX_VALUE; + minY = Integer.MAX_VALUE; + isInitialized = false; + } + + public static World getWorld() { + return me; + } + + public int getMaxX() { + return maxX; + } + + public Iterator getExtinguishIterator() { + return extinguishRequests.iterator(); + } + + public void addExtinguishRequest(Object request) { + extinguishRequests.add(request); + } + + public void clearExtinguishRequests() { + extinguishRequests.clear(); + for (Iterator i = firebrigades.iterator(); i.hasNext();) { + FireBrigade fb = (FireBrigade) i.next(); + fb.nextCycle(); + } + } + + public boolean isIntialized() { + return isInitialized; + } + + public int getMaxY() { + return maxY; + } + + public int getMinX() { + return minX; + } + + public int getMinY() { + return minY; + } + + private void loadVars() { + SAMPLE_SIZE = Integer.valueOf(Configuration.getValue("resq-fire.cell_size")).intValue(); + Building.concreteBurning = Float.valueOf(Configuration.getValue("resq-fire.concrete_burning")).floatValue(); + Building.concreteCapacity = Float.valueOf(Configuration.getValue("resq-fire.concrete_capacity")).floatValue(); + Building.concreteEnergie = Float.valueOf(Configuration.getValue("resq-fire.concrete_energy")).floatValue(); + Building.concreteIgnition = Float.valueOf(Configuration.getValue("resq-fire.concrete_ignition")).floatValue(); + Building.concreteSpeed = Float.valueOf(Configuration.getValue("resq-fire.concrete_speed")).floatValue(); + Building.steelBurning = Float.valueOf(Configuration.getValue("resq-fire.steel_burning")).floatValue(); + Building.steelCapacity = Float.valueOf(Configuration.getValue("resq-fire.steel_capacity")).floatValue(); + Building.steelEnergie = Float.valueOf(Configuration.getValue("resq-fire.steel_energy")).floatValue(); + Building.steelIgnition = Float.valueOf(Configuration.getValue("resq-fire.steel_ignition")).floatValue(); + Building.steelSpeed = Float.valueOf(Configuration.getValue("resq-fire.steel_speed")).floatValue(); + Building.woodBurning = Float.valueOf(Configuration.getValue("resq-fire.wooden_burning")).floatValue(); + Building.woodCapacity = Float.valueOf(Configuration.getValue("resq-fire.wooden_capacity")).floatValue(); + Building.woodEnergie = Float.valueOf(Configuration.getValue("resq-fire.wooden_energy")).floatValue(); + Building.woodIgnition = Float.valueOf(Configuration.getValue("resq-fire.wooden_ignition")).floatValue(); + Building.woodSpeed = Float.valueOf(Configuration.getValue("resq-fire.wooden_speed")).floatValue(); + Building.FIRE_INFALMEABLE = Boolean.valueOf(Configuration.getValue("resq-fire.fire_station_inflammable")) + .booleanValue(); + Building.AMBULANCE_INFALMEABLE = Boolean.valueOf(Configuration.getValue("resq-fire.ambulance_center_inflammable")) + .booleanValue(); + Building.POLICE_INFALMEABLE = Boolean.valueOf(Configuration.getValue("resq-fire.police_office_inflammable")) + .booleanValue(); + Building.REFUGE_INFALMEABLE = Boolean.valueOf(Configuration.getValue("resq-fire.refuge_inflammable")) + .booleanValue(); + Wall.RAY_RATE = Float.valueOf(Configuration.getValue("resq-fire.ray_rate")).floatValue(); + Wall.MAX_SAMPLE_DISTANCE = Integer.valueOf(Configuration.getValue("resq-fire.max_ray_distance")).intValue(); + FireBrigade.REFILL_QUANTITY = Integer.valueOf(Configuration.getValue("resq-fire.water_refill_rate")).intValue(); + FireBrigade.REFILL_HYDRANT_QUANTITY = Integer.valueOf(Configuration.getValue("resq-fire.water_hydrant_refill_rate")) + .intValue(); + FireBrigade.MAX_WATER_QUANTITY = Integer.valueOf(Configuration.getValue("resq-fire.water_capacity")).intValue(); + } + + public void initialize() { + LOG.info("World initialising"); + loadVars(); + allWalls.clear(); + clearExtinguishRequests(); + initializeBuildings(); + // initializeRoads(); + initializeAir(); + igniteGISFires(); + isInitialized = true; + LOG.info("World initialised"); + } + + private void initializeBuildings() { + for (Building b : buildings) { + int[] ap = b.getApexes(); + for (int n = 0; n < ap.length; n++) { + if (ap[n] > maxX) + maxX = ap[n]; + if (ap[n] < minX) + minX = ap[n]; + n++; + if (ap[n] > maxY) + maxY = ap[n]; + if (ap[n] < minY) + minY = ap[n]; + } + b.initialize(this); + } + maxDist = (float) Math.sqrt(((maxX - minX) * (maxX - minX)) + ((maxY - minY) * (maxY - minY))); + initRayValues(); + } + + private void initRayValues() { + long hash = hash(); + boolean loaded = false; + String fname = Configuration.getValue("resq-fire.rays.dir") + "/" + hash + ".rays"; + try { + File f = new File(fname); + BufferedReader br = new BufferedReader(new FileReader(f)); + float rayDens = Float.parseFloat(br.readLine()); + String nl; + while (null != (nl = br.readLine())) { + int x = Integer.parseInt(nl); + int y = Integer.parseInt(br.readLine()); + int quantity = Integer.parseInt(br.readLine()); + Building[] bl = new Building[quantity]; + float[] wght = new float[quantity]; + for (int c = 0; c < quantity; c++) { + int ox = Integer.parseInt(br.readLine()); + int oy = Integer.parseInt(br.readLine()); + bl[c] = (Building) getBuilding(ox, oy); + wght[c] = Float.parseFloat(br.readLine()); + } + Building b = getBuilding(x, y); + b.connectedBuilding = bl; + b.connectedValues = wght; + } + loaded = true; + LOG.info("loaded radiation sample file \"" + fname + "\""); + } catch (Exception e) { + LOG.warn("unable to load radiation sample file \"" + fname + "\", sampling:"); + int n = 0; + long t1 = System.currentTimeMillis(); + for (Building b : buildings) { + LOG.info("building " + b.getID() + " (" + (n++) + " of " + buildings.size() + ") "); + b.initWallValues(this); + long dt = System.currentTimeMillis() - t1; + dt = dt / n; + dt = dt * (buildings.size() - n); + long sec = dt / (1000); + long min = (sec / 60) % 60; + long hour = sec / (60 * 60); + sec = sec % 60; + LOG.info(" time left: ca. " + hour + ":" + min + ":" + sec); + } + } + try { + if (!loaded) { + File f = new File(fname); + f.createNewFile(); + BufferedWriter bw = new BufferedWriter(new FileWriter(f)); + bw.write(Wall.RAY_RATE + "\n"); + for (Building b : buildings) { + bw.write(b.getX() + "\n"); + bw.write(b.getY() + "\n"); + bw.write(b.connectedBuilding.length + "\n"); + for (int c = 0; c < b.connectedBuilding.length; c++) { + bw.write(b.connectedBuilding[c].getX() + "\n"); + bw.write(b.connectedBuilding[c].getY() + "\n"); + bw.write(b.connectedValues[c] + "\n"); + } + } + bw.close(); + LOG.info("wrote radiation sample file \"" + fname + "\""); + } + } catch (Exception e) { + LOG.error("error while writting radiation sample file \"" + fname + "\"", e); + } + } + + private Building getBuilding(int x, int y) { + for (Building b : buildings) { + if (b.isBuilding(x, y)) + return b; + } + LOG.error("parser error"); + throw new NullPointerException(); + } + + public float getMaxDistance() { + return maxDist; + } + + private void initializeAir() { + LOG.info("World width: " + (maxX - minX) + "mm"); + LOG.info("World height: " + (maxY - minY) + "mm"); + int xSamples = 1 + (maxX - minX) / SAMPLE_SIZE; + int ySamples = 1 + (maxY - minY) / SAMPLE_SIZE; + LOG.info("grid cell size=" + SAMPLE_SIZE + "mm, x*y=" + xSamples + "*" + ySamples + " = " + (xSamples * ySamples)); + airTemp = new double[xSamples][ySamples]; + for (int x = 0; x < airTemp.length; x++) + for (int y = 0; y < airTemp[x].length; y++) + airTemp[x][y] = 0; + CAPACITY = (int) (SAMPLE_SIZE * SAMPLE_SIZE * AIR_HEIGHT * AIR_CAPACITY) / 1000000; + // assign buildings + gridToBuilding = new ArrayList[xSamples][ySamples]; + for (int x = 0; x < gridToBuilding.length; x++) + for (int y = 0; y < gridToBuilding[0].length; y++) + gridToBuilding[x][y] = new ArrayList(); + for (Building b : buildings) { + b.findCells(this); + } + } + + public double[][] getAirTemp() { + return airTemp; + } + + public void setAirTemp(double[][] a) { + airTemp = a; + } + + public void setAirCellTemp(int x, int y, double temp) { + airTemp[x][y] = temp; + } + + public double getAirCellTemp(int x, int y) { + return airTemp[x][y]; + } + + public Collection<Building> getBuildings() { + return buildings; + } + + public void addUpdate(RescueObject obj) { + updatelist.add(obj); + } + + public void clearUpdates() { + updatelist.clear(); + } + + public Collection getUpdates() { + return updatelist; + } + + public int countObjects() { + return pool.size(); + } + + public int getTime() { + return time; + } + + public RescueObject getObject(int ID) { + return (RescueObject) pool.get(Integer.valueOf(ID)); + } + + public void putObject(RescueObject obj) { + pool.put(Integer.valueOf(obj.getID()), obj); + if (obj instanceof FireBrigade) { + firebrigades.add(obj); + } + if (obj instanceof Building) { + buildings.add((Building) obj); + } + // Moving objects need the world to get their position + if (obj instanceof MovingObject) { + ((MovingObject) obj).setWorld(this); + } + } + + public void setTime(int time) { + this.time = time; + } + + public void reset() { + loadVars(); + setTime(0); + resetAir(); + for (Iterator i = buildings.iterator(); i.hasNext(); ((Building) i.next()).reset(this)) + ; + for (Iterator i = firebrigades.iterator(); i.hasNext(); ((FireBrigade) i.next()).reset()) + ; + igniteGISFires(); + } + + private void resetAir() { + for (int x = 0; x < airTemp.length; x++) + for (int y = 0; y < airTemp[x].length; y++) + airTemp[x][y] = 0; + } + + public void igniteGISFires() { + for (Iterator it = getBuildings().iterator(); it.hasNext();) { + Building b = (Building) it.next(); + if (b.getIgnition() != 0) { + b.ignite(); + addUpdate(b); + } + } + } + + public Collection getFirebrigades() { + return firebrigades; + } + + public void setFirebrigades(Collection collection) { + firebrigades = collection; + } + + public void printSummary() { + LOG.debug("objects total: " + countObjects()); + } + + public long hash() { + if (hashValue == null) { + long sum = 0; + for (Iterator i = buildings.iterator(); i.hasNext();) { + Building b = (Building) i.next(); + int[] ap = b.getApexes(); + for (int c = 0; c < ap.length; c++) { + if (Long.MAX_VALUE - sum <= ap[c]) { + sum = 0; + } + sum += ap[c]; + } + } + hashValue = Long.valueOf(sum); + } + return hashValue.longValue(); + } +} \ No newline at end of file diff --git a/modules/oldsims/firesimulator/world/WorldConstants.java b/modules/oldsims/firesimulator/world/WorldConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..d57296f5c78e1d31e2f03118c8efa00a581dab0d --- /dev/null +++ b/modules/oldsims/firesimulator/world/WorldConstants.java @@ -0,0 +1,92 @@ +package firesimulator.world; + +/** + * @author tn + * + */ +public interface WorldConstants { + + public final static int TYPE_NULL = 0; + public final static int TYPE_WORLD = 0x01; + public final static int TYPE_ROAD = 0x02; + public final static int TYPE_RIVER = 0x03; + public final static int TYPE_NODE = 0x04; + public final static int TYPE_RIVER_NODE = 0x05; + public final static int TYPE_BUILDING = 0x20; + public final static int TYPE_REFUGE = 0x21; + public final static int TYPE_FIRE_STATION = 0x22; + public final static int TYPE_AMBULANCE_CENTER = 0x23; + public final static int TYPE_POLICE_OFFICE = 0x24; + public final static int TYPE_CIVILIAN = 0x40; + public final static int TYPE_CAR = 0x41; + public final static int TYPE_FIRE_BRIGADE = 0x42; + public final static int TYPE_AMBULANCE_TEAM = 0x43; + public final static int TYPE_POLICE_FORCE = 0x44; + + public final static int PROPERTY_NULL = 0; + public final static int PROPERTY_MIN = 1; + public final static int PROPERTY_START_TIME = 1; + public final static int PROPERTY_LONGITUDE = 2; + public final static int PROPERTY_LATITUDE = 3; + public final static int PROPERTY_WIND_FORCE = 4; + public final static int PROPERTY_WIND_DIRECTION = 5; + + public final static int PROPERTY_HEAD = 6; + public final static int PROPERTY_TAIL = 7; + public final static int PROPERTY_LENGTH = 8; + + public final static int PROPERTY_ROAD_KIND = 9; + public final static int PROPERTY_CARS_PASS_TO_HEAD = 10; + public final static int PROPERTY_CARS_PASS_TO_TAIL = 11; + public final static int PROPERTY_HUMANS_PASS_TO_HEAD = 12; + public final static int PROPERTY_HUMANS_PASS_TO_TAIL = 13; + public final static int PROPERTY_WIDTH = 14; + public final static int PROPERTY_BLOCK = 15; + public final static int PROPERTY_REPAIR_COST = 16; + public final static int PROPERTY_MEDIAN_STRIP = 17; + public final static int PROPERTY_LINES_TO_HEAD = 18; + public final static int PROPERTY_LINES_TO_TAIL = 19; + public final static int PROPERTY_WIDTH_FOR_WALKERS = 20; + public final static int PROPERTY_SIGNAL = 21; + public final static int PROPERTY_SHORTCUT_TO_TURN = 22; + public final static int PROPERTY_POCKET_TO_TURN_ACROSS = 23; + public final static int PROPERTY_SIGNAL_TIMING = 24; + + public final static int PROPERTY_X = 25; + public final static int PROPERTY_Y = 26; + public final static int PROPERTY_EDGES = 27; + + public final static int PROPERTY_FLOORS = 28; + public final static int PROPERTY_BUILDING_ATTRIBUTES = 29; + public final static int PROPERTY_IGNITION = 30; + public final static int PROPERTY_FIERYNESS = 31; + public final static int PROPERTY_BROKENNESS = 32; + public final static int PROPERTY_ENTRANCES = 33; + public final static int PROPERTY_BUILDING_CODE = 34; + public final static int PROPERTY_BUILDING_AREA_GROUND = 35; + public final static int PROPERTY_BUILDING_AREA_TOTAL = 36; + public final static int PROPERTY_BUILDING_APEXES = 37; + + public final static int PROPERTY_POSITION = 38; + public final static int PROPERTY_POSITION_EXTRA = 39; + public final static int PROPERTY_DIRECTION = 40; + public final static int PROPERTY_POSITION_HISTORY = 41; + public final static int PROPERTY_STAMINA = 42; + public final static int PROPERTY_HP = 43; + public final static int PROPERTY_DAMAGE = 44; + public final static int PROPERTY_BURIEDNESS = 45; + public final static int PROPERTY_WATER_QUANTITY = 46; + + public final static int PROPERTY_MAX = 46; + + static final int AK_EXTINGUISH = 0x86; + static final int AK_REST = 0x80; + static final int AK_MOVE = 0x81; + static final int AK_LOAD = 0x82; + static final int AK_UNLOAD = 0x83; + static final int AK_SAY = 0x84; + static final int AK_TELL = 0x85; + // static final int AK_STRETCH = 0x87; + static final int AK_RESCUE = 0x88; + static final int AK_CLEAR = 0x89; +} diff --git a/modules/oldsims/firesimulator/world/WorldInfo.java b/modules/oldsims/firesimulator/world/WorldInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b0137408de7d573f902bee1e17ce532ae7f53fa6 --- /dev/null +++ b/modules/oldsims/firesimulator/world/WorldInfo.java @@ -0,0 +1,19 @@ +package firesimulator.world; + +/** + * @author tn + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class WorldInfo extends RescueObject { + + public WorldInfo(int id) { + super(id); + } + + public String getType(){ + return "WORLD"; + } + +} diff --git a/modules/oldsims/rescuecore/Agent.java b/modules/oldsims/rescuecore/Agent.java new file mode 100644 index 0000000000000000000000000000000000000000..d0b35d38c5495add9717cc9c61e03048b2576531 --- /dev/null +++ b/modules/oldsims/rescuecore/Agent.java @@ -0,0 +1,377 @@ + +/* + * Last change: $Date: 2004/07/11 22:26:27 $ + * $Revision: 1.30 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.io.*; +import rescuecore.debug.*; +import rescuecore.commands.*; + +/** + This is the base class for all agents. This class handles messages from the server, provides a memory of the simulation environment and convenience methods for path planning etc. This class also enforces the message limits imposed by the robocup rescue rules. + <p>Agent implementations should provide at least one of the following three constructors: + <ol><li>A no-arg constructor - e.g. MyAgent() + <li>A String[] constructor - e.g. MyAgent(String[] args) + <li>A constructor that takes one or more String arguments - e.g. MyAgent(String arg1, String arg2) + </ol> + The reason for this is that the AgentSystem allows arguments to be passed to the Agent via the command line. When creating an instance of the agent it first looks for any constructor that accepts the right number of String arguments, followed by the String[] constructor. Failing that, the no-arg constructor will be used. + <p> + For example, assuming we have the three constructors mentioned above, if the command line provides two arguments then the AgentSystem will use the MyAgent(String arg1, String arg2) constructor. If only one argument is provided then the MyAgent(String[] args) constructor is used. + */ +public abstract class Agent extends RescueComponent { + private int[] agentTypes; + protected int type; + protected int id; + protected int timeStep; + protected Memory memory; + /* + private int numReceived, numSent; + private int sendMax,receiveMax; + */ + private int tempID; + private volatile boolean running; + + private static int NEXT_ID = 0; + + // private LogWriter logWriter; + // private File logFile; + // private int lastLogTime = -1; + protected boolean debug = false; + + /** + Create a new agent of a particular type. + @param types The entity types this agent wants. + */ + protected Agent(int... types) { + this.agentTypes = types; + id = -1; + timeStep = -1; + this.type = -1; + // numReceived = 0; + // numSent = 0; + tempID = ++NEXT_ID; + // tempID = (int)(Math.random()*Integer.MAX_VALUE); + } + + public final int getComponentType() { + return RescueConstants.COMPONENT_TYPE_AGENT; + } + + public final Command generateConnectCommand() { + return new AKConnect(0,tempID,getClass().getName(),agentTypes); + } + + protected void appendCommand(Command c){ + super.appendCommand(c); + if(debug) + logObject(c); + } + + public final boolean handleConnectOK(Command c) { + KAConnectOK ok = (KAConnectOK)c; + int requestID = ok.getRequestID(); + if (requestID==tempID) { + id = ok.getAgentID(); + System.out.println("Connect succeeded for "+tempID+". Kernel assigned id:"+id); + try { + RescueObject[] knowledge = ok.getKnowledge(); + // Initialise + initialise(knowledge); + // Send AK_ACKNOWLEDGE + RescueMessage ack = new RescueMessage(); + ack.append(new AKAcknowledge(requestID, id)); + sendMessage(ack); + } + catch (Exception e) { + System.out.println(e); + e.printStackTrace(); + } + timeStep = 0; + running = true; + return true; + } + else { + System.out.println("Received a KA_CONNECT_OK for agent "+requestID+", but I'm listening for a reply for "+tempID); + } + return false; + } + + public final String handleConnectError(Command c) { + KAConnectError error = (KAConnectError)c; + int requestID = error.getRequestID(); + String reason = error.getReason(); + if (requestID==tempID) + return reason; + else + System.out.println("Received a KA_CONNECT_ERROR ("+reason+") for agent "+requestID+", but I'm listening for a reply for "+tempID); + return null; + } + + public boolean isRunning() { + return running; + } + + public void shutdown() { + running = false; + } + + public final void handleMessage(Command c) { + // System.out.println("Handling "+c); + switch (c.getType()) { + case RescueConstants.KA_SENSE: + handleSense((KASense)c); + break; + case RescueConstants.KA_HEAR: + case RescueConstants.KA_HEAR_SAY: + case RescueConstants.KA_HEAR_TELL: + handleHear(c); + break; + case RescueConstants.KA_CONNECT_OK: + if (running) { + // Someone obviously didn't get our AK_ACKNOWLEDGE + KAConnectOK ok = (KAConnectOK)c; + int requestID = ok.getRequestID(); + System.out.println(this+" just received a KA_CONNECT_OK to "+requestID+" - my tempID is "+tempID); + if (requestID==tempID) { + int newID = ok.getAgentID(); + System.out.println("Old ID: "+id+", new ID: "+newID); + id = newID; + RescueMessage ack = new RescueMessage(); + ack.append(new AKAcknowledge(requestID, id)); + sendMessage(ack); + } + } + break; + default: + handleOtherMessage(c); + break; + } + logObject(c); + } + + protected void handleOtherMessage(Command c) { + System.out.println("Timestep "+timeStep+": "+this+" received a weird command: "+Handy.getCommandTypeName(c.getType())); + } + + /** + Handle a KA_SENSE message + @param c The KA_SENSE Command object + */ + private void handleSense(Command c) { + // System.out.println("Last timestep ("+timeStep+") "+this+" sent "+numSent+" messages and received "+numReceived); + KASense sense = (KASense)c; + // numSent = numReceived = 0; + try { + int newTimeStep = sense.getTime(); + if (newTimeStep < timeStep) System.err.println(this+" just moved back in time! It was timestep "+timeStep+" and now it's timestep "+newTimeStep); + if (newTimeStep > timeStep+1) System.err.println(this+" just skipped ahead in time! It was timestep "+timeStep+" and now it's timestep "+newTimeStep); + timeStep = newTimeStep; + memory.update(sense); // Update the memory + } + catch (Exception e) { + e.printStackTrace(); + } + // long start = System.currentTimeMillis(); + sense(); + // long end = System.currentTimeMillis(); + // System.out.println("Sense took "+(end-start)+"ms for "+this); + flushCommands(); + // Flush the log + DebugWriter.flush(this); + } + + /** + Handle a KA_HEAR (or KA_HEAR_SAY, or KA_HEAR_TELL) message + @param hear The KA_HEAR Command object. + @see RescueConstants#KA_HEAR + @see RescueConstants#KA_HEAR_SAY + @see RescueConstants#KA_HEAR_TELL + */ + + private void handleHear(Command c) { + KAHear hear = (KAHear)c; + int toID = hear.getToID(); + int fromID = hear.getFromID(); + int length = hear.getLength(); + byte[] msg = hear.getData(); + byte channel = hear.getChannel(); + // System.out.println(Handy.getCommandTypeName(type)+" received by "+id+" from "+fromID+" - have already accepted "+numReceived+" messages of "+receiveMax+" this timestep"); + + // if (willListenHear(fromID) && canListen()) { + // System.out.println("Hear from "+fromID+" to "+toID+" on channel "+channel); + hear(fromID,msg,channel); + // ++numReceived; + // } + } + + /* + private boolean canListen() { + if (numReceived < receiveMax) return true; + System.err.println("WARNING: "+this+" tried to receive too many messages in timestep "+timeStep+" (maximum="+receiveMax+")"); + return false; + } + */ + + private RescueObject me() { + return memory.lookup(id); + } + + public String toString() { + if (!running) return "Unconnected agent. Temporary ID: "+tempID; + return Handy.getTypeName(type)+" ("+id+")"; + } + + /** + Get this agents Memory + @return The agents Memory + */ + public final Memory getMemory() { + return memory; + } + + /** + Get the type of RescueObject that this agent represents + @see RescueConstants#TYPE_CIVILIAN + @see RescueConstants#TYPE_CAR + @see RescueConstants#TYPE_FIRE_BRIGADE + @see RescueConstants#TYPE_FIRE_STATION + @see RescueConstants#TYPE_POLICE_FORCE + @see RescueConstants#TYPE_POLICE_OFFICE + @see RescueConstants#TYPE_AMBULANCE_TEAM + @see RescueConstants#TYPE_AMBULANCE_CENTER + */ + public final int getType() { + return type; + } + + /** + Get this agent's unique id, assigned by the kernel + */ + public final int getID() { + return id; + } + + /** + Enable debugging + @param file The File to log information to. + */ + protected void enableDebug(String target) { + debug = true; + DebugWriter.register(this,this.toString(),target); + } + + /** + Disable debugging + */ + protected void disableDebug() { + debug = false; + } + + /** + * Writes an Update to a RescueObject to the log. + **/ + // public final void logUpdate(rescuecore.debug.Update upd){ + // if (debug) logWriter.addUpdate(upd, id, timeStep); + // } + /** + * Writes a transient Object to the log. + **/ + public final void logObject(Object obj){ + // if (debug) logWriter.addObject(obj,id,timeStep); + if (debug) DebugWriter.logUserObject(this,obj,timeStep); + } + + /** + Initialise this agent. Subclasses that override this method should invoke super.initialise(knowledge,self) at some point. + @param knowledge This agent's knowledge of the world + @param self The RescueObject describing this agent + */ + protected void initialise(RescueObject[] knowledge) { + memory = generateMemory(); + for (int i=0;i<knowledge.length;++i) { + memory.add(knowledge[i],0,RescueConstants.SOURCE_INITIAL); + } + type = me().getType(); + if(debug){ + DebugWriter.logInitialObjects(this,memory.getAllObjects()); + memory.addMemoryListener(new DebugMemoryListener(this)); + } + } + + /** + Construct a new Memory object for use by this Agent. This method allows Agents to customise their choice of Memory object. The default implementation returns a {@link HashMemory}. + @return A new Memory object + */ + protected Memory generateMemory() { + return new HashMemory(); + } + + /** + Called after a KA_SENSE is received + */ + protected abstract void sense(); + + /** + Called after a KA_HEAR is received + @param from The agent that sent the message + @param msg The message body + @param channel The channel that this message was received on + */ + protected void hear(int from, byte[] msg, byte channel){} + // protected boolean hearTell(int from, byte[] msg){return false;} + // protected boolean hearSay(int from, byte[] msg){return false;} + // protected boolean willListenHear(int from) {return false;} + // protected boolean willListenHearSay(int from) {return false;} + // protected boolean willListenHearTell(int from) {return false;} + + /** + How many messages can this agent send per timestep? + @return The maximum number of messages this agent can send per timestep + */ + // protected final int getMaxSend() { + // return sendMax; + // } + + /** + How many messages can this agent receive per timestep? + @return The maximum number of messages this agent can receive per timestep + */ + // protected final int getMaxReceive() { + // return receiveMax; + // } + + /** + Send an AK_SAY message to the kernel. If this agent has already send too many messages this timestep then this will be silently ignored + @param message The message + */ + protected final void say(byte[] message) { + // if (numSent < sendMax) { + appendCommand(Command.SAY(id,timeStep,message,message.length)); + // ++numSent; + // } + } + + /** + Send an AK_TELL message to the kernel. If this agent has already send too many messages this timestep then this will be silently ignored + @param message The message + */ + protected final void tell(byte[] message, byte channel) { + // if (numSent < sendMax) { + appendCommand(Command.TELL(id,timeStep,message,message.length,channel)); + // ++numSent; + // } + } +} diff --git a/modules/oldsims/rescuecore/ArrayMemory.java b/modules/oldsims/rescuecore/ArrayMemory.java new file mode 100644 index 0000000000000000000000000000000000000000..19cc975114e3427abba46bc1b07b2603e9f33387 --- /dev/null +++ b/modules/oldsims/rescuecore/ArrayMemory.java @@ -0,0 +1,117 @@ +/* + * Last change: $Date: 2004/05/04 03:09:37 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.*; + +/** + This is an implementation of Memory that stores the data in an array + */ +public class ArrayMemory extends Memory { + private final static int DEFAULT_MEMORY_SIZE = 3000; + private final static int DEFAULT_RESIZE_FACTOR = 1000; + + protected RescueObject[] data; + private int dataSize; + private int resizeFactor; + + /** + Construct a new empty memory + */ + public ArrayMemory() { + this(DEFAULT_MEMORY_SIZE,DEFAULT_RESIZE_FACTOR); + } + + /** + Construct a new empty memory + @param size The initial size of the array + @param factor The amount to increase the array by if we run out of room + */ + public ArrayMemory(int size, int factor) { + data = new RescueObject[size]; + resizeFactor = factor; + dataSize = 0; + } + + public RescueObject lookup(int id) { + return id>=data.length?null:data[id]; + } + + public Collection<RescueObject> getAllObjects() { + Collection<RescueObject> result = new HashSet<RescueObject>(dataSize); + for (int i=0;i<data.length;++i) { + if (data[i]!=null) result.add(data[i]); + } + return result; + } + + public void getObjectsOfType(Collection<RescueObject> result, int... types) { + for (int i=0;i<data.length;++i) { + RescueObject next = data[i]; + int type = next.getType(); + for (int nextType : types) { + if (type==nextType) { + result.add(next); + break; + } + } + } + } + + /* + public RescueObject[] getObjectsOfInternalType(int type) { + List result = new ArrayList(); + for (int i=0;i<data.length;++i) { + RescueObject next = data[i]; + if (next!=null && (next.getInternalType() & type)!=0) result.add(next); + } + return (RescueObject[])result.toArray(new RescueObject[0]); + } + */ + + public void add(RescueObject o, int timestamp, Object source) { + int id = o.getID(); + if (id >= data.length) resizeMemory(id+1); + if (data[id]==null) ++dataSize; + data[id] = o; + super.add(o,timestamp,source); + } + + public void remove(RescueObject o) { + int id = o.getID(); + if (id >= data.length) resizeMemory(id+1); + data[id] = null; + super.remove(o); + } + + private void resizeMemory(int sizeNeeded) { + RescueObject[] newData = new RescueObject[Math.max(sizeNeeded,data.length+resizeFactor)]; + System.arraycopy(data,0,newData,0,data.length); + data = newData; + } + + /** + * A deep clone of this memory. + * Any listeners on the original memory will not be registered on the new memory. + **/ + public Memory copy() { + ArrayMemory m = new ArrayMemory(data.length,resizeFactor); + for(int i = 0; i< data.length; i++) + if(data[i] != null) + m.add(data[i].copy(),0,RescueConstants.SOURCE_UNKNOWN); + return m; + } +} diff --git a/modules/oldsims/rescuecore/ArrayProperty.java b/modules/oldsims/rescuecore/ArrayProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..2b523efb7ec8d528cdf7839e5c3b72252f641431 --- /dev/null +++ b/modules/oldsims/rescuecore/ArrayProperty.java @@ -0,0 +1,198 @@ +/* + * Last change: $Date: 2005/02/20 01:29:55 $ + * $Revision: 1.15 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + This class encapsulates information about an array property + */ +public class ArrayProperty extends Property { + private int[] values; + private int numValues; + + public ArrayProperty(int type) { + super(type); + values = new int[10]; + numValues = 0; + } + + public ArrayProperty(int type, int[] values) { + super(type); + this.values = new int[values.length]; + numValues = values.length; + System.arraycopy(values,0,this.values,0,values.length); + lastUpdate = 0; + } + + /* + public Property copy(){ + ArrayProperty result = ArrayProperty(type,values,null); + result.numValues = numValues; + return result; + } + */ + + public int[] getValues() { + int[] result = new int[numValues]; + System.arraycopy(values,0,result,0,numValues); + return result; + } + + public String getStringValue() { + StringBuffer result = new StringBuffer(); + result.append("["); + for (int i=0;i<numValues;++i) { + result.append(values[i]); + if (i<numValues-1) result.append(","); + } + result.append("]"); + return result.toString(); + } + + /** + Set the values of this property. The timestamp will also be updated. Note that this method does not check that the update is newer than the current value - it is up to the application to test for this. + @param newValues The new values + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if the values were actually changed, i.e the new values are different from the old ones + @see #isOlderThan(int) + */ + public boolean setValues(int[] newValues, int timestamp, Object source) { + lastUpdate = timestamp; + lastUpdateSource = source; + if (!different(newValues)) return false; + values = new int[newValues.length]; + System.arraycopy(newValues,0,values,0,values.length); + numValues = newValues.length; + return true; + } + + /** + Update the values of this property. The timestamp will also be updated. + @param newValues The new values + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if the values were actually changed, i.e the new values are different from the old ones and the new timestamp is greater than the old timestamp + @see #isOlderThan(int) + */ + public boolean updateValues(int[] newValues, int timestamp, Object source) { + if (timestamp <= lastUpdate) return false; + if (lastUpdate>=0 && !different(newValues)) return false; + lastUpdate = timestamp; + lastUpdateSource = source; + values = new int[newValues.length]; + System.arraycopy(newValues,0,values,0,values.length); + numValues = newValues.length; + return true; + } + + /** + Append a value to the list + @param value The new value + */ + public void append(int value) { + if (numValues == values.length) { + int[] newValues = new int[values.length+10]; + System.arraycopy(values,0,newValues,0,values.length); + values = newValues; + } + values[numValues++] = value; + } + + public void clear() { + numValues = 0; + } + + /** + Merge another property into this one + @param p The Property to merge + @return true if and only if the value of this property was actually changed + */ + public boolean merge(Property p) { + if (p instanceof ArrayProperty) { + return updateValues(((ArrayProperty)p).values,p.lastUpdate,p.lastUpdateSource); + } + return false; + + /* + if (p.lastUpdate <= this.lastUpdate) { + return false; + } + if (p instanceof ArrayProperty) { + lastUpdate = p.lastUpdate; + lastUpdateSource = p.lastUpdateSource; + ArrayProperty a = (ArrayProperty)p; + if (!different(a.values,a.numValues)) { + return false; + } + values = new int[a.values.length]; + numValues = a.numValues; + System.arraycopy(a.values,0,values,0,values.length); + return true; + } + return false; + */ + } + + /** + Write this property to an OutputBuffer + @param out The OutputBuffer to write this proprty to + */ + public void write(OutputBuffer out) { + out.writeInt(numValues); + for (int i=0;i<numValues;++i) { + out.writeInt(values[i]); + } + } + + /** + Decode property data from an InputBuffer + @param in An InputBuffer to read data from + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if a change was made + */ + public boolean read(InputBuffer in, int timestamp, Object source) { + // System.out.println("Timestep "+timestamp+": updating "+this+" from buffer"); + int number = in.readInt(); + int[] newValues = new int[number]; + for (int i=0;i<number;++i) newValues[i] = in.readInt(); + return updateValues(newValues,timestamp,source); + /* + if (lastUpdate < timestamp) { + changed = different(newValues); + if (changed) { + values = newValues; + numValues = number; + lastUpdate = timestamp; + lastUpdateSource = source; + } + } + return changed; + */ + } + + private boolean different(int[] newValues) { + return different(newValues,newValues.length); + } + + private boolean different(int[] newValues, int count) { + if (count != numValues) return true; + for (int i=0;i<numValues && i<count;++i) { + if (newValues[i]!=values[i]) return true; + } + return false; + } +} diff --git a/modules/oldsims/rescuecore/CannotFindLocationException.java b/modules/oldsims/rescuecore/CannotFindLocationException.java new file mode 100644 index 0000000000000000000000000000000000000000..f5b2655f27a66c910e95a7d93d96f86b3cfe6b1a --- /dev/null +++ b/modules/oldsims/rescuecore/CannotFindLocationException.java @@ -0,0 +1,29 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + An exception to be thrown whenever a mobile RescueObject cannot be located + */ +public class CannotFindLocationException extends Exception { + public CannotFindLocationException() {super();} + + /** + Construct a new exception with a given error message + @param s The error message + */ + public CannotFindLocationException(String s) {super(s);} +} diff --git a/modules/oldsims/rescuecore/CenterAgent.java b/modules/oldsims/rescuecore/CenterAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..e72c2cd36c541ce329ce6f28fbe6eaf83f96140a --- /dev/null +++ b/modules/oldsims/rescuecore/CenterAgent.java @@ -0,0 +1,31 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import rescuecore.objects.*; + +/** + An abstract superclass for center agents (i.e fire station, police office, ambulance center). + */ +public abstract class CenterAgent extends Agent { + protected CenterAgent(int... types) { + super(types); + } + + private Building me() { + return (Building)memory.lookup(id); + } +} diff --git a/modules/oldsims/rescuecore/CommunicationException.java b/modules/oldsims/rescuecore/CommunicationException.java new file mode 100644 index 0000000000000000000000000000000000000000..dd51f036c75491f63b1000f8034539917a3373ff --- /dev/null +++ b/modules/oldsims/rescuecore/CommunicationException.java @@ -0,0 +1,23 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +public class CommunicationException extends Exception { + public CommunicationException() {super();} + public CommunicationException(String s) {super(s);} + public CommunicationException(java.net.InetAddress host, int port, String s) {super("Error talking to "+host+":"+port+": "+s);} + public CommunicationException(java.net.InetAddress host, int port, Throwable t) {super("Error talking to "+host+":"+port,t);} +} diff --git a/modules/oldsims/rescuecore/Connection.java b/modules/oldsims/rescuecore/Connection.java new file mode 100644 index 0000000000000000000000000000000000000000..13cfcf6bd6c28ef852c6bd254ffc787e19b10f64 --- /dev/null +++ b/modules/oldsims/rescuecore/Connection.java @@ -0,0 +1,43 @@ +/* + * Last change: $Date: 2005/06/14 21:55:50 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.io.IOException; + +/** + The Connection interface encapsulates the sending and receiving of messages to and from the kernel. + */ +public interface Connection { + /** + Close the connection + */ + public abstract void close(); + + /** + Send a message + @param msg The message to send + @throws IOException if something goes wrong during sending + */ + public abstract void send(byte[] data) throws IOException; + + /** + Receive a message. If there is nothing to receive then this method will wait for the specified timeout (in ms, -1 to wait forever, 0 to not wait). + @param timeout The maximum time to wait, in ms. If this is negative then this method will wait forever (or until interrupted), if it is zero then this method will not block. + @return The next message to be received, or null if nothing is available + @throws IOException if there is an error during receiving + */ + public abstract byte[] receive(int timeout) throws IOException, InterruptedException; +} diff --git a/modules/oldsims/rescuecore/DummyAgent.java b/modules/oldsims/rescuecore/DummyAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..5e6331526c9e6e433d8eab985a5688e675bf1ba4 --- /dev/null +++ b/modules/oldsims/rescuecore/DummyAgent.java @@ -0,0 +1,24 @@ +/* + * Last change: $Date: 2004/07/11 22:26:27 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +public class DummyAgent extends Agent { + public DummyAgent() { + super(RescueConstants.TYPE_FIRE_BRIGADE, RescueConstants.TYPE_AMBULANCE_TEAM, RescueConstants.TYPE_POLICE_FORCE, RescueConstants.TYPE_FIRE_STATION, RescueConstants.TYPE_AMBULANCE_CENTER, RescueConstants.TYPE_POLICE_OFFICE); + } + + protected void sense() {} +} diff --git a/modules/oldsims/rescuecore/Handy.java b/modules/oldsims/rescuecore/Handy.java new file mode 100644 index 0000000000000000000000000000000000000000..9b030ce0c4975e293887a69f8cd5fbc7c4a0de57 --- /dev/null +++ b/modules/oldsims/rescuecore/Handy.java @@ -0,0 +1,741 @@ +/* + * Last change: $Date: 2005/03/15 00:46:38 $ + * $Revision: 1.10 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.*; +import java.io.UnsupportedEncodingException; +import rescuecore.commands.*; + +/** + A collection of useful methods + */ +public class Handy { + private final static String[] HEX_DIGITS = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"}; + private final static String CHARACTER_ENCODING = "US-ASCII"; + + private final static Object PRINT_LOCK = new Object(); + + private Handy() {} + + /** + Print an array of bytes to System.out in a nice way + @param data The bytes to print out + */ + public static void printBytes(byte[] data) { + printBytes(null,data); + } + + /** + Print an array of bytes to System.out in a nice way + @param header A string to print out as a title + @param data The bytes to print out + */ + public static void printBytes(String header, byte[] data) { + synchronized(PRINT_LOCK) { + if (header!=null) System.out.println(header); + System.out.println("OFFSET\tBYTES"); + for (int i=0;i<data.length;i+=4) { + printBytes(i,data,""); + } + } + } + + public static void printBytes(InputBuffer in) { + printBytes(null,in); + } + + public static void printBytes(String header, InputBuffer in) { + int position = in.getPosition(); + byte[] temp = new byte[in.available()]; + in.readBytes(temp); + printBytes(header,temp); + in.setPosition(position); + } + + /** + Print four bytes to System.out in a nice way + @param startIndex The index of the first byte + @param data The buffer containing the bytes to print out + @param description A description of what these four bytes actually mean + */ + public static void printBytes(int startIndex, byte[] data, String description) { + StringBuffer buffer = new StringBuffer(); + synchronized(PRINT_LOCK) { + buffer.append(startIndex); + buffer.append("\t"); + for (int j=0;j<4;++j) { + if (data.length>startIndex+j) { + buffer.append(hex(data[startIndex+j])); + if (j!=3) buffer.append(" "); + } + } + buffer.append("\t"); + char charValue = (char)decodeInt(data,startIndex); + if (Character.isLetterOrDigit(charValue)) buffer.append(charValue); + buffer.append("\t"); + buffer.append(decodeInt(data,startIndex)); + buffer.append("\t"); + buffer.append(description); + System.out.println(buffer.toString()); + } + } + + /** + Turn a byte into a hexadecimal String + @param b The byte to convert + @return The byte in hexadecimal, as a String + */ + public static String hex(byte b) { + byte left = (byte)((b >> 4) & 0xF); + byte right = (byte)(b & 0xF); + return HEX_DIGITS[left]+HEX_DIGITS[right]; + } + + /** + Decode a byte from a buffer + @param buffer The buffer we are looking at + @param off The offset into the buffer to start decoding from + @return The next byte in the buffer + */ + /* + public static byte decodeByte(byte[] buffer, int off) { + return buffer[off]; + } + */ + + /** + Decode a byte array from a buffer + @param buffer The buffer we are looking at + @param off The offset into the buffer to start decoding from + @param length The number of bytes to read + @return The next byte array in the buffer + */ + /* + public static byte[] decodeBytes(byte[] buffer, int off, int length) { + byte[] result = new byte[length]; + System.arraycopy(buffer,off,result,0,length); + return result; + } + */ + + /** + Decode a short from a buffer + @param buffer The buffer we are looking at + @param off The offset into the buffer to start decoding from + @return The next short in the buffer + */ + /* + public static short decodeShort(byte[] buffer, int off) { + int result = ((buffer[off] << 8) & 0x0000FF00) | (buffer[off+1] & 0x000000FF); + return (short)result; + } + */ + + /** + Decode an int from a buffer + @param buffer The buffer we are looking at + @param off The offset into the buffer to start decoding from + @return The next int in the buffer + */ + private static int decodeInt(byte[] buffer, int off) { + int result = ((buffer[off] << 24) & 0xFF000000) | ((buffer[off+1] << 16) & 0x00FF0000) | ((buffer[off+2] << 8) & 0x0000FF00) | (buffer[off+3] & 0x000000FF); + return result; + } + + /** + Decode a String from a buffer + @param buffer The buffer we are looking at + @param off The offset into the buffer to start decoding from + @param length The number of characters in the String + @return The next String in the buffer + */ + /* + public static String decodeString(byte[] buffer, int off, int length) { + int realLength = Math.min(length,buffer.length-off); + byte[] data = new byte[realLength]; + System.arraycopy(buffer,off,data,0,realLength); + try { + return new String(data,CHARACTER_ENCODING); + } + catch (UnsupportedEncodingException e) { + return new String(data); + } + } + */ + + /** + Encode a byte into a byte array + @param value The byte to encode + @return A byte array representation of the input value + */ + /* + public static byte[] encodeByte(int value) { + return new byte[] {(byte)(value & 0xFF)}; + } + */ + + /** + Encode a byte into a buffer + @param value The byte to encode + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeByte(int value, byte[] buf, int off) { + buf[off] = (byte)(value&0xFF); + } + */ + + /** + Encode a byte arrray into a buffer + @param bytes The byte array to encode + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeBytes(byte[] bytes, byte[] buf, int off) { + System.arraycopy(bytes,0,buf,off,bytes.length); + } + */ + + /** + Encode part of a byte array into a buffer + @param bytes The byte arrray to encode + @param bytesOffset The offset into bytes to start writing from + @param bytesLength The number of bytes to write + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeBytes(byte[] bytes, int bytesOffset, int bytesLength, byte[] buf, int off) { + System.arraycopy(bytes,bytesOffset,buf,off,bytesLength); + } + */ + + /** + Encode a short into a byte array + @param value The short to encode + @return A byte array representation of the input value + */ + /* + public static byte[] encodeShort(int value) { + byte[] result = new byte[2]; + result[0] = (byte)(value >> 8 & 0xFF); + result[1] = (byte)(value & 0xFF); + return result; + } + */ + + /** + Encode a short into a buffer + @param value The short to encode + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeShort(int value, byte[] buf, int off) { + buf[off] = (byte)(value >> 8 & 0xFF); + buf[off+1] = (byte)(value & 0xFF); + } + */ + + /** + Encode an int into a byte array + @param value The int to encode + @return A byte array representation of the input value + */ + /* + public static byte[] encodeInt(int value) { + byte[] result = new byte[4]; + result[0] = (byte)(value >> 24 & 0xFF); + result[1] = (byte)(value >> 16 & 0xFF); + result[2] = (byte)(value >> 8 & 0xFF); + result[3] = (byte)(value & 0xFF); + return result; + } + */ + + /** + Encode an int into a buffer + @param value The int to encode + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeInt(int value, byte[] buf, int off) { + buf[off] = (byte)(value >> 24 & 0xFF); + buf[off+1] = (byte)(value >> 16 & 0xFF); + buf[off+2] = (byte)(value >> 8 & 0xFF); + buf[off+3] = (byte)(value & 0xFF); + } + */ + + /** + Encode a String into a byte array + @param value The String to encode + @param length The maximum number of bytes to use + @return A byte array representation of the input byte + */ + /* + public static byte[] encodeString(String value, int length) { + byte[] result = new byte[length]; + byte[] data; + try { + data = value.getBytes(CHARACTER_ENCODING); + } + catch (UnsupportedEncodingException e) { + data = value.getBytes(); + } + System.arraycopy(data,0,result,0,Math.min(data.length,length)); + return result; + } + */ + + /** + Encode a String into a buffer + @param value The String to encode + @param length The maximum number of bytes to write + @param buf The buffer to write the result into + @param off The offset to start writing at + */ + /* + public static void encodeString(String value, int length, byte[] buf, int off) { + byte[] data; + try { + data = value.getBytes(CHARACTER_ENCODING); + } + catch (UnsupportedEncodingException e) { + data = value.getBytes(); + } + System.arraycopy(data,0,buf,off,Math.min(data.length,length)); + } + */ + + /** + Find out if two int arrays are different. The two arrays are not different if and only if they are the same size and contain the same elements (possibly out of order). + @param a The first array + @param b The second array + @return true if and only if the two arrays are different + */ + /* + public static boolean isDifferent(int[] a, int[] b) { + if (a==null && b==null) return false; + if (a==null || b==null) return true; + if (a.length!=b.length) return true; + int[] aSorted = new int[a.length]; + int[] bSorted = new int[b.length]; + System.arraycopy(a,0,aSorted,0,a.length); + System.arraycopy(b,0,bSorted,0,b.length); + Arrays.sort(aSorted); + Arrays.sort(bSorted); + for (int i=0;i<a.length;++i) if (aSorted[i]!=bSorted[i]) return true; + return false; + } + */ + + /** + Translate a type name into a human-readable string + @param type The type we want to convert + @return A human-readable String showing the type given + @see RescueConstants#TYPE_CIVILIAN + @see RescueConstants#TYPE_FIRE_BRIGADE + @see RescueConstants#TYPE_AMBULANCE_TEAM + @see RescueConstants#TYPE_POLICE_FORCE + @see RescueConstants#TYPE_ROAD + @see RescueConstants#TYPE_NODE + @see RescueConstants#TYPE_RIVER + @see RescueConstants#TYPE_RIVER_NODE + @see RescueConstants#TYPE_BUILDING + @see RescueConstants#TYPE_REFUGE + @see RescueConstants#TYPE_FIRE_STATION + @see RescueConstants#TYPE_AMBULANCE_CENTER + @see RescueConstants#TYPE_POLICE_OFFICE + @see RescueConstants#TYPE_WORLD + @see RescueConstants#TYPE_CAR + */ + public static String getTypeName(int type) { + switch(type) { + case RescueConstants.TYPE_NULL: + return "TYPE_NULL"; + case RescueConstants.TYPE_CIVILIAN: + return "TYPE_CIVILIAN"; + case RescueConstants.TYPE_FIRE_BRIGADE: + return "TYPE_FIRE_BRIGADE"; + case RescueConstants.TYPE_AMBULANCE_TEAM: + return "TYPE_AMBULANCE_TEAM"; + case RescueConstants.TYPE_POLICE_FORCE: + return "TYPE_POLICE_FORCE"; + case RescueConstants.TYPE_ROAD: + return "TYPE_ROAD"; + case RescueConstants.TYPE_NODE: + return "TYPE_NODE"; + case RescueConstants.TYPE_RIVER: + return "TYPE_RIVER"; + case RescueConstants.TYPE_RIVER_NODE: + return "TYPE_RIVER_NODE"; + case RescueConstants.TYPE_BUILDING: + return "TYPE_BUILDING"; + case RescueConstants.TYPE_REFUGE: + return "TYPE_REFUGE"; + case RescueConstants.TYPE_FIRE_STATION: + return "TYPE_FIRE_STATION"; + case RescueConstants.TYPE_AMBULANCE_CENTER: + return "TYPE_AMBULANCE_CENTER"; + case RescueConstants.TYPE_POLICE_OFFICE: + return "TYPE_POLICE_OFFICE"; + case RescueConstants.TYPE_WORLD: + return "TYPE_WORLD"; + case RescueConstants.TYPE_CAR: + return "TYPE_CAR"; + default: + return "Unknown type: "+type; + } + } + + /** + Translate a property name into a human-readable string + @param type The property we want to convert + @return A human-readable String showing the property given + @see RescueConstants#PROPERTY_NULL + @see RescueConstants#PROPERTY_START_TIME + @see RescueConstants#PROPERTY_LONGITUDE + @see RescueConstants#PROPERTY_LATITUDE + @see RescueConstants#PROPERTY_WIND_FORCE + @see RescueConstants#PROPERTY_WIND_DIRECTION + @see RescueConstants#PROPERTY_X + @see RescueConstants#PROPERTY_Y + @see RescueConstants#PROPERTY_DIRECTION + @see RescueConstants#PROPERTY_POSITION + @see RescueConstants#PROPERTY_POSITION_HISTORY + @see RescueConstants#PROPERTY_POSITION_EXTRA + @see RescueConstants#PROPERTY_STAMINA + @see RescueConstants#PROPERTY_HP + @see RescueConstants#PROPERTY_DAMAGE + @see RescueConstants#PROPERTY_BURIEDNESS + @see RescueConstants#PROPERTY_FLOORS + @see RescueConstants#PROPERTY_BUILDING_ATTRIBUTES + @see RescueConstants#PROPERTY_IGNITION + @see RescueConstants#PROPERTY_BROKENNESS + @see RescueConstants#PROPERTY_FIERYNESS + @see RescueConstants#PROPERTY_ENTRANCES + @see RescueConstants#PROPERTY_BUILDING_SHAPE_ID + @see RescueConstants#PROPERTY_BUILDING_CODE + @see RescueConstants#PROPERTY_BUILDING_AREA_GROUND + @see RescueConstants#PROPERTY_BUILDING_AREA_TOTAL + @see RescueConstants#PROPERTY_BUILDING_APEXES + @see RescueConstants#PROPERTY_WATER_QUANTITY + @see RescueConstants#PROPERTY_STRETCHED_LENGTH + @see RescueConstants#PROPERTY_HEAD + @see RescueConstants#PROPERTY_TAIL + @see RescueConstants#PROPERTY_LENGTH + @see RescueConstants#PROPERTY_ROAD_KIND + @see RescueConstants#PROPERTY_CARS_PASS_TO_HEAD + @see RescueConstants#PROPERTY_CARS_PASS_TO_TAIL + @see RescueConstants#PROPERTY_HUMANS_PASS_TO_HEAD + @see RescueConstants#PROPERTY_HUMANS_PASS_TO_TAIL + @see RescueConstants#PROPERTY_WIDTH + @see RescueConstants#PROPERTY_BLOCK + @see RescueConstants#PROPERTY_REPAIR_COST + @see RescueConstants#PROPERTY_MEDIAN_STRIP + @see RescueConstants#PROPERTY_LINES_TO_HEAD + @see RescueConstants#PROPERTY_LINES_TO_TAIL + @see RescueConstants#PROPERTY_WIDTH_FOR_WALKERS + @see RescueConstants#PROPERTY_EDGES + @see RescueConstants#PROPERTY_SIGNAL + @see RescueConstants#PROPERTY_SIGNAL_TIMING + @see RescueConstants#PROPERTY_SHORTCUT_TO_TURN + @see RescueConstants#PROPERTY_POCKET_TO_TURN_ACROSS + */ + public static String getPropertyName(int type) { + switch (type) { + case RescueConstants.PROPERTY_NULL: + return "PROPERTY_NULL"; + case RescueConstants.PROPERTY_START_TIME: + return "PROPERTY_START_TIME"; + case RescueConstants.PROPERTY_LONGITUDE: + return "PROPERTY_LONGITUDE"; + case RescueConstants.PROPERTY_LATITUDE: + return "PROPERTY_LATITUDE"; + case RescueConstants.PROPERTY_WIND_FORCE: + return "PROPERTY_WIND_FORCE"; + case RescueConstants.PROPERTY_WIND_DIRECTION: + return "PROPERTY_WIND_DIRECTION"; + case RescueConstants.PROPERTY_X: + return "PROPERTY_X"; + case RescueConstants.PROPERTY_Y: + return "PROPERTY_Y"; + case RescueConstants.PROPERTY_DIRECTION: + return "PROPERTY_DIRECTION"; + case RescueConstants.PROPERTY_POSITION: + return "PROPERTY_POSITION"; + case RescueConstants.PROPERTY_POSITION_HISTORY: + return "PROPERTY_POSITION_HISTORY"; + case RescueConstants.PROPERTY_POSITION_EXTRA: + return "PROPERTY_POSITION_EXTRA"; + case RescueConstants.PROPERTY_STAMINA: + return "PROPERTY_STAMINA"; + case RescueConstants.PROPERTY_HP: + return "PROPERTY_HP"; + case RescueConstants.PROPERTY_DAMAGE: + return "PROPERTY_DAMAGE"; + case RescueConstants.PROPERTY_BURIEDNESS: + return "PROPERTY_BURIEDNESS"; + case RescueConstants.PROPERTY_FLOORS: + return "PROPERTY_FLOORS"; + case RescueConstants.PROPERTY_BUILDING_ATTRIBUTES: + return "PROPERTY_BUILDING_ATTRIBUTES"; + case RescueConstants.PROPERTY_IGNITION: + return "PROPERTY_IGNITION"; + case RescueConstants.PROPERTY_BROKENNESS: + return "PROPERTY_BROKENNESS"; + case RescueConstants.PROPERTY_FIERYNESS: + return "PROPERTY_FIERYNESS"; + case RescueConstants.PROPERTY_ENTRANCES: + return "PROPERTY_ENTRANCES"; + // case RescueConstants.PROPERTY_BUILDING_SHAPE_ID: + // return "PROPERTY_BUILDING_SHAPE_ID"; + case RescueConstants.PROPERTY_BUILDING_CODE: + return "PROPERTY_BUILDING_CODE"; + case RescueConstants.PROPERTY_BUILDING_AREA_GROUND: + return "PROPERTY_BUILDING_AREA_GROUND"; + case RescueConstants.PROPERTY_BUILDING_AREA_TOTAL: + return "PROPERTY_BUILDING_AREA_TOTAL"; + case RescueConstants.PROPERTY_BUILDING_APEXES: + return "PROPERTY_BUILDING_APEXES"; + case RescueConstants.PROPERTY_WATER_QUANTITY: + return "PROPERTY_WATER_QUANTITY"; + // case RescueConstants.PROPERTY_STRETCHED_LENGTH: + // return "PROPERTY_STRETCHED_LENGTH"; + case RescueConstants.PROPERTY_HEAD: + return "PROPERTY_HEAD"; + case RescueConstants.PROPERTY_TAIL: + return "PROPERTY_TAIL"; + case RescueConstants.PROPERTY_LENGTH: + return "PROPERTY_LENGTH"; + case RescueConstants.PROPERTY_ROAD_KIND: + return "PROPERTY_ROAD_KIND"; + case RescueConstants.PROPERTY_CARS_PASS_TO_HEAD: + return "PROPERTY_CARS_PASS_TO_HEAD"; + case RescueConstants.PROPERTY_CARS_PASS_TO_TAIL: + return "PROPERTY_CARS_PASS_TO_TAIL"; + case RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD: + return "PROPERTY_HUMANS_PASS_TO_HEAD"; + case RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL: + return "PROPERTY_HUMANS_PASS_TO_TAIL"; + case RescueConstants.PROPERTY_WIDTH: + return "PROPERTY_WIDTH"; + case RescueConstants.PROPERTY_BLOCK: + return "PROPERTY_BLOCK"; + case RescueConstants.PROPERTY_REPAIR_COST: + return "PROPERTY_REPAIR_COST"; + case RescueConstants.PROPERTY_MEDIAN_STRIP: + return "PROPERTY_MEDIAN_STRIP"; + case RescueConstants.PROPERTY_LINES_TO_HEAD: + return "PROPERTY_LINES_TO_HEAD"; + case RescueConstants.PROPERTY_LINES_TO_TAIL: + return "PROPERTY_LINES_TO_TAIL"; + case RescueConstants.PROPERTY_WIDTH_FOR_WALKERS: + return "PROPERTY_WIDTH_FOR_WALKERS"; + case RescueConstants.PROPERTY_EDGES: + return "PROPERTY_EDGES"; + case RescueConstants.PROPERTY_SIGNAL: + return "PROPERTY_SIGNAL"; + case RescueConstants.PROPERTY_SIGNAL_TIMING: + return "PROPERTY_SIGNAL_TIMING"; + case RescueConstants.PROPERTY_SHORTCUT_TO_TURN: + return "PROPERTY_SHORTCUT_TO_TURN"; + case RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS: + return "PROPERTY_POCKET_TO_TURN_ACROSS"; + case RescueConstants.PROPERTY_BUILDING_IMPORTANCE: + return "PROPERTY_BUILDING_IMPORTANCE"; + case RescueConstants.PROPERTY_BUILDING_TEMPERATURE: + return "PROPERTY_BUILDING_TEMPERATURE"; + default: + return "Unknown property: "+type; + } + } + + /** + Translate a command type into a human-readable string + @param header The type we want to convert + @return A human-readable String showing the type given + @see RescueConstants#HEADER_NULL + @see RescueConstants#AK_CONNECT + @see RescueConstants#AK_ACKNOWLEDGE + @see RescueConstants#AK_REST + @see RescueConstants#AK_MOVE + @see RescueConstants#AK_EXTINGUISH + @see RescueConstants#AK_RESCUE + @see RescueConstants#AK_CLEAR + @see RescueConstants#AK_LOAD + @see RescueConstants#AK_UNLOAD + @see RescueConstants#KA_CONNECT_OK + @see RescueConstants#KA_CONNECT_ERROR + @see RescueConstants#KA_SENSE + @see RescueConstants#KA_HEAR + @see RescueConstants#SK_CONNECT + @see RescueConstants#SK_ACKNOWLEDGE + @see RescueConstants#SK_UPDATE + @see RescueConstants#KS_CONNECT_OK + @see RescueConstants#KS_CONNECT_ERROR + @see RescueConstants#KS_COMMANDS + @see RescueConstants#KG_CONNECT + @see RescueConstants#KG_ACKNOWLEDGE + @see RescueConstants#KG_UPDATE + @see RescueConstants#GK_CONNECT_OK + @see RescueConstants#GK_CONNECT_ERROR + */ + public static String getCommandTypeName(int header) { + switch (header) { + case RescueConstants.HEADER_NULL: + return "HEADER_NULL"; + + case RescueConstants.GK_CONNECT_OK: + return "GK_CONNECT_OK"; + case RescueConstants.GK_CONNECT_ERROR: + return "GK_CONNECT_ERROR"; + case RescueConstants.KG_CONNECT: + return "KG_CONNECT"; + case RescueConstants.KG_ACKNOWLEDGE: + return "KG_ACKNOWLEDGE"; + + case RescueConstants.SK_CONNECT: + return "SK_CONNECT"; + case RescueConstants.SK_ACKNOWLEDGE: + return "SK_ACKNOWLEDGE"; + case RescueConstants.SK_UPDATE: + return "SK_UPDATE"; + case RescueConstants.KS_CONNECT_OK: + return "KS_CONNECT_OK"; + case RescueConstants.KS_CONNECT_ERROR: + return "KS_CONNECT_ERROR"; + case RescueConstants.COMMANDS: + return "COMMANDS"; + case RescueConstants.UPDATE: + return "UPDATE"; + + case RescueConstants.VK_CONNECT: + return "VK_CONNECT"; + case RescueConstants.VK_ACKNOWLEDGE: + return "VK_ACKNOWLEDGE"; + case RescueConstants.KV_CONNECT_OK: + return "KV_CONNECT_OK"; + case RescueConstants.KV_CONNECT_ERROR: + return "KV_CONNECT_ERROR"; + // case RescueConstants.KV_UPDATE: + // return "KV_UPDATE"; + + case RescueConstants.AK_CONNECT: + return "AK_CONNECT"; + case RescueConstants.AK_ACKNOWLEDGE: + return "AK_ACKNOWLEDGE"; + case RescueConstants.AK_REST: + return "AK_REST"; + case RescueConstants.AK_MOVE: + return "AK_MOVE"; + case RescueConstants.AK_EXTINGUISH: + return "AK_EXTINGUISH"; + case RescueConstants.AK_CLEAR: + return "AK_CLEAR"; + case RescueConstants.AK_RESCUE: + return "AK_RESCUE"; + case RescueConstants.AK_LOAD: + return "AK_LOAD"; + case RescueConstants.AK_UNLOAD: + return "AK_UNLOAD"; + case RescueConstants.AK_CHANNEL: + return "AK_CHANNEL"; + case RescueConstants.AK_REPAIR: + return "AK_REPAIR"; + case RescueConstants.KA_CONNECT_OK: + return "KA_CONNECT_OK"; + case RescueConstants.KA_CONNECT_ERROR: + return "KA_CONNECT_ERROR"; + case RescueConstants.KA_SENSE: + return "KA_SENSE"; + case RescueConstants.KA_HEAR: + return "KA_HEAR"; + // case RescueConstants.KA_HEAR_SAY: + // return "KA_HEAR_SAY"; + // case RescueConstants.KA_HEAR_TELL: + // return "KA_HEAR_TELL"; + default: + return "Unknown header: "+header; + } + } + + /** + Turn an agent type into a human-readable String + @param type The agent type to convert + @return A human-readable version of the type given + */ + public static String getAgentTypeName(int type) { + StringBuffer result = new StringBuffer(); + if ((type & RescueConstants.AGENT_TYPE_CIVILIAN)==RescueConstants.AGENT_TYPE_CIVILIAN) result.append("Civilian, "); + if ((type & RescueConstants.AGENT_TYPE_FIRE_BRIGADE)==RescueConstants.AGENT_TYPE_FIRE_BRIGADE) result.append("Fire Brigade, "); + if ((type & RescueConstants.AGENT_TYPE_FIRE_STATION)==RescueConstants.AGENT_TYPE_FIRE_STATION) result.append("Fire Station, "); + if ((type & RescueConstants.AGENT_TYPE_AMBULANCE_TEAM)==RescueConstants.AGENT_TYPE_AMBULANCE_TEAM) result.append("Ambulance Team, "); + if ((type & RescueConstants.AGENT_TYPE_AMBULANCE_CENTER)==RescueConstants.AGENT_TYPE_AMBULANCE_CENTER) result.append("Ambulance Center, "); + if ((type & RescueConstants.AGENT_TYPE_POLICE_FORCE)==RescueConstants.AGENT_TYPE_POLICE_FORCE) result.append("Police Force, "); + if ((type & RescueConstants.AGENT_TYPE_POLICE_OFFICE)==RescueConstants.AGENT_TYPE_POLICE_OFFICE) result.append("Police Office, "); + String s = result.toString(); + if (s.length()>0) return s.substring(0,s.length()-2); + return "Unknown"; + } + + /** + Turn an array of integers into a String containing a comma-seperated list of numbers + @param array The array to convert + @return A human-readable String containing a comma-seperated list of numbers + */ + public static String arrayAsString(int[] array) { + if (array==null) return "null"; + StringBuffer result = new StringBuffer(); + for (int i=0;i<array.length;++i) { + result.append(array[i]); + if (i<array.length-1) result.append(", "); + } + return result.toString(); + } + + public static String arrayAsString(Object[] array) { + if (array==null) return "null"; + StringBuffer result = new StringBuffer(); + for (int i=0;i<array.length;++i) { + result.append(array[i]==null?"null":array[i].toString()); + if (i<array.length-1) result.append(", "); + } + return result.toString(); + } + + public static String collectionAsString(Collection c) { + if (c==null) return "null"; + StringBuffer result = new StringBuffer(); + for (Iterator it = c.iterator();it.hasNext();) { + Object next = it.next(); + result.append(next==null?"null":next.toString()); + if (it.hasNext()) result.append(", "); + } + return result.toString(); + } + + public static RescueObject[] merge(RescueObject[] o1, RescueObject[] o2) { + RescueObject[] result = new RescueObject[o1.length+o2.length]; + System.arraycopy(o1,0,result,0,o1.length); + System.arraycopy(o2,0,result,o1.length,o2.length); + return result; + } +} diff --git a/modules/oldsims/rescuecore/HashMemory.java b/modules/oldsims/rescuecore/HashMemory.java new file mode 100644 index 0000000000000000000000000000000000000000..cbbf22bc7c33ca8dde2bd86cfb0ebae8e562f48e --- /dev/null +++ b/modules/oldsims/rescuecore/HashMemory.java @@ -0,0 +1,85 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +/** + * This is an implementation of Memory that stores the data in a hashtable + */ +public class HashMemory extends Memory { + protected Map data; + + /** + * Construct a new empty memory + */ + public HashMemory() { + data = new HashMap(); + } + + public Memory copy() { + HashMemory m = new HashMemory(); + Iterator it = data.values().iterator(); + while (it.hasNext()) { + RescueObject o = (RescueObject) it.next(); + m.add(o.copy(), 0, RescueConstants.SOURCE_UNKNOWN); + } + return m; + } + + public RescueObject lookup(int id) { + return (RescueObject) data.get(Integer.valueOf(id)); + } + + public Collection<RescueObject> getAllObjects() { + return new HashSet<RescueObject>(data.values()); + } + + public void getObjectsOfType(Collection<RescueObject> result, int... types) { + for (Iterator it = data.values().iterator(); it.hasNext();) { + RescueObject next = (RescueObject) it.next(); + int type = next.getType(); + for (int nextType : types) { + if (type == nextType) { + result.add(next); + break; + } + } + } + } + + /* + * public RescueObject[] getObjectsOfInternalType(int type) { List result = new + * ArrayList(); for (Iterator it = data.values().iterator();it.hasNext();) { + * RescueObject next = (RescueObject)it.next(); if (next!=null && + * (next.getInternalType() & type)!=0) result.add(next); } return + * (RescueObject[])result.toArray(new RescueObject[0]); } + */ + + public void add(RescueObject o, int timestamp, Object source) { + data.put(Integer.valueOf(o.getID()), o); + super.add(o, timestamp, source); + } + + public void remove(RescueObject o) { + data.remove(Integer.valueOf(o.getID())); + super.remove(o); + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/InputBuffer.java b/modules/oldsims/rescuecore/InputBuffer.java new file mode 100644 index 0000000000000000000000000000000000000000..af7ad5555cb4354e107770c94413d688cf332e5f --- /dev/null +++ b/modules/oldsims/rescuecore/InputBuffer.java @@ -0,0 +1,241 @@ +/* + * Last change: $Date: 2005/02/20 01:29:55 $ + * $Revision: 1.15 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import rescuecore.commands.*; +import rescuecore.objects.*; +import java.util.*; + +public class InputBuffer { + private byte[] data; + private int index; + + public InputBuffer(byte[] b) { + data = new byte[b.length]; + System.arraycopy(b,0,data,0,b.length); + } + + public int getSize() { + return data.length; + } + + public int available() { + return data.length-index; + } + + public byte readByte() { + return data[index++]; + } + + public void readBytes(byte[] result) { + System.arraycopy(data,index,result,0,result.length); + index += result.length; + } + + public short readShort() { + int result = ((data[index++] << 8) & 0xFF00) | (data[index++] & 0x00FF); + return (short)result; + } + + public int readInt() { + int result = ((data[index++] << 24) & 0xFF000000) | ((data[index++] << 16) & 0x00FF0000) | ((data[index++] << 8) & 0x0000FF00) | (data[index++] & 0x000000FF); + return result; + } + + public String readString() { + byte[] result = new byte[readInt()]; + readBytes(result); + return new String(result); + } + + public void reset() { + index = 0; + } + + public void skip(int size) { + index += size; + } + + public int getPosition() { + return index; + } + + public void setPosition(int i) { + index = i; + } + + /** + Decode a new RescueObject from the buffer + @param timestamp The current simulation timestamp + @param source The source of the new object + @return The next RescueObject in the buffer. Returns null if the next object is TYPE_NULL. + @see RescueConstants#TYPE_NULL + */ + public RescueObject readObject(int timestamp, Object source) { + int type = readInt(); + if (type==RescueConstants.TYPE_NULL) return null; + int id = readInt(); + int size = readInt(); + RescueObject result = RescueObject.newObject(type); + if (result==null) skip(size); + else if (size>0) { + result.read(this,timestamp,source); + } + result.setID(id); + return result; + } + + /** + Decode a set of objects from the buffer. + @param timestamp The current simulation timestamp + @param source The source of the new objects + @return The next RescueObject array in the buffer + */ + public RescueObject[] readObjects(int timestamp, Object source) { + List<RescueObject> result = new ArrayList<RescueObject>(); + int count = readInt(); + for (int i = 0; i < count; ++i) { + RescueObject next = readObject(timestamp, source); + result.add(next); + }; + return (RescueObject[])result.toArray(new RescueObject[0]); + } + + public Command readCommand() { + int type = readInt(); + if (type==RescueConstants.HEADER_NULL) return null; + int size = readInt(); + return readCommand(type,size); + } + + public Command readCommand(int type, int size) { + Command c = null; + switch (type) { + case RescueConstants.AK_EXTINGUISH: + c = new AKExtinguish(this); + break; + case RescueConstants.AK_MOVE: + c = new AKMove(this); + break; + case RescueConstants.AK_CLEAR: + c = new AKClear(this); + break; + case RescueConstants.AK_LOAD: + c = new AKLoad(this); + break; + case RescueConstants.AK_UNLOAD: + c = new AKUnload(this); + break; + case RescueConstants.AK_RESCUE: + c = new AKRescue(this); + break; + case RescueConstants.AK_SAY: + c = new AKSay(this); + break; + case RescueConstants.AK_TELL: + c = new AKTell(this); + break; + case RescueConstants.AK_REST: + c = new AKRest(this); + break; + case RescueConstants.AK_CONNECT: + c = new AKConnect(this); + break; + case RescueConstants.AK_ACKNOWLEDGE: + c = new AKAcknowledge(this); + break; + case RescueConstants.KA_CONNECT_OK: + c = new KAConnectOK(this); + break; + case RescueConstants.KA_CONNECT_ERROR: + c = new KAConnectError(this); + break; + case RescueConstants.KA_SENSE: + c = new KASense(this); + break; + case RescueConstants.KA_HEAR: + case RescueConstants.KA_HEAR_SAY: + case RescueConstants.KA_HEAR_TELL: + c = new KAHear(this); + break; + /* + case RescueConstants.KA_HEAR_SAY: + c = KAHear.KA_HEAR_SAY(this); + break; + case RescueConstants.KA_HEAR_TELL: + c = KAHear.KA_HEAR_TELL(this); + break; + */ + case RescueConstants.SK_CONNECT: + c = new SKConnect(this); + break; + case RescueConstants.SK_ACKNOWLEDGE: + c = new SKAcknowledge(this); + break; + case RescueConstants.SK_UPDATE: + c = new SKUpdate(this); + break; + case RescueConstants.KS_CONNECT_OK: + c = new KSConnectOK(this); + break; + case RescueConstants.KS_CONNECT_ERROR: + c = new KSConnectError(this); + break; + case RescueConstants.COMMANDS: + c = new Commands(this); + break; + case RescueConstants.UPDATE: + c = new Update(this); + break; + case RescueConstants.VK_CONNECT: + c = new VKConnect(this); + break; + case RescueConstants.VK_ACKNOWLEDGE: + c = new VKAcknowledge(this); + break; + case RescueConstants.KV_CONNECT_OK: + c = new KVConnectOK(this); + break; + case RescueConstants.KV_CONNECT_ERROR: + c = new KVConnectError(this); + break; + // case RescueConstants.KV_UPDATE: + // c = new KVUpdate(this); + // break; + default: + System.err.println("Don't know how to decode commands of type "+Handy.getCommandTypeName(type)); + skip(size); + break; + } + return c; + } + + /** + Decode a set of commands from the buffer. + @return The next Command array in the buffer + */ + public Command[] readCommands() { + List<Command> result = new ArrayList<Command>(); + Command next = null; + do { + next = readCommand(); + if (next!=null) { + result.add(next); + } + } while (next!=null); + return (Command[])result.toArray(new Command[0]); + } +} diff --git a/modules/oldsims/rescuecore/IntProperty.java b/modules/oldsims/rescuecore/IntProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..5be44116d1185955b3d4be40b794d1b89c155052 --- /dev/null +++ b/modules/oldsims/rescuecore/IntProperty.java @@ -0,0 +1,155 @@ +/* + * Last change: $Date: 2005/02/20 01:29:55 $ + * $Revision: 1.15 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.*; +import java.io.*; + +/** + This class encapsulates information about an individual property within a RescueObject + */ +public class IntProperty extends Property { + private int value; + + public IntProperty(int type) { + super(type); + } + + public IntProperty(int type, boolean value) { + this(type,value?1:0); + } + + public IntProperty(int type, int value) { + super(type); + this.value = value; + lastUpdate = 0; + } + + /* + public Property copy() { + return new IntProperty(type,value); + } + */ + + /** + Get the value of this property. + @return The integer value of this property + */ + public int getValue() { + return value; + } + + /** + Get the value of this property as a string + @return A nice string representation of the value of this property + */ + public String getStringValue() { + StringBuffer result = new StringBuffer(); + result.append(value); + return result.toString(); + } + + /** + Set the value of this property. The timestamp will also be updated. Note that this method does not check that the update is newer than the current value - it is up to the application to test for this. + @param newValue The new value + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if the value was actually changed, i.e the new value is different from the old value + @see #isOlderThan(int) + */ + public boolean setValue(int newValue, int timestamp, Object source) { + lastUpdate = timestamp; + lastUpdateSource = source; + if (value==newValue) return false; + value = newValue; + return true; + } + + /** + Update the value of this property. The timestamp will also be updated. + @param newValue The new value + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if the value was actually changed, i.e the new value is different from the old value, and the new timestamp is greater than the old timestamp + @see #isOlderThan(int) + */ + public boolean updateValue(int newValue, int timestamp, Object source) { + if (timestamp <= lastUpdate) return false; + if (lastUpdate>=0 && newValue==value) return false; + lastUpdate = timestamp; + lastUpdateSource = source; + value = newValue; + return true; + } + + /** + Merge this Property with a different one + @param p The Property to merge from + @return true if and only if the value of this property was actually changed + */ + public boolean merge(Property p) { + if (p instanceof IntProperty) { + return updateValue(((IntProperty)p).value,p.lastUpdate,p.lastUpdateSource); + } + return false; + + + /* + if (p.lastUpdate <= this.lastUpdate) return false; + if (p instanceof IntProperty) { + IntProperty i = (IntProperty)p; + lastUpdate = i.lastUpdate; + lastUpdateSource = i.lastUpdateSource; + if (value==i.value) return false; + value = i.value; + return true; + } + return false; + */ + } + + /** + Write this property to an OutputBuffer + @param out The OutputBuffer to write this property to + */ + public void write(OutputBuffer out) { + out.writeInt(value); + } + + /** + Decode property data from an InputBuffer. + @param in An InputBuffer to read data from + @param timestamp The timestamp of this update + @param source The source of this update + @return true if and only if a change was made + */ + public boolean read(InputBuffer in, int timestamp, Object source) { + // System.out.println("Timestep "+timestamp+": updating "+this+" from buffer"); + int newValue = in.readInt(); + return updateValue(newValue,timestamp,source); + /* + if (lastUpdate < timestamp) { + changed = (newValue != value); + if (changed) { + value = newValue; + lastUpdate = timestamp; + lastUpdateSource = source; + } + } + return changed; + */ + } +} diff --git a/modules/oldsims/rescuecore/Launch.java b/modules/oldsims/rescuecore/Launch.java new file mode 100644 index 0000000000000000000000000000000000000000..37a06a66044d782fb999112e3852714267f1bc62 --- /dev/null +++ b/modules/oldsims/rescuecore/Launch.java @@ -0,0 +1,372 @@ +/* + * Last change: $Date: 2005/06/14 21:55:50 $ + * $Revision: 1.9 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +import rescuecore.commands.Command; + +/** + * The Launch class is responsible for starting rescue components. + */ +public class Launch { + private static Collection components; + + private final static int TIMEOUT = 60000; + + private final static String UDP_FLAG = "-u"; + private final static String UDP_LONG_FLAG = "--udp"; + + /** + * Launch some rescue components + */ + public static void main(String[] args) { + boolean tcp = true; + components = new ArrayList(); + InetAddress kernel = null; + int kernelPort = -1; + if (args.length < 3) { + printUsage(); + return; + } + try { + kernel = InetAddress.getByName(args[0]); + kernelPort = Integer.parseInt(args[1]); + } catch (UnknownHostException e) { + System.out.println("Bad host name: " + args[0]); + printUsage(); + return; + } catch (NumberFormatException e) { + System.out.println("Bad host port: " + args[1]); + printUsage(); + return; + } + Collection launch = new ArrayList(); + for (int i = 2; i < args.length; ++i) { + // Process the next argument + if (args[i].startsWith("-")) { + // It is a switch + if (args[i].equalsIgnoreCase(UDP_FLAG) || args[i].equalsIgnoreCase(UDP_LONG_FLAG)) + tcp = false; + else { + System.out.println("Unrecognised option: " + args[i]); + printUsage(); + } + } else { + // Is it a file? + File file = new File(args[i]); + if (file.exists()) { + System.out.println("I don't handle files yet. Try again later."); + } else { + // It's a command line component + try { + Info info = new Info(args[i]); + launch.add(info); + } catch (Exception e) { + e.printStackTrace(); + return; + } + } + } + } + // Launch the components + long start = System.currentTimeMillis(); + for (Iterator it = launch.iterator(); it.hasNext();) { + // Open the connection and input thread + Info next = (Info) it.next(); + try { + System.out.println("Launching " + (next.number == 0 ? "lots of" : "" + next.number) + " components of class " + + next.clazz.getName()); + int max = next.number; + if (max < 1) + max = Integer.MAX_VALUE; + for (int j = 0; j < max; ++j) { + RescueComponent component = instantiate(next); + Connection connection; + if (tcp) + connection = new TCPConnection(kernel, kernelPort); + else + connection = new LongUDPConnection(kernel, kernelPort); + component.setConnection(connection); + if (launch(component, connection)) { + InputThread input = new InputThread(connection, component); + input.start(); + components.add(new ComponentInfo(component, input, connection)); + } else { + connection.close(); + break; + } + } + System.out.println("Finished launching components of class " + next.clazz.getName()); + } catch (Exception e) { + e.printStackTrace(); + } + } + long end = System.currentTimeMillis(); + System.out.println("Launched all components in " + ((end - start) / 1000.0) + " s"); + // Wait for one of them to terminate + boolean running = true; + while (running) { + // Sleep for a bit + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + for (Iterator it = components.iterator(); it.hasNext();) { + RescueComponent c = ((ComponentInfo) it.next()).component; + if (!c.isRunning()) + running = false; + } + } + shutdown(); + } + + private static void printUsage() { + System.out.println("Usage: Launch hostname portnumber components [options]"); + System.out.println("hostname:\tThe name of the machine running the simulation kernel"); + System.out.println("portnumber:\tThe port number that the kernel is listening on"); + System.out.println( + "components:\tA set of components that should be launched. These are all of the form \"[number] classname arguments\". This will launch <number> (default 1) objects of type <classname>, passing <arguments> to each component."); + System.out.println("Options"); + System.out.println("======="); + System.out.println(UDP_FLAG + "\t" + UDP_LONG_FLAG + "\tUse UDP instead of TCP"); + } + + private static RescueComponent instantiate(Info info) throws InstantiationException, IllegalAccessException, + ClassNotFoundException, InvocationTargetException, NoSuchMethodException { + RescueComponent result; + // System.out.print("Instantiating "+info+": "); + if (info.args.length != 0) { + // Try to find a constructor + Constructor c; + try { + c = info.clazz.getDeclaredConstructor(new Class[] { String[].class }); + result = (RescueComponent) c.newInstance(new Object[] { info.args }); + } catch (NoSuchMethodException e) { + try { + Class[] classes = new Class[info.args.length]; + for (int j = 0; j < classes.length; ++j) + classes[j] = String.class; + c = info.clazz.getDeclaredConstructor(classes); + result = (RescueComponent) c.newInstance((Object[]) info.args); + } catch (NoSuchMethodException ex) { + result = (RescueComponent) info.clazz.getDeclaredConstructor().newInstance(); + } + } + } else { + result = (RescueComponent) info.clazz.getDeclaredConstructor().newInstance(); + } + // System.out.println(result); + return result; + } + + private static boolean launch(RescueComponent c, Connection connection) throws IOException { + System.out.println("Launching " + c); + RescueMessage msg = new RescueMessage(); + msg.append(c.generateConnectCommand()); + int okReply = -1; + int errorReply = -1; + switch (c.getComponentType()) { + case RescueConstants.COMPONENT_TYPE_AGENT: + okReply = RescueConstants.KA_CONNECT_OK; + errorReply = RescueConstants.KA_CONNECT_ERROR; + break; + case RescueConstants.COMPONENT_TYPE_SIMULATOR: + okReply = RescueConstants.KS_CONNECT_OK; + errorReply = RescueConstants.KS_CONNECT_ERROR; + break; + case RescueConstants.COMPONENT_TYPE_VIEWER: + okReply = RescueConstants.KV_CONNECT_OK; + errorReply = RescueConstants.KV_CONNECT_ERROR; + break; + default: + throw new RuntimeException("Unknown component type"); + } + connection.send(msg.toByteArray()); + // Wait for reply + long timeout = System.currentTimeMillis() + 60000; + boolean success = false; + do { + // System.out.println("Waiting for reply..."); + try { + byte[] reply = connection.receive(TIMEOUT); + if (reply != null) { + InputBuffer in = new InputBuffer(reply); + Command[] messages = in.readCommands(); + // System.out.println("Received "+messages.length+" messages"); + for (int i = 0; i < messages.length; ++i) { + if (success) + c.handleMessage(messages[i]); + else { + if (messages[i].getType() == okReply) + success = c.handleConnectOK(messages[i]); + if (messages[i].getType() == errorReply) { + String reason = c.handleConnectError(messages[i]); + if (reason != null) { + System.out.println("Error connecting " + c + ": " + reason); + return false; + } + } + } + } + } + } catch (InterruptedException e) { + return false; + } + } while (!success && System.currentTimeMillis() < timeout); + if (!success) + System.out.println("Timeout trying to connect " + c); + return success; + } + + private static void shutdown() { + // Terminate all components + for (Iterator it = components.iterator(); it.hasNext();) { + ComponentInfo next = (ComponentInfo) it.next(); + // Shut down the component + next.component.shutdown(); + // Kill the input thread + next.input.kill(); + } + // Wait for components to finish terminating + for (Iterator it = components.iterator(); it.hasNext();) { + ComponentInfo next = (ComponentInfo) it.next(); + next.input.killAndWait(); + } + // Exit the VM + System.exit(0); + } + + private static class InputThread extends Thread { + private volatile boolean running, alive; + private final Object aliveLock = new Object(); + private RescueComponent target; + private Connection connection; + + InputThread(Connection connection, RescueComponent target) { + this.connection = connection; + this.target = target; + running = alive = true; + } + + public void kill() { + running = false; + interrupt(); + } + + public void killAndWait() { + kill(); + synchronized (aliveLock) { + while (alive) + try { + aliveLock.wait(1000); + } catch (InterruptedException e) { + } + } + connection.close(); + } + + public void run() { + while (running) { + try { + // System.out.println(target+" waiting for input"); + byte[] msg = connection.receive(1000); + if (msg != null) { + // Handy.printBytes("RECEIVED",msg.getData()); + // long start = System.currentTimeMillis(); + Command[] messages = new InputBuffer(msg).readCommands(); + for (int i = 0; i < messages.length; ++i) { + // System.out.println(target+" received "+messages[i]); + target.handleMessage(messages[i]); + } + // long end = System.currentTimeMillis(); + // System.out.println(target+" took "+(end-start)+"ms to process a message"); + } + } catch (InterruptedException e) { + } catch (IOException e) { + e.printStackTrace(); + running = false; + } + } + synchronized (aliveLock) { + alive = false; + aliveLock.notifyAll(); + } + } + } + + private static class ComponentInfo { + RescueComponent component; + InputThread input; + Connection connection; + + public ComponentInfo(RescueComponent c, InputThread i, Connection connection) { + component = c; + input = i; + this.connection = connection; + } + } + + private static class Info { + Class clazz; + int number; + String[] args; + + Info(String line) throws ClassNotFoundException, NoSuchElementException { + StringTokenizer tokens = new StringTokenizer(line); + // Try to parse the first token as an integer + String first = tokens.nextToken(); + String second; + number = 1; + try { + number = Integer.parseInt(first); + second = tokens.nextToken(); + } catch (NumberFormatException e) { + // Couldn't parse it, we'll assume no number was given + second = first; + } + clazz = Class.forName(second); + args = new String[tokens.countTokens()]; + for (int i = 0; i < args.length; ++i) + args[i] = tokens.nextToken(); + } + + public String toString() { + StringBuffer result = new StringBuffer(); + result.append(clazz.getName()); + result.append("("); + for (int i = 0; i < args.length; ++i) { + result.append("\""); + result.append(args[i]); + result.append("\""); + if (i < args.length - 1) + result.append(","); + } + result.append(")"); + return result.toString(); + } + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/LongUDPConnection.java b/modules/oldsims/rescuecore/LongUDPConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..cf46fc82116ef4c56f8273b3587512db3b4d7aa4 --- /dev/null +++ b/modules/oldsims/rescuecore/LongUDPConnection.java @@ -0,0 +1,257 @@ +/* + * Last change: $Date: 2005/06/14 21:55:50 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.net.*; +import java.util.*; +import java.io.*; + +/** + The LongUDPConnection class encapsulates the sending and receiving of LongUDPMessages to and from the kernel. Messages are received asynchronously and stored in a queue. + */ +public class LongUDPConnection implements Connection { + private short nextID; + private DatagramSocket socket; + private List q; + private ReadThread read; + private IOException toThrow; + private InetAddress destination; + private int port; + + private final static long WAIT = 10000; + + /** + Generate a new LongUDPConnection + @throws SocketException if something goes wrong + */ + public LongUDPConnection() throws SocketException { + this(null,-1); + } + + /** + Generate a new LongUDPConnection with a given destination + @param destination The target machine + @param port The target port + @throws SocketException if something goes wrong + */ + public LongUDPConnection(InetAddress destination, int port) throws SocketException { + nextID = 0; + socket = new DatagramSocket(); + socket.setSoTimeout(1000); + // socket.setReceiveBufferSize(1000000); + q = new LinkedList(); + toThrow = null; + read = new ReadThread(); + read.start(); + this.destination = destination; + this.port = port; + // System.err.println("Socket opened listening on port "+socket.getLocalPort()); + } + + public int getLocalPort() { + return socket.getLocalPort(); + } + + /** + Close the socket + */ + public void close() { + read.kill(); + socket.close(); + } + + public void send(byte[] bytes) throws IOException { + if (destination==null || port < 0) throw new IOException("No destination given"); + send(new LongUDPMessage(bytes),destination,port); + } + + /** + Send a message + @param msg The LongUDPMessage to send + @param destination The destination address + @param port The destination port + @throws IOException if something goes wrong during sending + */ + public void send(LongUDPMessage msg, InetAddress destination, int port) throws IOException { + LongUDPFragment[] fragments = msg.fragment(nextID++); + // System.out.println("Sending message to "+destination+":"+port); + for (int i=0;i<fragments.length;++i) { + // System.out.print("Sending fragment "+(i+1)+" of "+fragments.length+"..."); + byte[] buffer = fragments[i].toByteArray(); + // Handy.printBytes(buffer); + DatagramPacket packet = new DatagramPacket(buffer,buffer.length,destination,port); + packet.setLength(buffer.length); + try { + socket.send(packet); + // System.out.println("sent"); + } + catch (InterruptedIOException e) { + System.out.println("send interrupted"); + --i; + } + } + } + + public byte[] receive(int timeout) throws IOException, InterruptedException { + synchronized(q) { + if (toThrow!=null) throw toThrow; + if (q.size()==0) { + if (timeout<1) q.wait(); + else q.wait(timeout); + } + if (toThrow!=null) throw toThrow; + if (q.size()==0) return null; + return ((LongUDPMessage)q.remove(0)).getData(); + } + } + + private class ReadThread extends Thread { + private boolean running; + private boolean alive; + private final Object aliveLock = new Object(); + private Set partial; + private long lastCheck; + + ReadThread() { + running = true; + alive = true; + partial = new HashSet(); + } + + public void kill() { + running = false; + synchronized(aliveLock) { + while (alive) try {aliveLock.wait(1000);} catch (InterruptedException e) {} + } + } + + public void run() { + byte[] input = new byte[65536]; + lastCheck = System.currentTimeMillis(); + DatagramPacket packet = new DatagramPacket(input,input.length); + while (running) { + try { + socket.receive(packet); + // System.out.println("Received packet of length "+packet.getLength()); + byte[] packetData = packet.getData(); + byte[] result = new byte[packet.getLength()]; + System.arraycopy(packetData,0,result,0,result.length); + LongUDPFragment fragment = new LongUDPFragment(result); + // System.out.println("Received "+fragment); + addFragment(fragment); + } + catch (InterruptedIOException e) {} + catch (IOException e) { + e.printStackTrace(); + synchronized(q) { + toThrow = e; + } + } + if (System.currentTimeMillis()>lastCheck+WAIT) checkForTimeouts(); + } + synchronized(aliveLock) { + alive = false; + aliveLock.notifyAll(); + } + } + + private void addFragment(LongUDPFragment fragment) { + boolean found = false; + for (Iterator it = partial.iterator();it.hasNext()&&!found;) { + PartialMessage next = (PartialMessage)it.next(); + if (next.id==fragment.getID()) { + found = true; + if (next.addFragment(fragment)) { + finishedMessage(next.fragments); + it.remove(); + } + // System.out.println("New fragment for message "+next.id+". Fragment size: "+fragment.getData().length+" - now have "+next.received+" of "+next.total+" fragments with a total size of "+next.size); + if (next.total > 10) System.out.println(next.received+"/"+next.total); + } + } + if (!found) { + PartialMessage pm = new PartialMessage(fragment.getID(),fragment.getTotal()); + // System.out.println("Started receiving message "+pm.id); + if (pm.addFragment(fragment)) { + finishedMessage(pm.fragments); + } + else { + partial.add(pm); + } + } + } + + private void finishedMessage(LongUDPFragment[] fragments) { + // System.out.println("Finished receiving message "+fragments[0].getID()); + synchronized(q) { + q.add(LongUDPMessage.defragment(fragments)); + q.notifyAll(); + // System.err.println("Listening on port "+socket.getLocalPort()+" receive queue size: "+q.size()); + } + } + + private void checkForTimeouts() { + // System.out.println("Checking for timeouts"); + for (Iterator it = partial.iterator();it.hasNext();) { + PartialMessage next = (PartialMessage)it.next(); + // System.out.println("Checking "+next+" for timeout at time "+System.currentTimeMillis()); + if (System.currentTimeMillis() > next.last+WAIT) { + System.out.println("Message "+next.id+" timed out - we received "+next.received+" of "+next.total+" packets"); + it.remove(); + } + } + lastCheck = System.currentTimeMillis(); + } + + private class PartialMessage { + int id; + int total; + int received; + long last; + int size; + LongUDPFragment[] fragments; + + PartialMessage(int id, int total) { + this.id = id; + this.total = total; + this.received = 0; + this.last = System.currentTimeMillis(); + fragments = new LongUDPFragment[total]; + size = 0; + } + + public String toString() { + return "Partial message "+id+": received "+received+" of "+total+", last fragment at time "+last; + } + + /** + Returns true iff this message is now complete + */ + public boolean addFragment(LongUDPFragment fragment) { + if (fragment.getID()==id) { + if (fragments[fragment.getNumber()]==null) { + fragments[fragment.getNumber()] = fragment; + ++received; + last = System.currentTimeMillis(); + size += fragment.getData().length; + if (received == total) return true; + } + } + return false; + } + } + } +} diff --git a/modules/oldsims/rescuecore/LongUDPFragment.java b/modules/oldsims/rescuecore/LongUDPFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..e60193d77c40472d2e01a018b2e8ae0ccd65525e --- /dev/null +++ b/modules/oldsims/rescuecore/LongUDPFragment.java @@ -0,0 +1,117 @@ +/* + * Last change: $Date: 2005/02/18 03:34:33 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + This is a piece of a LongUDPMessage + */ +public class LongUDPFragment implements Comparable { + private final static int MAGIC = 0x0008; + private int id; + private int number; + private int total; + private byte[] data; + + /** + Generate a new LongUDPFragment ready for sending + @param id The id of the LongUDPMessage we are sending + @param number The number of this fragment + @param total The total number of fragments we are sending + @param data The body of this fragment + */ + public LongUDPFragment(int id, int number, int total, byte[] data) { + this.id = id; + this.number = number; + this.total = total; + this.data = new byte[data.length]; + System.arraycopy(data,0,this.data,0,data.length); + } + + /** + Generate a new LongUDPFragment from some data sent by the kernal + @param input The raw data from the kernal. It should consist of four 16-bit numbers (magic number, id, fragment number, total number of fragments) followed by some data. + */ + public LongUDPFragment(byte[] input) { + int magic = input[0]<<8 | input[1]; + if (magic!=MAGIC) System.err.println("Oh oh - we got a LongUDPFragment with a bad magic number ("+magic+" instead of "+MAGIC+")"); + id = input[2]<<8 | input[3]; + number = input[4]<<8 | input[5]; + total = input[6]<<8 | input[7]; + data = new byte[input.length-8]; + System.arraycopy(input,8,data,0,data.length); + // System.out.println((number+1)+" of "+total); + } + + public String toString() { + return "LongUDPFragment: "+id+" ("+(number+1)+" of "+total+")"; + } + + public int compareTo(Object o) { + LongUDPFragment l = (LongUDPFragment)o; + return this.number-l.number; + } + + /** + Turn this fragment into a byte array suitable for transmission to the kernal + @return A byte array containing the raw data of this fragment + */ + public byte[] toByteArray() { + byte[] result = new byte[data.length+8]; + write(result,0,MAGIC); + write(result,2,id); + write(result,4,number); + write(result,6,total); + System.arraycopy(data,0,result,8,data.length); + return result; + } + + /** + Get the body of this fragment + @return A byte array containing the body of this fragment + */ + public byte[] getData() { + return data; + } + + /** + Get the message id of this fragment + @return This fragment's message id + */ + public int getID() { + return id; + } + + /** + Get the sequence number of this fragment + @return This fragment's sequence number + */ + public int getNumber() { + return number; + } + + /** + Get the total number of fragments in this fragment's message + @return The total number of fragments in this fragment's message + */ + public int getTotal() { + return total; + } + + private void write(byte[] buffer, int off, int value) { + buffer[off] = (byte)((value>>8)&0xFF); + buffer[off+1] = (byte)(value&0xFF); + } +} diff --git a/modules/oldsims/rescuecore/LongUDPMessage.java b/modules/oldsims/rescuecore/LongUDPMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..87d23a0d844cea51fe8e6faea02e6374c28a35cc --- /dev/null +++ b/modules/oldsims/rescuecore/LongUDPMessage.java @@ -0,0 +1,114 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + This class encapsulates a long UDP message, as defined by the robocup rescue manual version 0.4 + */ +public class LongUDPMessage { + public final static int CHUNK_SIZE = 1024; + private byte[] data; + + /** + Construct a new LongUDPMessage containing the given data + @param data The body of the message + */ + public LongUDPMessage(byte[] data) { + this.data = data; + } + + /** + Break up this message into fragments ready for sending + @param id The id number assigned to each fragment + @return An array of LongUDPFragments that can be sent to the kernal + */ + public LongUDPFragment[] fragment(short id) { + short count = 0; + int done = 0; + short fragments = (short)(data.length/CHUNK_SIZE); + if (data.length%CHUNK_SIZE!=0) ++fragments; + LongUDPFragment[] result = new LongUDPFragment[fragments]; + while (done < data.length) { + byte[] someData = new byte[Math.min(CHUNK_SIZE,data.length-done)]; + System.arraycopy(data,done,someData,0,someData.length); + result[count] = new LongUDPFragment(id,count,fragments,someData); + ++count; + done += someData.length; + } + return result; + } + + /** + Get the body of this message + @return The message body + */ + public byte[] getData() { + return data; + } + + /** + Defragment a set of LongUDPFragments into one LongUDPMessage + @param fragments The fragmented message to reassemble + @return A new LongUDPMessage that has been assembled from the given fragments. If there is a problem, then null is returned. + */ + public static LongUDPMessage defragment(LongUDPFragment[] fragments) { + // System.out.println("Defragmenting message"); + java.util.Arrays.sort(fragments); + // Check that all the fragments are correct + if (!checkFragments(fragments)) return null; + int totalSize = 0; + for (int i=0;i<fragments.length;++i) { + totalSize += fragments[i].getData().length; + } + // System.out.println("Total size: "+totalSize); + byte[] data = new byte[totalSize]; + int offset = 0; + for (int i=0;i<fragments.length;++i) { + byte[] next = fragments[i].getData(); + // System.out.println("Fragment "+(i+1)+" gets appended at position "+offset); + System.arraycopy(next,0,data,offset,next.length); + offset += next.length; + } + // Handy.printBytes("Fragment 1",fragments[0].getData()); + // Handy.printBytes("Fragment 2",fragments[1].getData()); + // Handy.printBytes("Message received",data); + return new LongUDPMessage(data); + } + + private static boolean checkFragments(LongUDPFragment[] fragments) { + // System.out.println("Checking fragments"); + int id = fragments[0].getID(); + int total = fragments[0].getTotal(); + for (int i=0;i<fragments.length;++i) { + if (fragments[i].getID()!=id) { + System.out.println("Fragment "+(i+1)+" has id "+fragments[i].getID()+" - it should be "+id); + return false; + } + if (fragments[i].getTotal()!=total) { + System.out.println("Fragment "+(i+1)+" thinks there should be "+fragments[i].getTotal()+" fragments in total - it should be "+total); + return false; + } + } + // Make sure they're in order + for (int i=0;i<fragments.length;++i) { + if (fragments[i].getNumber()!=i) { + System.out.println("Fragment "+(i+1)+" thinks it should be number "+(fragments[i].getNumber()+1)); + return false; + } + } + return true; + } +} diff --git a/modules/oldsims/rescuecore/Memory.java b/modules/oldsims/rescuecore/Memory.java new file mode 100644 index 0000000000000000000000000000000000000000..3790b676a443708d1f71ca08fa44f5c503c1a61c --- /dev/null +++ b/modules/oldsims/rescuecore/Memory.java @@ -0,0 +1,760 @@ +/* + * Last change: $Date: 2005/03/17 06:07:08 $ + * $Revision: 1.14 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import rescuecore.commands.KASense; +import rescuecore.commands.Update; +import rescuecore.event.MemoryListener; +import rescuecore.event.ObjectAddedEvent; +import rescuecore.event.ObjectChangedEvent; +import rescuecore.event.PropertyChangedEvent; +import rescuecore.event.PropertyListener; +import rescuecore.objects.Building; +import rescuecore.objects.Edge; +import rescuecore.objects.Humanoid; +import rescuecore.objects.MotionlessObject; +import rescuecore.objects.MovingObject; +import rescuecore.objects.Node; +import rescuecore.objects.River; +import rescuecore.objects.RiverNode; +import rescuecore.objects.Road; +import rescuecore.objects.Vertex; + +/** + * This class holds an agents view of the world + */ +public abstract class Memory implements java.io.Serializable { + private int minX, maxX, width, minY, maxY, height; + private transient Collection<MemoryListener> listeners; + private final Object LOCK = Integer.valueOf(0); + + private class InternalPropertyListener implements PropertyListener, java.io.Serializable { + public void propertyChanged(PropertyChangedEvent event) { + fireObjectChanged(event.getObject(), event.getProperty(), event.getTimestamp(), event.getSource()); + } + } + + private final PropertyListener PROPERTY_LISTENER = new InternalPropertyListener(); + + /** + * Construct a new empty memory + */ + protected Memory() { + listeners = new HashSet<MemoryListener>(); + width = -1; + } + + public abstract Memory copy(); + + /** + * Get the minimum x value of the world + * + * @return The minimum x value of the world + */ + public int getMinX() { + if (width == -1) + calculateDimensions(); + return minX; + } + + /** + * Get the maximum x value of the world + * + * @return The maximum x value of the world + */ + public int getMaxX() { + if (width == -1) + calculateDimensions(); + return maxX; + } + + /** + * Get the minimum y value of the world + * + * @return The minimum y value of the world + */ + public int getMinY() { + if (width == -1) + calculateDimensions(); + return minY; + } + + /** + * Get the maximum y value of the world + * + * @return The maximum y value of the world + */ + public int getMaxY() { + if (width == -1) + calculateDimensions(); + return maxY; + } + + /** + * Get the width of the world + * + * @return The width of the world + */ + public int getWidth() { + if (width == -1) + calculateDimensions(); + return width; + } + + /** + * Get the height of the world + * + * @return The height of the world + */ + public int getHeight() { + if (width == -1) + calculateDimensions(); + return height; + } + + private void calculateDimensions() { + Collection<RescueObject> allNodes = getObjectsOfType(RescueConstants.TYPE_NODE); + if (allNodes.size() == 0) { + minX = 0; + maxX = 1; + minY = 0; + maxY = 1; + } else { + Iterator it = allNodes.iterator(); + Node n = (Node) it.next(); + minX = n.getX(); + minY = n.getY(); + maxX = minX; + maxY = minY; + while (it.hasNext()) { + n = (Node) it.next(); + int x = n.getX(); + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + int y = n.getY(); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y); + } + } + width = maxX - minX; + height = maxY - minY; + } + + /** + * Add a MemoryListener that will be informed of adds and updates + * + * @param l The MemoryListener to add + */ + public void addMemoryListener(MemoryListener l) { + synchronized (LOCK) { + if (listeners == null) + listeners = new HashSet<MemoryListener>(); + listeners.add(l); + } + } + + /** + * Remove a MemoryListener + * + * @param l The MemoryListener to remove + */ + public void removeMemoryListener(MemoryListener l) { + synchronized (LOCK) { + if (listeners == null) + listeners = new HashSet<MemoryListener>(); + listeners.remove(l); + } + } + + /** + * Look up a RescueObject by id + * + * @param id The id of the object we want + * @return The object with the given id, or null if there are no objects with + * that id + */ + public abstract RescueObject lookup(int id); + + /** + * Get all objects in memory + * + * @return An array of all objects in memory + */ + // public abstract RescueObject[] getAllObjects(); + + /** + * Get all objects in memory as a Collection + * + * @return A Collection of all objects in memory + */ + public abstract Collection<RescueObject> getAllObjects(); + + /** + * Get all objects of particular types + * + * @param result The Collection we want to fill with RescueObjects. Implementers + * should not remove any items from this Collection. + * @param type The types we want + * @see RescueConstants#TYPE_CIVILIAN + * @see RescueConstants#TYPE_FIRE_BRIGADE + * @see RescueConstants#TYPE_AMBULANCE_TEAM + * @see RescueConstants#TYPE_POLICE_FORCE + * @see RescueConstants#TYPE_ROAD + * @see RescueConstants#TYPE_NODE + * @see RescueConstants#TYPE_RIVER + * @see RescueConstants#TYPE_RIVER_NODE + * @see RescueConstants#TYPE_BUILDING + * @see RescueConstants#TYPE_REFUGE + * @see RescueConstants#TYPE_FIRE_STATION + * @see RescueConstants#TYPE_AMBULANCE_CENTER + * @see RescueConstants#TYPE_POLICE_OFFICE + * @see RescueConstants#TYPE_WORLD + * @see RescueConstants#TYPE_CAR + */ + public abstract void getObjectsOfType(Collection<RescueObject> result, int... types); + + public Collection<RescueObject> getObjectsOfType(int... types) { + Collection<RescueObject> result = new HashSet<RescueObject>(); + getObjectsOfType(result, types); + return result; + } + + /** + * Get all objects of a particular internal type + * + * @param type The type we want + * @return An array of all objects of the given internal type + * @see RescueConstants#INTERNAL_TYPE_CIVILIAN + * @see RescueConstants#INTERNAL_TYPE_FIRE_BRIGADE + * @see RescueConstants#INTERNAL_TYPE_AMBULANCE_TEAM + * @see RescueConstants#INTERNAL_TYPE_POLICE_FORCE + * @see RescueConstants#INTERNAL_TYPE_CAR + * @see RescueConstants#INTERNAL_TYPE_BUILDING + * @see RescueConstants#INTERNAL_TYPE_REFUGE + * @see RescueConstants#INTERNAL_TYPE_FIRE_STATION + * @see RescueConstants#INTERNAL_TYPE_POLICE_OFFICE + * @see RescueConstants#INTERNAL_TYPE_AMBULANCE_CENTER + * @see RescueConstants#INTERNAL_TYPE_ROAD + * @see RescueConstants#INTERNAL_TYPE_NODE + * @see RescueConstants#INTERNAL_TYPE_RIVER + * @see RescueConstants#INTERNAL_TYPE_RIVER_NODE + * @see RescueConstants#INTERNAL_TYPE_WORLD + * @see RescueConstants#INTERNAL_TYPE_ANY_BUILDING + * @see RescueConstants#INTERNAL_TYPE_ANY_HUMANOID + */ + // public abstract RescueObject[] getObjectsOfInternalType(int type); + + /** + * Add a new object with a null source + * + * @param o The new object + * @param timestamp The time that this object is added + */ + public void add(RescueObject o, int timestamp) { + add(o, timestamp, null); + } + + /** + * Add a new object + * + * @param o The new object + * @param timestamp The time that this object is added + * @param source The source of the change + */ + public void add(RescueObject o, int timestamp, Object source) { + fireObjectAdded(o, timestamp, source); + o.addPropertyListener(PROPERTY_LISTENER); + } + + /** + * Remove an object from the memory + * + * @param o The object to be removed + */ + public void remove(RescueObject o) { + o.removePropertyListener(PROPERTY_LISTENER); + } + + /** + * Update our memory from a KASense object + * + * @param sense The KASense object to update from + */ + public void update(KASense sense) { + update(sense.getUpdatedObjects(), sense.getTime(), RescueConstants.SOURCE_SENSE); + } + + /** + * Update our memory from an Update object + * + * @param sense The Update object to update from + */ + public void update(Update update) { + update(update.getUpdatedObjects(), update.getTime(), RescueConstants.SOURCE_UPDATE); + } + + public void update(RescueObject[] changed, int time, Object source) { + for (int i = 0; i < changed.length; ++i) { + RescueObject current = lookup(changed[i].getID()); + if (current == null) { + RescueObject copy = changed[i].copy(); + add(copy, time, source); + } else { + current.merge(changed[i]); + } + } + } + + /** + * Notify listeners that an object has been added + * + * @param o The added object + * @param source The source of the change + */ + private void fireObjectAdded(RescueObject o, int timestep, Object source) { + ObjectAddedEvent event = new ObjectAddedEvent(o, timestep, source); + synchronized (LOCK) { + if (listeners == null) + listeners = new HashSet<MemoryListener>(); + for (Iterator it = listeners.iterator(); it.hasNext();) { + ((MemoryListener) it.next()).objectAdded(event); + } + } + } + + /** + * Notify listeners that an object has been changed + * + * @param o The changed object + * @param source The source of the change + */ + private void fireObjectChanged(RescueObject o, int property, int timestep, Object source) { + ObjectChangedEvent event = new ObjectChangedEvent(o, property, timestep, source); + synchronized (LOCK) { + if (listeners == null) + listeners = new HashSet<MemoryListener>(); + for (Iterator it = listeners.iterator(); it.hasNext();) { + ((MemoryListener) it.next()).objectChanged(event); + } + } + } + + /** + * Get the Euclidean distance between two objects + * + * @param o1 The first object + * @param o2 The second object + * @return The distance between the two objects + */ + public double getDistance(RescueObject o1, RescueObject o2) throws CannotFindLocationException { + int x1, x2, y1, y2; + int[] xy = getXY(o1); + x1 = xy[0]; + y1 = xy[1]; + xy = getXY(o2); + x2 = xy[0]; + y2 = xy[1]; + double x = x2 - x1; + x = x * x; + double y = y2 - y1; + y = y * y; + return Math.sqrt(x + y); + } + + /** + * Get the angle in arc-seconds from one object to another + * + * @param from The first object + * @param to The second object + * @return The angle from 'from' to 'to', in arc-seconds + */ + public int getAngle(RescueObject from, RescueObject to) throws CannotFindLocationException { + int x1, x2, y1, y2; + int[] xy = getXY(from); + x1 = xy[0]; + y1 = xy[1]; + xy = getXY(to); + x2 = xy[0]; + y2 = xy[1]; + int dx = x2 - x1; + int dy = y2 - y1; + double theta = Math.atan2(-dx, dy); + if (theta < 0) + theta += 2 * Math.PI; + return (int) (theta * 360 * 60 * 60 / (2.0 * Math.PI)); + } + + /** + * Get the X and Y coordinates of an object + * + * @param o The object of interest + * @return An array of two ints. The first element is the X coordinate, the + * second is Y. The return value may be null if the location of the + * object is unknown + */ + public int[] getXY(RescueObject o) throws CannotFindLocationException { + int x = 0, y = 0; + switch (o.getType()) { + case RescueConstants.TYPE_CIVILIAN: + case RescueConstants.TYPE_FIRE_BRIGADE: + case RescueConstants.TYPE_AMBULANCE_TEAM: + case RescueConstants.TYPE_POLICE_FORCE: + case RescueConstants.TYPE_CAR: + RescueObject location = lookup(((Humanoid) o).getPosition()); + if (location == null) { + throw new CannotFindLocationException(); + } + switch (location.getType()) { + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_POLICE_OFFICE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_AMBULANCE_CENTER: + case RescueConstants.TYPE_REFUGE: + x = ((Building) location).getX(); + y = ((Building) location).getY(); + break; + case RescueConstants.TYPE_NODE: + x = ((Node) location).getX(); + y = ((Node) location).getY(); + break; + case RescueConstants.TYPE_RIVER_NODE: + x = ((RiverNode) location).getX(); + y = ((RiverNode) location).getY(); + break; + case RescueConstants.TYPE_ROAD: + Node roadHead = (Node) lookup(((Road) location).getHead()); + Node roadTail = (Node) lookup(((Road) location).getTail()); + int roadLength = ((Road) location).getLength(); + double d = ((double) ((Humanoid) o).getPositionExtra()) / ((double) roadLength); + int xDelta = roadTail.getX() - roadHead.getX(); + int yDelta = roadTail.getY() - roadHead.getY(); + x = (int) (roadHead.getX() + d * xDelta); + y = (int) (roadHead.getY() + d * yDelta); + break; + case RescueConstants.TYPE_AMBULANCE_TEAM: + return getXY(location); + default: + System.err + .println("Can't find location for a humanoid that lives in " + Handy.getTypeName(location.getType())); + throw new CannotFindLocationException( + o + " is located at " + location + " but I don't know how to handle that"); + } + break; + case RescueConstants.TYPE_NODE: + x = ((Node) o).getX(); + y = ((Node) o).getY(); + break; + case RescueConstants.TYPE_RIVER_NODE: + x = ((RiverNode) o).getX(); + y = ((RiverNode) o).getY(); + break; + case RescueConstants.TYPE_ROAD: + // Halfway between head and tail? + Node head = (Node) lookup(((Road) o).getHead()); + Node tail = (Node) lookup(((Road) o).getTail()); + if (head == null) { + System.err.println("Can't find head of this road: " + o.toLongString()); + } + if (tail == null) { + System.err.println("Can't find tail of this road: " + o.toLongString()); + } + x = (head.getX() + tail.getX()) / 2; + y = (head.getY() + tail.getY()) / 2; + break; + case RescueConstants.TYPE_RIVER: + RiverNode head_ = (RiverNode) lookup(((River) o).getHead()); + RiverNode tail_ = (RiverNode) lookup(((River) o).getTail()); + x = (head_.getX() + tail_.getX()) / 2; + y = (head_.getY() + tail_.getY()) / 2; + break; + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_AMBULANCE_CENTER: + case RescueConstants.TYPE_POLICE_OFFICE: + x = ((Building) o).getX(); + y = ((Building) o).getY(); + break; + case RescueConstants.TYPE_WORLD: + throw new CannotFindLocationException("Tried to find the position of a TYPE_WORLD object"); + } + return new int[] { x, y }; + } + + /** + * Get the RescueObject the represents the position of a MovingObject. This + * might be another MovingObject, for example the position of a civilian in an + * ambulance will be the ambulance + * + * @param object The object in question + * @return The position of the object, or null if the position cannot be found + */ + public RescueObject getPosition(MovingObject o) { + RescueObject result = lookup(o.getPosition()); + return result; + } + + /** + * Get the MotionlessObject that represents a given object's position in the + * world + * + * @param o The object in question + * @return The MotionlessObject that represents a given object's position in the + * world, or null if the position cannot be found + */ + public MotionlessObject getMotionlessPosition(RescueObject o) { + switch (o.getType()) { + case RescueConstants.TYPE_CIVILIAN: + case RescueConstants.TYPE_FIRE_BRIGADE: + case RescueConstants.TYPE_POLICE_FORCE: + case RescueConstants.TYPE_AMBULANCE_TEAM: + case RescueConstants.TYPE_CAR: + RescueObject position = lookup(((Humanoid) o).getPosition()); + if (position == null) + return null; + return getMotionlessPosition(position); + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_POLICE_OFFICE: + case RescueConstants.TYPE_AMBULANCE_CENTER: + case RescueConstants.TYPE_ROAD: + case RescueConstants.TYPE_NODE: + case RescueConstants.TYPE_RIVER_NODE: + case RescueConstants.TYPE_RIVER: + return (MotionlessObject) o; + default: + return null; + } + } + + /** + * Find the closest Node to a RescueObject. If the given object is a Node then + * it is returned. If it is a road, then the head node is returned. If it is a + * building, then the entrance node closest to the center of the building is + * returned. If it is a mobile agent (civilian, fire brigade etc) then the + * closest node to it's location is returned + * + * @param o The object we want the closest node to + * @return The Node object that is closest to the given RescueObject + */ + public Node getClosestNode(RescueObject o) { + switch (o.getType()) { + case RescueConstants.TYPE_NODE: + return (Node) o; + case RescueConstants.TYPE_ROAD: + return (Node) lookup(((Road) o).getHead()); + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_AMBULANCE_CENTER: + case RescueConstants.TYPE_POLICE_OFFICE: + int[] entrances = ((Building) o).getEntrances(); + int best = -1; + double bestDistance = Double.MAX_VALUE; + for (int i = 0; i < entrances.length; ++i) { + try { + double d = getDistance(o, lookup(entrances[i])); + if (d < bestDistance) { + best = i; + bestDistance = d; + } + } catch (CannotFindLocationException e) { + } + } + if (best == -1) + return null; + return (Node) lookup(entrances[best]); + case RescueConstants.TYPE_CIVILIAN: + case RescueConstants.TYPE_FIRE_BRIGADE: + case RescueConstants.TYPE_AMBULANCE_TEAM: + case RescueConstants.TYPE_POLICE_FORCE: + case RescueConstants.TYPE_CAR: + RescueObject location = lookup(((Humanoid) o).getPosition()); + if (location == null) + return null; + switch (location.getType()) { + case RescueConstants.TYPE_NODE: + return (Node) location; + case RescueConstants.TYPE_ROAD: + int length = ((Road) location).getLength(); + int pos = ((Humanoid) o).getPositionExtra(); + if (pos < length / 2) + return (Node) lookup(((Road) location).getHead()); + else + return (Node) lookup(((Road) location).getTail()); + default: + return getClosestNode(location); + } + } + return null; + } + + /** + * Get the node id (either head or tail of the given road) that is closest to + * the position given + * + * @param road The road that we want the head or tail of + * @param positionExtra The agent's position along the road + * @return Either the head or tail node of the given road, whichever is closest + */ + public Node getClosestNode(Road road, int positionExtra) { + if (positionExtra < (road.getLength() / 2)) + return (Node) lookup(road.getHead()); + return (Node) lookup(road.getTail()); + } + + /** + * Find the neighbours of a RescueObject. This method delegates to @{link + * #findNodeNeighbours(Node)}, @{link #findRoadNeighbours(Road)} or @{link + * #findBuildingNeighbours(Building)} for Nodes, Roads and Buildings + * respectively + * + * @param o The RescueObject we want the neighbours for + * @return An array of all neighbours of the given object + */ + public RescueObject[] findNeighbours(RescueObject o) { + switch (o.getType()) { + case RescueConstants.TYPE_NODE: + return findNodeNeighbours((Node) o); + case RescueConstants.TYPE_ROAD: + return findRoadNeighbours((Road) o); + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_POLICE_OFFICE: + case RescueConstants.TYPE_AMBULANCE_CENTER: + return findBuildingNeighbours((Building) o); + default: + System.out.println("Cannot find neighbours of a " + Handy.getTypeName(o.getType())); + return null; + } + } + + /** + * Find the neighbours of a Node. This method returns all RescueObjects + * identified by this Nodes 'edges' property + * + * @param node The Node we want the neighbours for + * @return An array of all neighbours of the given Node + */ + public RescueObject[] findNodeNeighbours(Node node) { + int[] edges = node.getEdges(); + RescueObject[] all = new RescueObject[edges.length]; + int count = 0; + for (int i = 0; i < edges.length; ++i) { + all[i] = lookup(edges[i]); + } + return all; + } + + /** + * Find the neighbours of a Road. This method returns the head and tail nodes + * connected to this Road + * + * @param road The Road we want the neighbours for + * @return An array containing the head and tail nodes, in that order + */ + public RescueObject[] findRoadNeighbours(Road road) { + RescueObject head = lookup(road.getHead()); + RescueObject tail = lookup(road.getTail()); + return new RescueObject[] { head, tail }; + } + + public Node getHead(Road r) { + return (Node) lookup(r.getHead()); + } + + public Node getTail(Road r) { + return (Node) lookup(r.getTail()); + } + + public Road getRoadBetween(Node n1, Node n2) { + RescueObject[] neighbours = findNodeNeighbours(n1); + for (int i = 0; i < neighbours.length; ++i) { + if (neighbours[i] instanceof Road) { + Road r = (Road) neighbours[i]; + if ((r.getHead() == n1.getID() && r.getTail() == n2.getID()) + || (r.getHead() == n2.getID() && r.getTail() == n1.getID())) + return r; + } + } + return null; + } + + public boolean neighbours(RescueObject o1, RescueObject o2) { + int id = o2.getID(); + if (o1 instanceof Building) { + int[] entrances = ((Building) o1).getEntrances(); + for (int i = 0; i < entrances.length; ++i) + if (entrances[i] == id) + return true; + } + if (o1 instanceof Edge) { + Edge e = (Edge) o1; + if (e.getHead() == id || e.getTail() == id) + return true; + } + if (o1 instanceof Vertex) { + return isEdge(id, (Vertex) o1); + } + return false; + } + + public boolean isEdge(int id, Vertex v) { + int[] edges = v.getEdges(); + for (int i = 0; i < edges.length; ++i) + if (edges[i] == id) + return true; + return false; + } + + public boolean isEdge(RescueObject o, Node node) { + return isEdge(o.getID(), node); + } + + public boolean isEntrance(int id, Building b) { + int[] entrances = b.getEntrances(); + for (int i = 0; i < entrances.length; ++i) + if (entrances[i] == id) + return true; + return false; + } + + public boolean isEntrance(RescueObject o, Building b) { + return isEntrance(o.getID(), b); + } + + /** + * Find the neighbours of a Building. This method returns all Nodes identified + * by this Buildings 'entrances' property + * + * @param b The Building we want the neighbours for + * @return An array of all Nodes identified by the 'entrances' property + */ + public RescueObject[] findBuildingNeighbours(Building b) { + int[] entrances = b.getEntrances(); + RescueObject[] all = new RescueObject[entrances.length]; + for (int i = 0; i < entrances.length; ++i) { + all[i] = lookup(entrances[i]); + } + return all; + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/OutputBuffer.java b/modules/oldsims/rescuecore/OutputBuffer.java new file mode 100644 index 0000000000000000000000000000000000000000..64218b0a401416f07e5ebbb81b3db3e504e9264a --- /dev/null +++ b/modules/oldsims/rescuecore/OutputBuffer.java @@ -0,0 +1,139 @@ +/* + * Last change: $Date: 2005/02/20 01:29:55 $ + * $Revision: 1.15 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.io.UnsupportedEncodingException; + +import rescuecore.commands.Command; + +public class OutputBuffer { + private byte[] data; + private int index; + + public OutputBuffer() { + this(256); + } + + public OutputBuffer(int capacity) { + data = new byte[capacity]; + } + + public void writeByte(byte b) { + expand(RescueConstants.BYTE_SIZE); + data[index++] = b; + } + + public void writeBytes(byte[] b) { + expand(b.length*RescueConstants.BYTE_SIZE); + for (int i=0;i<b.length;++i) data[index++] = b[i]; + } + + public void writeShort(int s) { + expand(RescueConstants.SHORT_SIZE); + data[index++] = (byte)(s>>8 & 0xFF); + data[index++] = (byte)(s & 0xFF); + } + + public void writeInt(int i) { + expand(RescueConstants.INT_SIZE); + data[index++] = (byte)(i >> 24 & 0xFF); + data[index++] = (byte)(i >> 16 & 0xFF); + data[index++] = (byte)(i >> 8 & 0xFF); + data[index++] = (byte)(i & 0xFF); + } + + public void writeString(String s) { + try { + byte[] bytes = s.getBytes("UTF-8"); + writeInt(bytes.length); + writeBytes(bytes); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 encoding not found!", e); + } + } + + public void writeObject(RescueObject o) { + writeInt(o.getType()); + writeInt(o.getID()); + int base = markBlock(); + o.write(this); + writeBlockSize(base); + } + + public void writeObjects(RescueObject[] objects) { + writeInt(objects.length); + for (int i=0;i<objects.length;++i) writeObject(objects[i]); + } + + public void writeCommand(Command c) { + writeInt(c.getType()); + int base = markBlock(); + c.write(this); + writeBlockSize(base); + } + + public void writeCommands(Command[] c) { + writeInt(c.length); + for (int i=0;i<c.length;++i) writeCommand(c[i]); + } + + public int markBlock() { + writeInt(0); + return index; + } + + public void writeBlockSize(int mark) { + int size = index-mark; + writeInt(size,mark-RescueConstants.INT_SIZE); + } + + public void clear() { + index = 0; + } + + public int getSize() { + return index; + } + + public byte[] getBytes() { + byte[] result = new byte[index]; + System.arraycopy(data,0,result,0,index); + return result; + } + + public void ensureCapacity(int capacity) { + expand(capacity); + } + + public void trim(int size) { + index = Math.min(index,size); + } + + private void writeInt(int i, int position) { + int old = index; + index = position; + writeInt(i); + if (index < old) index = old; // If we went past the old position then just leave things as they are + } + + private void expand(int needed) { + if (index+needed < data.length) return; + byte[] newData = new byte[Math.max(index+needed,data.length*2)]; + System.arraycopy(data,0,newData,0,data.length); + data = newData; + } +} diff --git a/modules/oldsims/rescuecore/PlatoonAgent.java b/modules/oldsims/rescuecore/PlatoonAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..da39e8b295f53db1bcea2450f2e7c77f56ad3779 --- /dev/null +++ b/modules/oldsims/rescuecore/PlatoonAgent.java @@ -0,0 +1,125 @@ +/* + * Last change: $Date: 2004/05/20 23:41:59 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import rescuecore.objects.*; +import rescuecore.commands.Command; + +/** + An abstract superclass for platoon agents (i.e fire brigade, police force, ambulance team). + */ +public abstract class PlatoonAgent extends Agent { + protected PlatoonAgent(int... types) { + super(types); + } + + private Humanoid me() { + return (Humanoid)memory.lookup(id); + } + + /** + Get this agent's current position as a RescueObject + @return The RescueObject representing this agent's current position + */ + protected RescueObject getLocation() { + return memory.lookup(me().getPosition()); + } + + /** + Get this agent's current position + @return This agent's current position + */ + protected int getPosition() { + return me().getPosition(); + } + + /** + Get this agent's current position + @return This agent's current position + */ + protected int getPositionExtra() { + return me().getPositionExtra(); + } + + /** + Append a move command + @param ids The list of road/node/building ids that we want to move through + */ + protected void move(int[] ids) { + if (ids==null || ids.length==0) return; + appendCommand(Command.MOVE(id,timeStep,ids)); + } + + /** + Append an extinguish command + @param target The building to extinguish + */ + protected void extinguish(Building target) { + try { + int[] xy = memory.getXY(me()); + appendCommand(Command.EXTINGUISH(id,timeStep,target.getID())); + } + catch (CannotFindLocationException e) { + System.err.println(e); + } + } + + /** + Append an extinguish command + @param target The building to extinguish + @param power The amount of water to use + */ + protected void extinguish(Building target, int power) { + try { + int[] xy = memory.getXY(me()); + appendCommand(Command.EXTINGUISH(id,timeStep,target.getID(),memory.getAngle(me(),target),xy[0],xy[1],power)); + } + catch (CannotFindLocationException e) { + System.err.println(e); + } + } + + /** + Append a clear command + @param target The road to clear + */ + protected void clear(Road target) { + appendCommand(Command.CLEAR(id,timeStep,target.getID())); + } + + /** + Append a rescue command + @param target The humanoid to rescue + */ + protected void rescue(Humanoid target) { + appendCommand(Command.RESCUE(id,timeStep,target.getID())); + } + + /** + Append a load command + @param target The humanoid to load + */ + protected void load(Humanoid target) { + appendCommand(Command.LOAD(id,timeStep,target.getID())); + } + + /** + Append an unload command + */ + protected void unload() { + appendCommand(Command.UNLOAD(id,timeStep)); + } +} diff --git a/modules/oldsims/rescuecore/Property.java b/modules/oldsims/rescuecore/Property.java new file mode 100644 index 0000000000000000000000000000000000000000..337c7543c81a657b302adf6b6da091ca50ba6c64 --- /dev/null +++ b/modules/oldsims/rescuecore/Property.java @@ -0,0 +1,100 @@ +/* + * Last change: $Date: 2005/02/20 01:29:55 $ + * $Revision: 1.15 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + This class encapsulates information about an individual property within a RescueObject + */ +public abstract class Property implements java.io.Serializable { + private int type; + protected int lastUpdate; + protected Object lastUpdateSource; + + protected Property(int type) { + this.type = type; + lastUpdate = RescueConstants.VALUE_UNKNOWN; + lastUpdateSource = null; + } + + /** + Get the type of this property + @return The type of this property + */ + public int getType() { + return type; + } + + public abstract boolean read(InputBuffer in, int timestamp, Object source); + public abstract void write(OutputBuffer out); + + /** + Get the last time this property was updated + @return The last time this property was updated + */ + public int getLastUpdate() { + return lastUpdate; + } + + /** + Get the source of the last update + @return The source of the last update + */ + public Object getLastUpdateSource() { + return lastUpdateSource; + } + + /** + Find out whether this property was last updated before a certain time + @param time The time to check against + @return true if and only if this property was last updated before the given time + */ + public boolean isOlderThan(int time) { + return lastUpdate < time; + } + + /** + Find out whether the value of this property is known or not + @return true if and only if the value of this property is known + */ + public boolean isValueKnown() { + return lastUpdate >= 0; + } + + /** + Find out whether the value of this property is assumed or not + @return true if and only if the value of this property is assumed + */ + public boolean isValueAssumed() { + return lastUpdate == RescueConstants.VALUE_ASSUMED; + } + + public String toString() { + return Handy.getPropertyName(type)+": "+getStringValue()+" (last update: "+lastUpdate+")"; + } + + /** + Get the value of this property as a string + @return A nice string representation of the value of this property + */ + public abstract String getStringValue(); + + /** + Update this Property from a different one + @param p The Property to update from + @return true if and only if the value of this property was actually changed + */ + public abstract boolean merge(Property p); +} diff --git a/modules/oldsims/rescuecore/RescueComponent.java b/modules/oldsims/rescuecore/RescueComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..1db5c4ce45d1831a7afea8190814bfb1e4651760 --- /dev/null +++ b/modules/oldsims/rescuecore/RescueComponent.java @@ -0,0 +1,114 @@ +/* + * Last change: $Date: 2005/06/14 21:55:50 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import rescuecore.commands.*; +import java.io.*; + +public abstract class RescueComponent { + private RescueMessage currentMessage; + private Connection connection; + + /** Handle an incoming message + @param msg The incoming message + */ + public abstract void handleMessage(Command msg); + + /** + Get the type of this component, either COMPONENT_TYPE_AGENT, COMPONENT_TYPE_SIMULATOR or COMPONENT_TYPE_VIEWER. + @return The type of this component + @see RescueConstants#COMPONENT_TYPE_AGENT + @see RescueConstants#COMPONENT_TYPE_SIMULATOR + @see RescueConstants#COMPONENT_TYPE_VIEWER + */ + public abstract int getComponentType(); + + /** + Generate a Command that can be used to connect this component to the kernel + @return A Command that will connect this component to the kernel + */ + public abstract Command generateConnectCommand(); + + /** + Handle a message that looks like a successful connection to the kernel + @param c The message from the kernel + @return If this really was a successful connection + */ + public abstract boolean handleConnectOK(Command c); + + /** + Handle a message that looks like an unsuccessful connection to the kernel + @param c The message from the kernel + @return A string describing the error, or null if this message does not indicate a failed connection + */ + public abstract String handleConnectError(Command c); + + /** + Find out whether this component is still running + @return true iff this component is still running + */ + public abstract boolean isRunning(); + + /** + Shut the component down + */ + public abstract void shutdown(); + + /** + Set the Connection that should be used for talking to the kernel. Subclasses should NOT attempt to read input from this connection. + @param connection The Connection to use when talking to the kernel. + */ + public void setConnection(Connection connection) { + this.connection = connection; + } + + /** + Send a message to the kernel + @param message The message to send + */ + public void sendMessage(RescueMessage message) { + if (connection==null) { + System.out.println(this+" tried to send to a null socket"); + return; + } + try { + connection.send(message.toByteArray()); + } + catch (IOException e) { + System.err.println("Error while sending message: "+e); + e.printStackTrace(); + } + } + + /** + Add a Command to our current set of Commands to be sent to the kernel. Commands will be buffered until @{link #flushCommands()} is called. + @param c The next Command to send. + */ + protected void appendCommand(Command c) { + if (currentMessage==null) currentMessage = new RescueMessage(); + currentMessage.append(c); + } + + /** + Send all our buffered commands to the kernel + */ + protected void flushCommands() { + if (currentMessage!=null) { + sendMessage(currentMessage); + } + currentMessage = null; + } +} diff --git a/modules/oldsims/rescuecore/RescueConstants.java b/modules/oldsims/rescuecore/RescueConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..2b215965160c457322e71ab5abe22d162ee48a9c --- /dev/null +++ b/modules/oldsims/rescuecore/RescueConstants.java @@ -0,0 +1,195 @@ +/* + * Last change: $Date: 2004/05/20 23:41:59 $ + * $Revision: 1.9 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +/** + A whole pile of useful constants + */ +public final class RescueConstants { + private RescueConstants() {} + + public final static int MAX_EXTINGUISH_DISTANCE = 30000; + public final static int MAX_EXTINGUISH_POWER = 1000; + public final static int MAX_WATER = 15000; + public final static int MAX_RESCUE_DISTANCE = 30000; + public final static int MAX_HP = 10000; + public final static int MAX_VISION = 10000; + + public final static int SAY_LENGTH = 256; + public final static int TELL_LENGTH = 256; + public final static int MAX_PLATOON_MESSAGES = 4; + public final static int MAX_CENTER_MESSAGES = 2; + + public final static int COMPONENT_TYPE_AGENT = 1; + public final static int COMPONENT_TYPE_SIMULATOR = 2; + public final static int COMPONENT_TYPE_VIEWER = 3; + + public final static int BYTE_SIZE = 1; + public final static int SHORT_SIZE = 2; + public final static int INT_SIZE = 4; + + public final static int FIERYNESS_NOT_BURNT = 0; + public final static int FIERYNESS_HEATING = 1; + public final static int FIERYNESS_BURNING = 2; + public final static int FIERYNESS_INFERNO = 3; + public final static int FIERYNESS_WATER_DAMAGE = 4; + public final static int FIERYNESS_SLIGHTLY_BURNT = 5; + public final static int FIERYNESS_MODERATELY_BURNT = 6; + public final static int FIERYNESS_VERY_BURNT = 7; + public final static int FIERYNESS_BURNT_OUT = 8; + public final static int NUM_FIERYNESS_LEVELS = 9; + + public final static int TYPE_NULL = 0; + public final static int TYPE_WORLD = 0x01; + public final static int TYPE_ROAD = 0x02; + public final static int TYPE_RIVER = 0x03; + public final static int TYPE_NODE = 0x04; + public final static int TYPE_RIVER_NODE = 0x05; + public final static int TYPE_BUILDING = 0x20; + public final static int TYPE_REFUGE = 0x21; + public final static int TYPE_FIRE_STATION = 0x22; + public final static int TYPE_AMBULANCE_CENTER = 0x23; + public final static int TYPE_POLICE_OFFICE = 0x24; + public final static int TYPE_CIVILIAN = 0x40; + public final static int TYPE_CAR = 0x41; + public final static int TYPE_FIRE_BRIGADE = 0x42; + public final static int TYPE_AMBULANCE_TEAM = 0x43; + public final static int TYPE_POLICE_FORCE = 0x44; + + public final static int AGENT_TYPE_CIVILIAN = 0x01; + public final static int AGENT_TYPE_FIRE_BRIGADE = 0x02; + public final static int AGENT_TYPE_FIRE_STATION = 0x04; + public final static int AGENT_TYPE_AMBULANCE_TEAM = 0x08; + public final static int AGENT_TYPE_AMBULANCE_CENTER = 0x10; + public final static int AGENT_TYPE_POLICE_FORCE = 0x20; + public final static int AGENT_TYPE_POLICE_OFFICE = 0x40; + public final static int AGENT_TYPE_ANY_MOBILE = AGENT_TYPE_FIRE_BRIGADE | AGENT_TYPE_AMBULANCE_TEAM | AGENT_TYPE_POLICE_FORCE; + public final static int AGENT_TYPE_ANY_BUILDING = AGENT_TYPE_FIRE_STATION | AGENT_TYPE_AMBULANCE_CENTER | AGENT_TYPE_POLICE_OFFICE; + public final static int AGENT_TYPE_ANY_AGENT = AGENT_TYPE_ANY_MOBILE | AGENT_TYPE_ANY_BUILDING; + public final static int AGENT_TYPE_ANY = AGENT_TYPE_ANY_MOBILE | AGENT_TYPE_ANY_BUILDING | AGENT_TYPE_CIVILIAN; + + public final static int PROPERTY_NULL = 0; + public final static int PROPERTY_MIN = 1; + public final static int PROPERTY_START_TIME = 1; + public final static int PROPERTY_LONGITUDE = 2; + public final static int PROPERTY_LATITUDE = 3; + public final static int PROPERTY_WIND_FORCE = 4; + public final static int PROPERTY_WIND_DIRECTION = 5; + + public final static int PROPERTY_HEAD = 6; + public final static int PROPERTY_TAIL = 7; + public final static int PROPERTY_LENGTH = 8; + + public final static int PROPERTY_ROAD_KIND = 9; + public final static int PROPERTY_CARS_PASS_TO_HEAD = 10; + public final static int PROPERTY_CARS_PASS_TO_TAIL = 11; + public final static int PROPERTY_HUMANS_PASS_TO_HEAD = 12; + public final static int PROPERTY_HUMANS_PASS_TO_TAIL = 13; + public final static int PROPERTY_WIDTH = 14; + public final static int PROPERTY_BLOCK = 15; + public final static int PROPERTY_REPAIR_COST = 16; + public final static int PROPERTY_MEDIAN_STRIP = 17; + public final static int PROPERTY_LINES_TO_HEAD = 18; + public final static int PROPERTY_LINES_TO_TAIL = 19; + public final static int PROPERTY_WIDTH_FOR_WALKERS = 20; + public final static int PROPERTY_SIGNAL = 21; + public final static int PROPERTY_SHORTCUT_TO_TURN = 22; + public final static int PROPERTY_POCKET_TO_TURN_ACROSS = 23; + public final static int PROPERTY_SIGNAL_TIMING = 24; + + public final static int PROPERTY_X = 25; + public final static int PROPERTY_Y = 26; + public final static int PROPERTY_EDGES = 27; + + public final static int PROPERTY_FLOORS = 28; + public final static int PROPERTY_BUILDING_ATTRIBUTES = 29; + public final static int PROPERTY_IGNITION = 30; + public final static int PROPERTY_FIERYNESS = 31; + public final static int PROPERTY_BROKENNESS = 32; + public final static int PROPERTY_ENTRANCES = 33; + public final static int PROPERTY_BUILDING_CODE = 34; + public final static int PROPERTY_BUILDING_AREA_GROUND = 35; + public final static int PROPERTY_BUILDING_AREA_TOTAL = 36; + public final static int PROPERTY_BUILDING_APEXES = 37; + + public final static int PROPERTY_POSITION = 38; + public final static int PROPERTY_POSITION_EXTRA = 39; + public final static int PROPERTY_DIRECTION = 40; + public final static int PROPERTY_POSITION_HISTORY = 41; + public final static int PROPERTY_STAMINA = 42; + public final static int PROPERTY_HP = 43; + public final static int PROPERTY_DAMAGE = 44; + public final static int PROPERTY_BURIEDNESS = 45; + public final static int PROPERTY_WATER_QUANTITY = 46; + + public final static int PROPERTY_BUILDING_TEMPERATURE = 47; + public final static int PROPERTY_BUILDING_IMPORTANCE = 48; + + public final static int PROPERTY_MAX = 48; + + public final static int HEADER_NULL = 0; + + public final static int KG_CONNECT = 0x10; + public final static int KG_ACKNOWLEDGE = 0x11; + public final static int GK_CONNECT_OK = 0x12; + public final static int GK_CONNECT_ERROR = 0x13; + + public final static int SK_CONNECT = 0x20; + public final static int SK_ACKNOWLEDGE = 0x21; + public final static int SK_UPDATE = 0x22; + public final static int KS_CONNECT_OK = 0x23; + public final static int KS_CONNECT_ERROR = 0x24; + + public final static int VK_CONNECT = 0x30; + public final static int VK_ACKNOWLEDGE = 0x31; + public final static int KV_CONNECT_OK = 0x32; + public final static int KV_CONNECT_ERROR = 0x33; + + public final static int AK_CONNECT = 0x40; + public final static int AK_ACKNOWLEDGE = 0x41; + public final static int KA_CONNECT_OK = 0x42; + public final static int KA_CONNECT_ERROR = 0x43; + public final static int KA_SENSE = 0x44; + public final static int KA_HEAR = 0x45; + public final static int KA_HEAR_SAY = 0x46; + public final static int KA_HEAR_TELL = 0x47; + + public final static int UPDATE = 0x50; + public final static int COMMANDS = 0x51; + + public final static int AK_REST = 0x80; + public final static int AK_MOVE = 0x81; + public final static int AK_LOAD = 0x82; + public final static int AK_UNLOAD = 0x83; + public final static int AK_SAY = 0x84; + public final static int AK_TELL = 0x85; + public final static int AK_EXTINGUISH = 0x86; + // public final static int AK_STRETCH = 0x87; + public final static int AK_RESCUE = 0x88; + public final static int AK_CLEAR = 0x89; + public final static int AK_CHANNEL = 0x90; + public final static int AK_REPAIR = 0x91; //changed from 90 + + public final static Object SOURCE_SENSE = "KA_SENSE"; + public final static Object SOURCE_INITIAL = "KA_CONNECT_OK"; + public final static Object SOURCE_UNKNOWN = "Unknown"; + public final static Object SOURCE_NONE = "None"; + public final static Object SOURCE_ASSUMED = "Assumed"; + public final static Object SOURCE_UPDATE = "Update"; + + public final static int VALUE_UNKNOWN = -2; + public final static int VALUE_ASSUMED = -1; +} diff --git a/modules/oldsims/rescuecore/RescueMessage.java b/modules/oldsims/rescuecore/RescueMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..3ba4cb151b2b2af8c2d71c84ce2812d7307d543c --- /dev/null +++ b/modules/oldsims/rescuecore/RescueMessage.java @@ -0,0 +1,132 @@ +/* + * Last change: $Date: 2004/05/20 23:41:59 $ $Revision: 1.5 $ Copyright (c) + * 2004, The Black Sheep, Department of Computer Science, The University of + * Auckland All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. Neither the name of + * The Black Sheep, The Department of Computer Science or The University of + * Auckland nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package rescuecore; + +import rescuecore.commands.*; +import java.util.*; + +/** + * A RescueMessage is a package of Commands to be sent to the kernal + */ +public class RescueMessage { + + private List parts; + + + public RescueMessage() { + parts = new ArrayList(); + } + + + /** + * Append a new Command to the end of this message + * + * @param command + * The Command to append + */ + public void append( Command command ) { + parts.add( command ); + } + + + /** + * Go through all parts of this message and make sure we don't have too many + * of any kind of Command + * + * @param type + * The bitwise or of the types of Command that are being culled + * @param max + * The maximum number of Commands of the given type that we are + * allowed + * @return The number of matching Commands left in this message once we're + * finished + */ + @Deprecated + public int cull( int type, int max ) { + int count = 0; + for ( Iterator it = parts.iterator(); it.hasNext(); ) { + Command next = (Command) it.next(); + if ( ( next.getType() & type ) != 0 ) { + ++count; + if ( count > max ) it.remove(); + } + } + return count; + } + + + /** + * Go through all parts of this message and count how many of each type of + * Commannd we have + * + * @param type + * The bitwise or of the types of Command that are being counted + * @return The number of matching Commands + */ + @Deprecated + public int count( int type ) { + int count = 0; + for ( Iterator it = parts.iterator(); it.hasNext(); ) { + Command next = (Command) it.next(); + if ( ( next.getType() & type ) != 0 ) { + ++count; + } + } + return count; + } + + + /** + * Turn this RescueMessage into a raw byte array ready for wrapping in a + * LongUDPMessage + * + * @return A byte array ready for wrapping + * @see LongUDPMessage + */ + public byte[] toByteArray() { + OutputBuffer out = new OutputBuffer(); + Command[] all = new Command[parts.size()]; + parts.toArray( all ); + out.writeCommands( all ); + return out.getBytes(); + } + + + public String toString() { + StringBuffer result = new StringBuffer(); + result.append( "RescueMessage with " ); + result.append( parts.size() ); + result.append( parts.size() == 1 ? " part" : " parts" ); + result.append( ": " ); + for ( Iterator it = parts.iterator(); it.hasNext(); ) { + result.append( + Handy.getCommandTypeName( ( (Command) it.next() ).getType() ) ); + if ( it.hasNext() ) result.append( ", " ); + } + return result.toString(); + } +} diff --git a/modules/oldsims/rescuecore/RescueObject.java b/modules/oldsims/rescuecore/RescueObject.java new file mode 100644 index 0000000000000000000000000000000000000000..1253823f1e5e167564cc423801702cfb5e824f0a --- /dev/null +++ b/modules/oldsims/rescuecore/RescueObject.java @@ -0,0 +1,750 @@ +/* + * Last change: $Date: 2005/03/16 02:30:30 $ + * $Revision: 1.20 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; + +import rescuecore.event.PropertyChangedEvent; +import rescuecore.event.PropertyListener; +import rescuecore.objects.AmbulanceCenter; +import rescuecore.objects.AmbulanceTeam; +import rescuecore.objects.Building; +import rescuecore.objects.Car; +import rescuecore.objects.Civilian; +import rescuecore.objects.FireBrigade; +import rescuecore.objects.FireStation; +import rescuecore.objects.Node; +import rescuecore.objects.PoliceForce; +import rescuecore.objects.PoliceOffice; +import rescuecore.objects.Refuge; +import rescuecore.objects.River; +import rescuecore.objects.RiverNode; +import rescuecore.objects.Road; +import rescuecore.objects.World; + +/** + * This is the base class for all objects in the simulation environment + */ +public abstract class RescueObject implements java.io.Serializable { + private transient Collection listeners; + /** The kernel-assigned id */ + protected int id; + + private transient int[] knownProperties; + + private transient HashMap annotations; + + /** + * Construct a new RescueObject + **/ + protected RescueObject() { + listeners = new ArrayList(); + id = 0; + knownProperties = null; + annotations = null; + } + + /** + * Adds an annotation to this object. This will overwrite any existing + * annotations with the same key. + */ + public void addAnnotation(String key, Object annotation) { + if (annotations == null) + annotations = new HashMap(); + annotations.put(key, annotation); + } + + /** + * Gets an annotation for the given String identifier. If no such annotation + * exists this will return null. + * + * @return The annotation. + */ + public Object getAnnotation(String key) { + if (annotations == null) { + // System.err.println("Could not find annotation named \""+key+"\""); + return null; + } + return annotations.get(key); + } + + /** + * Get this objects type + * + * @return The type of this object + * @see RescueConstants#TYPE_CIVILIAN + * @see RescueConstants#TYPE_FIRE_BRIGADE + * @see RescueConstants#TYPE_AMBULANCE_TEAM + * @see RescueConstants#TYPE_POLICE_FORCE + * @see RescueConstants#TYPE_ROAD + * @see RescueConstants#TYPE_NODE + * @see RescueConstants#TYPE_RIVER + * @see RescueConstants#TYPE_RIVER_NODE + * @see RescueConstants#TYPE_BUILDING + * @see RescueConstants#TYPE_REFUGE + * @see RescueConstants#TYPE_FIRE_STATION + * @see RescueConstants#TYPE_AMBULANCE_CENTER + * @see RescueConstants#TYPE_POLICE_OFFICE + * @see RescueConstants#TYPE_WORLD + * @see RescueConstants#TYPE_CAR + */ + public abstract int getType(); + + /** + * Get this objects id + */ + public int getID() { + return id; + } + + public void setID(int id) { + this.id = id; + } + + public int hashCode() { + return id; + } + + public boolean equals(Object o) { + if (o instanceof RescueObject) + return this.id == ((RescueObject) o).id; + return false; + } + + /** + * Add a PropertyListener + * + * @param l The listener that will be notified of changes to any properties in + * this object + */ + public void addPropertyListener(PropertyListener l) { + if (listeners == null) + listeners = new ArrayList(); + synchronized (listeners) { + listeners.add(l); + } + } + + /** + * Remove a PropertyListener + * + * @param l The listener that will no longer be notified of changes to any + * properties in this object + */ + public void removePropertyListener(PropertyListener l) { + if (listeners == null) + listeners = new ArrayList(); + synchronized (listeners) { + listeners.remove(l); + } + } + + /** + * Get a String representation of all this object's properties + * + * @return A String representation of all this object's properties + */ + protected String getPropertiesString() { + StringBuffer result = new StringBuffer(); + int[] known = getKnownPropertyTypes(); + for (int i = 0; i < known.length; ++i) { + if (i != 0) + result.append(", "); + result.append(getPropertyAsString(known[i])); + } + return result.toString(); + } + + /** + * Get all property types known by this object + * + * @return An array containing all the property types known by this object + * @see RescueConstants#PROPERTY_NULL + * @see RescueConstants#PROPERTY_START_TIME + * @see RescueConstants#PROPERTY_LONGITUDE + * @see RescueConstants#PROPERTY_LATITUDE + * @see RescueConstants#PROPERTY_WIND_FORCE + * @see RescueConstants#PROPERTY_WIND_DIRECTION + * @see RescueConstants#PROPERTY_X + * @see RescueConstants#PROPERTY_Y + * @see RescueConstants#PROPERTY_DIRECTION + * @see RescueConstants#PROPERTY_POSITION + * @see RescueConstants#PROPERTY_POSITION_HISTORY + * @see RescueConstants#PROPERTY_POSITION_EXTRA + * @see RescueConstants#PROPERTY_STAMINA + * @see RescueConstants#PROPERTY_HP + * @see RescueConstants#PROPERTY_DAMAGE + * @see RescueConstants#PROPERTY_BURIEDNESS + * @see RescueConstants#PROPERTY_FLOORS + * @see RescueConstants#PROPERTY_BUILDING_ATTRIBUTES + * @see RescueConstants#PROPERTY_IGNITION + * @see RescueConstants#PROPERTY_BROKENNESS + * @see RescueConstants#PROPERTY_FIERYNESS + * @see RescueConstants#PROPERTY_ENTRANCES + * @see RescueConstants#PROPERTY_BUILDING_SHAPE_ID + * @see RescueConstants#PROPERTY_BUILDING_CODE + * @see RescueConstants#PROPERTY_BUILDING_AREA_GROUND + * @see RescueConstants#PROPERTY_BUILDING_AREA_TOTAL + * @see RescueConstants#PROPERTY_BUILDING_APEXES + * @see RescueConstants#PROPERTY_WATER_QUANTITY + * @see RescueConstants#PROPERTY_STRETCHED_LENGTH + * @see RescueConstants#PROPERTY_HEAD + * @see RescueConstants#PROPERTY_TAIL + * @see RescueConstants#PROPERTY_LENGTH + * @see RescueConstants#PROPERTY_ROAD_KIND + * @see RescueConstants#PROPERTY_CARS_PASS_TO_HEAD + * @see RescueConstants#PROPERTY_CARS_PASS_TO_TAIL + * @see RescueConstants#PROPERTY_HUMANS_PASS_TO_HEAD + * @see RescueConstants#PROPERTY_HUMANS_PASS_TO_TAIL + * @see RescueConstants#PROPERTY_WIDTH + * @see RescueConstants#PROPERTY_BLOCK + * @see RescueConstants#PROPERTY_REPAIR_COST + * @see RescueConstants#PROPERTY_MEDIAN_STRIP + * @see RescueConstants#PROPERTY_LINES_TO_HEAD + * @see RescueConstants#PROPERTY_LINES_TO_TAIL + * @see RescueConstants#PROPERTY_WIDTH_FOR_WALKERS + * @see RescueConstants#PROPERTY_EDGES + * @see RescueConstants#PROPERTY_SIGNAL + * @see RescueConstants#PROPERTY_SIGNAL_TIMING + * @see RescueConstants#PROPERTY_SHORTCUT_TO_TURN + * @see RescueConstants#PROPERTY_POCKET_TO_TURN_ACROSS + */ + public int[] getKnownPropertyTypes() { + if (knownProperties == null) { + Collection c = new ArrayList(); + for (int i = RescueConstants.PROPERTY_MIN; i <= RescueConstants.PROPERTY_MAX; ++i) { + if (propertyExists(i)) + c.add(Integer.valueOf(i)); + } + Object[] all = c.toArray(); + knownProperties = new int[all.length]; + for (int i = 0; i < all.length; ++i) + knownProperties[i] = ((Integer) all[i]).intValue(); + } + return knownProperties; + } + + /** + * Get whether this object has a particular property or not + * + * @param property The property in question + * @return True iff the given property exists for this object + * @see RescueConstants#PROPERTY_NULL + * @see RescueConstants#PROPERTY_START_TIME + * @see RescueConstants#PROPERTY_LONGITUDE + * @see RescueConstants#PROPERTY_LATITUDE + * @see RescueConstants#PROPERTY_WIND_FORCE + * @see RescueConstants#PROPERTY_WIND_DIRECTION + * @see RescueConstants#PROPERTY_X + * @see RescueConstants#PROPERTY_Y + * @see RescueConstants#PROPERTY_DIRECTION + * @see RescueConstants#PROPERTY_POSITION + * @see RescueConstants#PROPERTY_POSITION_HISTORY + * @see RescueConstants#PROPERTY_POSITION_EXTRA + * @see RescueConstants#PROPERTY_STAMINA + * @see RescueConstants#PROPERTY_HP + * @see RescueConstants#PROPERTY_DAMAGE + * @see RescueConstants#PROPERTY_BURIEDNESS + * @see RescueConstants#PROPERTY_FLOORS + * @see RescueConstants#PROPERTY_BUILDING_ATTRIBUTES + * @see RescueConstants#PROPERTY_IGNITION + * @see RescueConstants#PROPERTY_BROKENNESS + * @see RescueConstants#PROPERTY_FIERYNESS + * @see RescueConstants#PROPERTY_ENTRANCES + * @see RescueConstants#PROPERTY_BUILDING_SHAPE_ID + * @see RescueConstants#PROPERTY_BUILDING_CODE + * @see RescueConstants#PROPERTY_BUILDING_AREA_GROUND + * @see RescueConstants#PROPERTY_BUILDING_AREA_TOTAL + * @see RescueConstants#PROPERTY_BUILDING_APEXES + * @see RescueConstants#PROPERTY_WATER_QUANTITY + * @see RescueConstants#PROPERTY_STRETCHED_LENGTH + * @see RescueConstants#PROPERTY_HEAD + * @see RescueConstants#PROPERTY_TAIL + * @see RescueConstants#PROPERTY_LENGTH + * @see RescueConstants#PROPERTY_ROAD_KIND + * @see RescueConstants#PROPERTY_CARS_PASS_TO_HEAD + * @see RescueConstants#PROPERTY_CARS_PASS_TO_TAIL + * @see RescueConstants#PROPERTY_HUMANS_PASS_TO_HEAD + * @see RescueConstants#PROPERTY_HUMANS_PASS_TO_TAIL + * @see RescueConstants#PROPERTY_WIDTH + * @see RescueConstants#PROPERTY_BLOCK + * @see RescueConstants#PROPERTY_REPAIR_COST + * @see RescueConstants#PROPERTY_MEDIAN_STRIP + * @see RescueConstants#PROPERTY_LINES_TO_HEAD + * @see RescueConstants#PROPERTY_LINES_TO_TAIL + * @see RescueConstants#PROPERTY_WIDTH_FOR_WALKERS + * @see RescueConstants#PROPERTY_EDGES + * @see RescueConstants#PROPERTY_SIGNAL + * @see RescueConstants#PROPERTY_SIGNAL_TIMING + * @see RescueConstants#PROPERTY_SHORTCUT_TO_TURN + * @see RescueConstants#PROPERTY_POCKET_TO_TURN_ACROSS + */ + public final boolean propertyExists(int property) { + return getProperty(property) != null; + // return false; + } + + public Property getProperty(int property) /* throws UnknownPropertyException */ { + // throw new UnknownPropertyException(property); + return null; + } + + /* + * Get a particular property as a String + * + * @param property The property we want + * + * @return A String representation of the property + * + * @throws UnknownPropertyException if the property is unknown + */ + public String getPropertyAsString(int property) /* throws UnknownPropertyException */ { + Property p = getProperty(property); + if (p == null) + return "<unknown>"; + return p.getStringValue(); + } + + /** + * Get the last time a property was updated + * + * @param property The property we want + * @return The last time the property was updated + * @throws UnknownPropertyException if the property is unknown + */ + public int getLastPropertyUpdate(int property) /* throws UnknownPropertyException */ { + Property p = getProperty(property); + if (p == null) + return RescueConstants.VALUE_UNKNOWN; + return p.getLastUpdate(); + } + + /** + * Get the source for last update to a property + * + * @param property The property we want + * @return The source of the last update to the property + * @throws UnknownPropertyException if the property is unknown + */ + public Object getLastPropertyUpdateSource(int property) /* throws UnknownPropertyException */ { + Property p = getProperty(property); + if (p == null) + return null; + return p.getLastUpdateSource(); + } + + /** + * Is the value of a particular property known? + * + * @param property The property we want + * @return True if and only if the value of the given property is known + * @throws UnknownPropertyException if the property does not exist + */ + public boolean isPropertyValueKnown(int property) /* throws UnknownPropertyException */ { + Property p = getProperty(property); + if (p == null) + return false; + return p.isValueKnown(); + } + + /** + * Is the value of a particular property assumed? + * + * @param property The property we want + * @return True if and only if the value of the given property is assumed + * @throws UnknownPropertyException if the property does not exist + */ + public boolean isPropertyValueAssumed(int property) /* throws UnknownPropertyException */ { + Property p = getProperty(property); + if (p == null) + return false; + return p.isValueAssumed(); + } + + /** + * Update the value of a property. All PropertyListeners will be notified if the + * value is actually updated. + * + * @param property The property to update + * @param timestamp The current time step. If this update is more recent than + * the current value of the property then the update will + * proceed + * @param newValue The new value of the property + * @param source The source of the change + * @return true if and only if the update was successful. An update will not + * occur if the new value is less recent than our current information, + * or if the current value is the same as the new value + * @throws UnknownPropertyException if the property is not recognised + */ + // public boolean updateProperty(int property, int timestamp, int newValue, + // Object source) throws UnknownPropertyException { + // return updateProperty(getProperty(property),timestamp,newValue,source); + // } + + /** + * Update the value of a property. All PropertyListeners will be notified if the + * value is actually updated. + * + * @param property The property to update + * @param timestamp The current time step. If this update is more recent than + * the current value of the property then the update will + * proceed + * @param newValue The new value of the property + * @param source The source of the change + * @return true if and only if the update was successful. An update will not + * occur if the new value is less recent than our current information, + * or if the current value is the same as the new value + * @throws UnknownPropertyException if the property is not recognised + */ + // public boolean updateProperty(int property, int timestamp, int[] newValue, + // Object source) throws UnknownPropertyException { + // return updateProperty(getProperty(property),timestamp,newValue,source); + // } + + /** + * Update the value of a property. All PropertyListeners will be notified if the + * value is actually updated. + * + * @param property The property to update + * @param timestamp The current time step. If this update is more recent than + * the current value of the property then the update will + * proceed + * @param newValue The new value of the property + * @param source The source of the change + * @return true if and only if the update was successful. An update will not + * occur if the new value is less recent than our current information, + * or if the current value is the same as the new value + * @throws UnknownPropertyException if the property is not recognised + */ + // public boolean updateProperty(int property, int timestamp, boolean newValue, + // Object source) throws UnknownPropertyException { + // return updateProperty(getProperty(property),timestamp,newValue,source); + // } + + protected boolean setProperty(IntProperty p, int newValue, int timestamp, + Object source) /* throws UnknownPropertyException */ { + if (p.setValue(newValue, timestamp, source)) { + firePropertyChanged(p.getType(), timestamp, source); + return true; + } + return false; + } + + protected boolean setProperty(ArrayProperty p, int[] newValue, int timestamp, + Object source) /* throws UnknownPropertyException */ { + if (p.setValues(newValue, timestamp, source)) { + firePropertyChanged(p.getType(), timestamp, source); + return true; + } + return false; + } + + protected boolean updateProperty(IntProperty p, int newValue, int timestamp, + Object source) /* throws UnknownPropertyException */ { + if (p.updateValue(newValue, timestamp, source)) { + firePropertyChanged(p.getType(), timestamp, source); + return true; + } + return false; + } + + protected boolean updateProperty(ArrayProperty p, int[] newValue, int timestamp, + Object source) /* throws UnknownPropertyException */ { + if (p.updateValues(newValue, timestamp, source)) { + firePropertyChanged(p.getType(), timestamp, source); + return true; + } + return false; + } + + /* + * protected boolean updateProperty(Property p, int timestamp, boolean newValue, + * Object source) throws UnknownPropertyException { if + * (p.updateValue(newValue,timestamp,source)){ + * firePropertyChanged(p.getPropertyType(),timestamp,source); return true; } + * return false; } + */ + + public String toString() { + return Handy.getTypeName(getType()) + " " + id; + } + + public String toLongString() { + return Handy.getTypeName(getType()) + " " + id + " [" + getPropertiesString() + "]"; + } + + /** + * Read from an InputBuffer + * + * @param in An InputBuffer to read data from + * @param timestamp The timestamp of the update + * @param source The source of the change + */ + public final void read(InputBuffer in, int timestamp, Object source) { + int prop; + do { + prop = in.readInt(); + if (prop != RescueConstants.PROPERTY_NULL) { + int size = in.readInt(); + Property p = getProperty(prop); + if (p != null) { + if (p.read(in, timestamp, source)) + firePropertyChanged(prop, timestamp, source); + } else { + System.err.println("Got an unknown property (" + prop + ") from the stream"); + in.skip(size); + } + } + } while (prop != RescueConstants.PROPERTY_NULL); + } + + /** + * Update this RescueObject from a different one + * + * @param o The object to update from + */ + public final void merge(RescueObject o) { + int[] known = getKnownPropertyTypes(); + for (int i = 0; i < known.length; ++i) { + Property oldP = getProperty(known[i]); + Property newP = o.getProperty(known[i]); + if (oldP != null && newP != null) { + if (oldP.merge(newP)) + firePropertyChanged(oldP.getType(), oldP.getLastUpdate(), oldP.getLastUpdateSource()); + } + } + } + + /** + * Write this RescueObject to an OutputBuffer + * + * @param out The OutputBuffer to write to + */ + public void write(OutputBuffer out) { + int[] props = getKnownPropertyTypes(); + for (int i = 0; i < props.length; ++i) { + Property p = getProperty(props[i]); + if (p != null && p.isValueKnown()) { + out.writeInt(p.getType()); + int base = out.markBlock(); + p.write(out); + out.writeBlockSize(base); + } + } + out.writeInt(RescueConstants.PROPERTY_NULL); + } + + /** + * Is this object a building? + * + * @return true if and only if this object is a building + */ + public boolean isBuilding() { + // return (getInternalType() & INTERNAL_TYPE_ANY_BUILDING) != 0; + switch (getType()) { + case RescueConstants.TYPE_BUILDING: + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_POLICE_OFFICE: + case RescueConstants.TYPE_AMBULANCE_CENTER: + return true; + default: + return false; + } + } + + public boolean isOrdinaryBuilding() { + return getType() == RescueConstants.TYPE_BUILDING; + } + + /** + * Is this object a refuge? + * + * @return true if and only if this object is a refuge + */ + public boolean isRefuge() { + return getType() == RescueConstants.TYPE_REFUGE; + } + + /** + * Is this object a fire station? + * + * @return true if and only if this object is a fire station + */ + public boolean isFireStation() { + return getType() == RescueConstants.TYPE_FIRE_STATION; + } + + /** + * Is this object a police office? + * + * @return true if and only if this object is a police office + */ + public boolean isPoliceOffice() { + return getType() == RescueConstants.TYPE_POLICE_OFFICE; + } + + /** + * Is this object an ambulance center? + * + * @return true if and only if this object is an ambulance center + */ + public boolean isAmbulanceCenter() { + return getType() == RescueConstants.TYPE_AMBULANCE_CENTER; + } + + /** + * Is this object a road? + * + * @return true if and only if this object is a road + */ + public boolean isRoad() { + return getType() == RescueConstants.TYPE_ROAD; + } + + /** + * Is this object a node? + * + * @return true if and only if this object is a node + */ + public boolean isNode() { + return getType() == RescueConstants.TYPE_NODE; + } + + /** + * Is this object a humanoid? + * + * @return true if and only if this object is a humanoid + */ + public boolean isHumanoid() { + // return (getInternalType() & INTERNAL_TYPE_ANY_HUMANOID) != 0; + switch (getType()) { + case RescueConstants.TYPE_CIVILIAN: + case RescueConstants.TYPE_AMBULANCE_TEAM: + case RescueConstants.TYPE_FIRE_BRIGADE: + case RescueConstants.TYPE_POLICE_FORCE: + return true; + default: + return false; + } + } + + /** + * Is this object a civilian? + * + * @return true if and only if this object is a civilian + */ + public boolean isCivilian() { + return getType() == RescueConstants.TYPE_CIVILIAN; + } + + /** + * Is this object an ambulance team? + * + * @return true if and only if this object is an ambulance + */ + public boolean isAmbulanceTeam() { + return getType() == RescueConstants.TYPE_AMBULANCE_TEAM; + } + + /** + * Is this object a police force? + * + * @return true if and only if this object is a police force + */ + public boolean isPoliceForce() { + return getType() == RescueConstants.TYPE_POLICE_FORCE; + } + + /** + * Is this object a fire brigade? + * + * @return true if and only if this object is a fire brigade + */ + public boolean isFireBrigade() { + return getType() == RescueConstants.TYPE_FIRE_BRIGADE; + } + + protected void firePropertyChanged(int property, int timestep, Object source) { + if (listeners == null) + return; + PropertyChangedEvent event = new PropertyChangedEvent(this, property, timestep, source); + synchronized (listeners) { + for (Iterator it = listeners.iterator(); it.hasNext();) { + ((PropertyListener) it.next()).propertyChanged(event); + } + } + } + + /** + * Make a deep copy of this RescueObject + * + * @return A deep copy of this object + */ + public final RescueObject copy() { + RescueObject result = newObject(getType()); + result.merge(this); + result.setID(id); + return result; + } + + /** + * Construct a new RescueObject + * + * @param type The type of the new object + * @return A newly constructed RescueObject + */ + public static RescueObject newObject(int type) { + switch (type) { + case RescueConstants.TYPE_WORLD: + return new World(); + case RescueConstants.TYPE_CIVILIAN: + return new Civilian(); + case RescueConstants.TYPE_CAR: + return new Car(); + case RescueConstants.TYPE_FIRE_BRIGADE: + return new FireBrigade(); + case RescueConstants.TYPE_AMBULANCE_TEAM: + return new AmbulanceTeam(); + case RescueConstants.TYPE_POLICE_FORCE: + return new PoliceForce(); + case RescueConstants.TYPE_ROAD: + return new Road(); + case RescueConstants.TYPE_RIVER: + return new River(); + case RescueConstants.TYPE_NODE: + return new Node(); + case RescueConstants.TYPE_RIVER_NODE: + return new RiverNode(); + case RescueConstants.TYPE_BUILDING: + return new Building(); + case RescueConstants.TYPE_REFUGE: + return new Refuge(); + case RescueConstants.TYPE_FIRE_STATION: + return new FireStation(); + case RescueConstants.TYPE_AMBULANCE_CENTER: + return new AmbulanceCenter(); + case RescueConstants.TYPE_POLICE_OFFICE: + return new PoliceOffice(); + default: + System.out.println("WARNING: Unknown object type: " + type); + return null; + } + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/Simulator.java b/modules/oldsims/rescuecore/Simulator.java new file mode 100644 index 0000000000000000000000000000000000000000..3d8da887545d1bbf71aed6bbab68c4d9fb18a835 --- /dev/null +++ b/modules/oldsims/rescuecore/Simulator.java @@ -0,0 +1,93 @@ +/* + * Last change: $Date$ + * $Revision$ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.io.*; +import java.util.*; +import rescuecore.commands.*; +//import rescuecore.debug.*; + +public abstract class Simulator extends RescueComponent { + protected int timeStep; + protected Memory memory; + protected boolean running; + + protected Simulator() { + running = false; + timeStep = -1; + } + + public final int getComponentType() { + return RescueConstants.COMPONENT_TYPE_SIMULATOR; + } + + public final Command generateConnectCommand() { + return new SKConnect(getClass().getName()); + } + + public final boolean handleConnectOK(Command c) { + return true; + } + + public final String handleConnectError(Command c) { + return null; + } + + public boolean isRunning() { + return running; + } + + public void shutdown() { + running = false; + } + + public void handleMessage(Command c) { + // System.out.println("Handling "+c); + switch (c.getType()) { + case RescueConstants.COMMANDS: + handleCommands(c); + break; + case RescueConstants.KS_CONNECT_OK: + // Someone obviously didn't get our SK_ACKNOWLEDGE + RescueMessage ack = new RescueMessage(); + // ack.append(new SK_ACKNOWLEDGE()); + sendMessage(ack); + break; + } + } + + /** + Initialise this simulator. Subclasses that override this method should invoke super.initialise(knowledge) at some point. + @param knowledge This simulator's knowledge of the world + */ + protected void initialise(RescueObject[] knowledge) { + memory = generateMemory(); + for (int i=0;i<knowledge.length;++i) { + memory.add(knowledge[i],0,RescueConstants.SOURCE_INITIAL); + } + } + + /** + Construct a new Memory object for use by this Agent. This method allows Agents to customise their choice of Memory object. The default implementation returns an {@link HashMemory}. + @return A new Memory object + */ + protected Memory generateMemory() { + return new HashMemory(); + } + + private void handleCommands(Command c) { + } +} diff --git a/modules/oldsims/rescuecore/TCPConnection.java b/modules/oldsims/rescuecore/TCPConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..ee2054d5a0c7fab3c27dbbfc5d1ba931573ef7a4 --- /dev/null +++ b/modules/oldsims/rescuecore/TCPConnection.java @@ -0,0 +1,154 @@ +/* + * Last change: $Date: 2005/06/14 21:55:50 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore; + +import java.net.*; +import java.util.*; +import java.io.*; + +/** + The TCPSocket class sends messages via TCP + */ +public class TCPConnection implements Connection { + private Socket socket; + private List q; + private ReadThread read; + private IOException toThrow; + private InetAddress destination; + private int port; + private OutputStream out; + + /** + Generate a new TCPSocket with a given destination + @param destination The target machine + @param port The target port + @throws SocketException if something goes wrong + */ + public TCPConnection(InetAddress destination, int port) throws SocketException, IOException { + socket = new Socket(destination,port); + socket.setSoTimeout(1000); + q = new LinkedList(); + toThrow = null; + read = new ReadThread(); + read.start(); + this.destination = destination; + this.port = port; + out = socket.getOutputStream(); + // System.err.println("Socket opened listening on port "+socket.getLocalPort()); + } + + public int getLocalPort() { + return socket.getLocalPort(); + } + + /** + Close the socket + */ + public void close() { + read.kill(); + try { + out.close(); + socket.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public void send(byte[] bytes) throws IOException { + out.write((byte)((bytes.length>>24)&0xFF)); + out.write((byte)((bytes.length>>16)&0xFF)); + out.write((byte)((bytes.length>>8)&0xFF)); + out.write((byte)(bytes.length&0xFF)); + out.write(bytes); + out.flush(); + } + + public byte[] receive(int timeout) throws IOException, InterruptedException { + synchronized(q) { + if (toThrow!=null) throw toThrow; + if (q.size()==0) { + if (timeout<1) q.wait(); + else q.wait(timeout); + } + if (toThrow!=null) throw toThrow; + if (q.size()==0) return null; + return (byte[])q.remove(0); + } + } + + private class ReadThread extends Thread { + private boolean running; + private boolean alive; + private final Object aliveLock = new Object(); + private InputStream in; + + ReadThread() throws IOException { + running = true; + alive = true; + in = socket.getInputStream(); + } + + public void kill() { + running = false; + synchronized(aliveLock) { + while (alive) try {aliveLock.wait(1000);} catch (InterruptedException e) {} + } + } + + public void run() { + while (running) { + try { + int length = in.read()<<24 | in.read()<<16 | in.read()<<8 | in.read(); + if (length<0) { + running = false; + continue; + } + byte[] data = new byte[length]; + int count = 0; + while (count<length) { + int amount = in.read(data,count,length-count); + count += amount; + } + synchronized(q) { + q.add(data); + q.notifyAll(); + } + } + catch (InterruptedIOException e) {} + catch (SocketException e) { + running = false; + } + catch (IOException e) { + e.printStackTrace(); + synchronized(q) { + toThrow = e; + } + } + } + try { + in.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + synchronized(aliveLock) { + alive = false; + aliveLock.notifyAll(); + } + } + } +} diff --git a/modules/oldsims/rescuecore/commands/AKAcknowledge.java b/modules/oldsims/rescuecore/commands/AKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..95e719e50bdbf5db5a30dfbaf790c9aa10b76b35 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKAcknowledge.java @@ -0,0 +1,46 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKAcknowledge extends Command { + private int requestID; + private int agentID; + + public AKAcknowledge(int requestID, int agentID) { + super(RescueConstants.AK_ACKNOWLEDGE); + this.requestID = requestID; + this.agentID = agentID; + } + + public AKAcknowledge(InputBuffer in) { + super(RescueConstants.AK_ACKNOWLEDGE); + read(in); + } + + public void write(OutputBuffer out) { + out.writeInt(requestID); + out.writeInt(agentID); + } + + public void read(InputBuffer in) { + requestID = in.readInt(); + agentID = in.readInt(); + } +} diff --git a/modules/oldsims/rescuecore/commands/AKChannel.java b/modules/oldsims/rescuecore/commands/AKChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..9fa4fc09d7142621e9a05261a776cca6011cd860 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKChannel.java @@ -0,0 +1,57 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKChannel extends AgentCommand { + private byte[] channels; + + public AKChannel(int senderID, int time, byte channel) { + this(senderID, time, new byte[] {channel}); + } + + public AKChannel(int senderID, int time, byte[] channels) { + super(RescueConstants.AK_CHANNEL, senderID, time); + this.channels = new byte[channels.length]; + System.arraycopy(channels,0,this.channels,0,channels.length); + } + + public AKChannel(InputBuffer in) { + super(RescueConstants.AK_CHANNEL,0,0); + read(in); + } + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(channels.length); + for (int i=0;i<channels.length;++i) + out.writeByte(channels[i]); + } + + public void read(InputBuffer in) { + super.read(in); + channels = new byte[in.readInt()]; + for (int i=0;i<channels.length;++i) + channels[i] = in.readByte(); + } + + public byte[] getChannels() { + return channels; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKClear.java b/modules/oldsims/rescuecore/commands/AKClear.java new file mode 100644 index 0000000000000000000000000000000000000000..c6680a12f55803b14af1be94c9468e53b0b47b09 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKClear.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKClear extends AgentCommand { + private int target; + + public AKClear(int senderID, int time, int target) { + super(RescueConstants.AK_CLEAR,senderID,time); + this.target = target; + } + + public AKClear(InputBuffer in) { + super(RescueConstants.AK_CLEAR,0,0); + read(in); + } + + /* + public AKClear(int senderID, byte[] data) { + super(AK_CLEAR,senderID); + target = Handy.decodeInt(data,0); + } + */ + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(target); + } + + public void read(InputBuffer in) { + super.read(in); + target = in.readInt(); + } + + public int getTarget() { + return target; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKConnect.java b/modules/oldsims/rescuecore/commands/AKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..94cf4e2b2e62b45817579930f6e9be82b17c9115 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKConnect.java @@ -0,0 +1,70 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKConnect extends Command { + private int version; + private int[] agentTypes; + private int requestID; + private String name; + + public AKConnect(int version, int requestID, String name, int... types) { + super(RescueConstants.AK_CONNECT); + this.agentTypes = types; + this.version = version; + this.requestID = requestID; + } + + public AKConnect(InputBuffer in) { + super(RescueConstants.AK_CONNECT); + read(in); + } + + public void write(OutputBuffer out) { + out.writeInt(requestID); + out.writeInt(version); + out.writeString(name); + out.writeInt(agentTypes.length); + for (int next : agentTypes) { + out.writeInt(next); + } + } + + public void read(InputBuffer in) { + requestID = in.readInt(); + version = in.readInt(); + name = in.readString(); + agentTypes = new int[in.readInt()]; + for (int i = 0; i < agentTypes.length; ++i) { + agentTypes[i] = in.readInt(); + } + } + public int getVersion() { + return version; + } + + public int[] getAgentTypes() { + return agentTypes; + } + + public int getRequestID() { + return requestID; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKExtinguish.java b/modules/oldsims/rescuecore/commands/AKExtinguish.java new file mode 100644 index 0000000000000000000000000000000000000000..741386155aa2e75d2ea6e199f4b0f2dd35533a40 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKExtinguish.java @@ -0,0 +1,79 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import java.util.List; +import java.util.ArrayList; + +public class AKExtinguish extends AgentCommand { + private Nozzle[] nozzles; + + public AKExtinguish(int senderID,int time, int targetID, int direction, int x, int y, int water) { + super(RescueConstants.AK_EXTINGUISH,senderID,time); + nozzles = new Nozzle[1]; + nozzles[0] = new Nozzle(targetID,direction,x,y,water); + } + + public AKExtinguish(int senderID, int time, Nozzle[] nozzles) { + super(RescueConstants.AK_EXTINGUISH,senderID,time); + this.nozzles = nozzles; + } + + public AKExtinguish(InputBuffer in) { + super(RescueConstants.AK_EXTINGUISH,0,0); + read(in); + } + + /* + public AKExtinguish(int senderID, byte[] data) { + super(AK_EXTINGUISH,senderID); + readNozzles(data,0); + } + */ + + public void read(InputBuffer in) { + super.read(in); + List allNozzles = new ArrayList(); + int target = 0; + do { + target = in.readInt(); + if (target!=0) { + allNozzles.add(new Nozzle(target,in.readInt(),in.readInt(),in.readInt(),in.readInt())); + } + } while (target!=0); + nozzles = new Nozzle[allNozzles.size()]; + allNozzles.toArray(nozzles); + } + + public void write(OutputBuffer out) { + super.write(out); + for (int i=0;i<nozzles.length;++i) { + out.writeInt(nozzles[i].getTarget()); + out.writeInt(nozzles[i].getDirection()); + out.writeInt(nozzles[i].getX()); + out.writeInt(nozzles[i].getY()); + out.writeInt(nozzles[i].getWater()); + } + out.writeInt(0); + } + + public Nozzle[] getNozzles() { + return nozzles; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKLoad.java b/modules/oldsims/rescuecore/commands/AKLoad.java new file mode 100644 index 0000000000000000000000000000000000000000..8d34d71a195105dc545b6bd3e85bdb0fe7208faf --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKLoad.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKLoad extends AgentCommand { + private int target; + + public AKLoad(int senderID, int time, int target) { + super(RescueConstants.AK_LOAD,senderID,time); + this.target = target; + } + + public AKLoad(InputBuffer in) { + super(RescueConstants.AK_LOAD,0,0); + read(in); + } + + /* + public AKLoad(int senderID, byte[] data) { + super(AK_LOAD,senderID); + target = Handy.decodeInt(data,0); + } + */ + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(target); + } + + public void read(InputBuffer in) { + super.read(in); + target = in.readInt(); + } + + public int getTarget() { + return target; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKMove.java b/modules/oldsims/rescuecore/commands/AKMove.java new file mode 100644 index 0000000000000000000000000000000000000000..be6f7a83a75ebf68c9be15d556721763298e7229 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKMove.java @@ -0,0 +1,60 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import rescuecore.Handy; + +public class AKMove extends AgentCommand { + private int[] path; + + public AKMove(int senderID, int time, int[] path) { + super(RescueConstants.AK_MOVE,senderID, time); + this.path = new int[path.length]; + System.arraycopy(path,0,this.path,0,path.length); + } + + public AKMove(InputBuffer in) { + super(RescueConstants.AK_MOVE,0,0); + read(in); + } + + public void read(InputBuffer in) { + super.read(in); + path = new int[in.readInt()]; + // System.out.println("Reading AK_MOVE of length "+path.length); + for (int i=0;i<path.length;++i) { + path[i] = in.readInt(); + // System.out.println(path[i]); + } + } + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(path.length); + for (int i=0;i<path.length;++i) out.writeInt(path[i]); + } + + public int[] getPath() { + return path; + } + + public String toString() { + return "AK_MOVE ("+Handy.arrayAsString(path)+")"; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKRescue.java b/modules/oldsims/rescuecore/commands/AKRescue.java new file mode 100644 index 0000000000000000000000000000000000000000..ec39ea899d9c1f2c7285d67342bf416a98b81d51 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKRescue.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKRescue extends AgentCommand { + private int target; + + public AKRescue(int senderID, int time, int target) { + super(RescueConstants.AK_RESCUE,senderID,time); + this.target = target; + } + + public AKRescue(InputBuffer in) { + super(RescueConstants.AK_RESCUE,0,0); + read(in); + } + + /* + public AKRescue(int senderID, byte[] data) { + super(AK_RESCUE,senderID); + target = Handy.decodeInt(data,0); + } + */ + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(target); + } + + public void read(InputBuffer in) { + super.read(in); + target = in.readInt(); + } + + public int getTarget() { + return target; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKRest.java b/modules/oldsims/rescuecore/commands/AKRest.java new file mode 100644 index 0000000000000000000000000000000000000000..3ef3f4aec4bce37465f2b65e16190aff9d351e5a --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKRest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKRest extends AgentCommand { + public AKRest(int senderID, int time) { + super(RescueConstants.AK_REST,senderID,time); + } + + public AKRest(InputBuffer in) { + super(RescueConstants.AK_REST,0,0); + read(in); + } + + public void write(OutputBuffer out) { + super.write(out); + } + + public void read(InputBuffer in) { + super.read(in); + } +} diff --git a/modules/oldsims/rescuecore/commands/AKSay.java b/modules/oldsims/rescuecore/commands/AKSay.java new file mode 100644 index 0000000000000000000000000000000000000000..afcf638b2298bd1faafdfd174c439d84478d26ee --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKSay.java @@ -0,0 +1,59 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKSay extends AgentCommand { + private byte[] msg; + + public AKSay(int senderID, int time, byte[] msg, int length) { + super(RescueConstants.AK_SAY,senderID,time); + this.msg = new byte[length]; + System.arraycopy(msg,0,this.msg,0,length); + } + + public AKSay(InputBuffer in) { + super(RescueConstants.AK_SAY,0,0); + read(in); + } + + /* + public AKSay(int senderID, byte[] data) { + super(AK_SAY,senderID); + int length = Handy.decodeInt(data,0); + msg = Handy.decodeBytes(data,INT_SIZE,length); + } + */ + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(msg.length); + out.writeBytes(msg); + } + + public void read(InputBuffer in) { + super.read(in); + msg = new byte[in.readInt()]; + in.readBytes(msg); + } + + public byte[] getMessage() { + return msg; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKTell.java b/modules/oldsims/rescuecore/commands/AKTell.java new file mode 100644 index 0000000000000000000000000000000000000000..d563e8081c6ac738ff690e51e2525dd2cbe901f1 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKTell.java @@ -0,0 +1,59 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKTell extends AgentCommand { + private byte[] msg; + private byte channel; + + public AKTell(int senderID, int time, byte[] msg, int length, byte channel) { + super(RescueConstants.AK_TELL,senderID,time); + this.msg = new byte[length]; + System.arraycopy(msg,0,this.msg,0,length); + this.channel = channel; + } + + public AKTell(InputBuffer in) { + super(RescueConstants.AK_TELL,0,0); + read(in); + } + + public void write(OutputBuffer out) { + super.write(out); + out.writeInt(channel); + out.writeInt(msg.length); + out.writeBytes(msg); + } + + public void read(InputBuffer in) { + super.read(in); + channel = (byte)in.readInt(); + msg = new byte[in.readInt()]; + in.readBytes(msg); + } + + public byte[] getMessage() { + return msg; + } + + public byte getChannel() { + return channel; + } +} diff --git a/modules/oldsims/rescuecore/commands/AKUnload.java b/modules/oldsims/rescuecore/commands/AKUnload.java new file mode 100644 index 0000000000000000000000000000000000000000..f2409d82f53265a9500151d9cf27870455addaf5 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AKUnload.java @@ -0,0 +1,31 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class AKUnload extends AgentCommand { + public AKUnload(int senderID, int time) { + super(RescueConstants.AK_UNLOAD,senderID,time); + } + + public AKUnload(InputBuffer in) { + super(RescueConstants.AK_UNLOAD,0,0); + read(in); + } +} diff --git a/modules/oldsims/rescuecore/commands/AgentCommand.java b/modules/oldsims/rescuecore/commands/AgentCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..9ea1e1a031ca202e334e1f9a9414aa91a4d41625 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/AgentCommand.java @@ -0,0 +1,62 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.Handy; +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; + +/** + This class encapsulates a command. + @see RescueConstants#COMMAND_MOVE + @see RescueConstants#COMMAND_EXTINGUISH + @see RescueConstants#COMMAND_CLEAR + @see RescueConstants#COMMAND_LOAD + @see RescueConstants#COMMAND_UNLOAD + @see RescueConstants#COMMAND_RESCUE + */ +public abstract class AgentCommand extends Command { + protected int senderID; + protected int time; + + protected AgentCommand(int type, int senderID, int time) { + super(type); + this.senderID = senderID; + this.time = time; + } + + public final int getSender() { + return senderID; + } + + public final int getTime() { + return time; + } + + public void write(OutputBuffer out) { + out.writeInt(senderID); + out.writeInt(time); + } + + public void read(InputBuffer in) { + senderID = in.readInt(); + time = in.readInt(); + } + + public String toString() { + return Handy.getCommandTypeName(type)+" from agent "+senderID+" at time " + time; + } +} diff --git a/modules/oldsims/rescuecore/commands/Command.java b/modules/oldsims/rescuecore/commands/Command.java new file mode 100644 index 0000000000000000000000000000000000000000..deb3f016ccdac32e4097dd5fd90f017e3969bc7c --- /dev/null +++ b/modules/oldsims/rescuecore/commands/Command.java @@ -0,0 +1,195 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.RescueConstants; +import rescuecore.Handy; +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; + +/** + This class encapsulates a command. + @see RescueConstants#COMMAND_MOVE + @see RescueConstants#COMMAND_EXTINGUISH + @see RescueConstants#COMMAND_CLEAR + @see RescueConstants#COMMAND_LOAD + @see RescueConstants#COMMAND_UNLOAD + @see RescueConstants#COMMAND_RESCUE + */ +public abstract class Command implements java.io.Serializable { + protected int type; + + /** + Construct a new Command from a byte array + @param type The type of this command + @see RescueConstants#COMMAND_MOVE + @see RescueConstants#COMMAND_EXTINGUISH + @see RescueConstants#COMMAND_CLEAR + @see RescueConstants#COMMAND_LOAD + @see RescueConstants#COMMAND_UNLOAD + @see RescueConstants#COMMAND_RESCUE + */ + protected Command(int type) { + this.type = type; + } + + /** + Get the type of this command + @return This command's type + @see RescueConstants#COMMAND_MOVE + @see RescueConstants#COMMAND_EXTINGUISH + @see RescueConstants#COMMAND_CLEAR + @see RescueConstants#COMMAND_LOAD + @see RescueConstants#COMMAND_UNLOAD + @see RescueConstants#COMMAND_RESCUE + */ + public final int getType() { + return type; + } + + /** + Write this command to an OutputBuffer + @param The OutputBuffer to write to + */ + public abstract void write(OutputBuffer out); + + /** + Read command data from an InputBuffer + @param The InputBuffer to read from + */ + public abstract void read(InputBuffer in); + + public String toString() { + return Handy.getCommandTypeName(type); + } + + /** + Create an EXTINGUISH command that will use the maximum extinguish power and ignore the direction,x and y parameters + @param agentID The id of the agent sending the extinguish command + @param time The time of the command. + @param targetID The id of the burning building + @return A filled-in EXTINGUISH command + */ + public static Command EXTINGUISH(int agentID, int time, int targetID) { + return EXTINGUISH(agentID,time,targetID,0,0,0,RescueConstants.MAX_EXTINGUISH_POWER); + } + + /** + Create an EXTINGUISH command that will use the maximum extinguish power + @param agentID The id of the agent sending the extinguish command + @param time The time of the command. + @param targetID The id of the burning building + @param direction The direction from the nozzle to the building + @param x The x coordinate of the nozzle + @param y The y coordinate of the nozzle + @return A filled-in EXTINGUISH command + */ + public static Command EXTINGUISH(int agentID, int time, int targetID, int direction, int x, int y) { + return EXTINGUISH(agentID,time,targetID,direction,x,y,RescueConstants.MAX_EXTINGUISH_POWER); + } + + /** + Create an EXTINGUISH command + @param agentID The id of the agent sending the extinguish command + @param time The time of the command. + @param targetID The id of the burning building + @param direction The direction from the nozzle to the building + @param x The x coordinate of the nozzle + @param y The y coordinate of the nozzle + @param water The amount of water to extinguish with, in 1/1000 m^3/min + @return A filled-in EXTINGUISH command + */ + public static Command EXTINGUISH(int agentID, int time, int targetID, int direction, int x, int y, int water) { + return new AKExtinguish(agentID, time, targetID, direction, x, y, water); + } + + /** + Create a CLEAR command + @param id The id of the agent sending the clear command + @param time The time of the command. + @param target The id of the road to be cleared + @return A filled-in CLEAR command + */ + public static Command CLEAR(int id, int time, int target) { + return new AKClear(id,time,target); + } + + /** + Create a RESCUE command + @param id The id of the agent sending the rescue command + @param time The time of the command. + @param target The id of the civilian to be rescued + @return A filled-in RESCUE command + */ + public static Command RESCUE(int id, int time, int target) { + return new AKRescue(id,time,target); + } + + /** + Create a LOAD command + @param id The id of the agent sending the load command + @param time The time of the command. + @param target The id of the civilian to be loaded + @return A filled-in LOAD command + */ + public static Command LOAD(int id, int time, int target) { + return new AKLoad(id,time,target); + } + + /** + Create an UNLOAD command + @param id The id of the agent sending the unload command + @param time The time of the command. + @return A filled-in UNLOAD command + */ + public static Command UNLOAD(int id, int time) { + return new AKUnload(id,time); + } + + /** + Create a MOVE command + @param id The id of the agent sending the move command + @param time The time of the command. + @param path A list of ids (nodes, roads, rivers, rivernodes, buildings) the describe the path + @return A filled-in MOVE command + */ + public static Command MOVE(int id, int time, int[] path) { + return new AKMove(id,time,path); + } + + /** + Create a SAY command + @param id The id of the agent saying something + @param time The time of the command. + @param msg The message + @return A filled-in SAY command + */ + public static Command SAY(int id, int time, byte[] msg, int length) { + return new AKSay(id,time,msg,length); + } + + /** + Create an TELL command + @param id The id of the agent saying something + @param time The time of the command. + @param msg The message + @param channel The channel to use + @return A filled-in TELL command + */ + public static Command TELL(int id, int time, byte[] msg, int length, byte channel) { + return new AKTell(id,time,msg,length,channel); + } +} diff --git a/modules/oldsims/rescuecore/commands/Commands.java b/modules/oldsims/rescuecore/commands/Commands.java new file mode 100644 index 0000000000000000000000000000000000000000..cc2613a257515c694c7e5c75e0a8d960ed4302a6 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/Commands.java @@ -0,0 +1,62 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import java.util.List; +import java.util.ArrayList; + +public class Commands extends Command { + private int time; + private AgentCommand[] commands; + + public Commands(int time, AgentCommand[] commands) { + super(RescueConstants.COMMANDS); + this.time = time; + this.commands = commands; + } + + public Commands(InputBuffer in) { + super(RescueConstants.COMMANDS); + read(in); + } + + public void read(InputBuffer in) { + time = in.readInt(); + Command[] all = in.readCommands(); + List allCommands = new ArrayList(); + for (Command next : all) { + if (next instanceof AgentCommand) allCommands.add(next); + } + commands = new AgentCommand[allCommands.size()]; + allCommands.toArray(commands); + } + + public void write(OutputBuffer out) { + out.writeInt(time); + out.writeCommands(commands); + } + + public int getTime() { + return time; + } + + public AgentCommand[] getCommands() { + return commands; + } +} diff --git a/modules/oldsims/rescuecore/commands/KAConnectError.java b/modules/oldsims/rescuecore/commands/KAConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..ea28b09499d14d2c0f51e31fcf7721a0d647e8aa --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KAConnectError.java @@ -0,0 +1,54 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class KAConnectError extends Command { + private int requestID; + private String reason; + + public KAConnectError(int requestID, String reason) { + super(RescueConstants.KA_CONNECT_ERROR); + this.requestID = requestID; + this.reason = reason; + } + + public KAConnectError(InputBuffer in) { + super(RescueConstants.KA_CONNECT_ERROR); + read(in); + } + + public void read(InputBuffer in) { + requestID = in.readInt(); + reason = in.readString(); + } + + public void write(OutputBuffer out) { + out.writeInt(requestID); + out.writeString(reason); + } + + public int getRequestID() { + return requestID; + } + + public String getReason() { + return reason; + } +} diff --git a/modules/oldsims/rescuecore/commands/KAConnectOK.java b/modules/oldsims/rescuecore/commands/KAConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..97dd990b8ccffb3d07c71a254e84a5159e81fb2f --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KAConnectOK.java @@ -0,0 +1,63 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +public class KAConnectOK extends Command { + private RescueObject[] knowledge; + private int requestID; + private int agentID; + + public KAConnectOK(int requestID, int agentID, RescueObject[] knowledge) { + super(RescueConstants.KA_CONNECT_OK); + this.knowledge = knowledge; + this.requestID = requestID; + this.agentID = agentID; + } + + public KAConnectOK(InputBuffer in) { + super(RescueConstants.KA_CONNECT_OK); + read(in); + } + + public void read(InputBuffer in) { + requestID = in.readInt(); + agentID = in.readInt(); + knowledge = in.readObjects(0,RescueConstants.SOURCE_INITIAL); + } + + public void write(OutputBuffer out) { + out.writeInt(requestID); + out.writeInt(agentID); + out.writeObjects(knowledge); + } + + public int getRequestID() { + return requestID; + } + + public int getAgentID() { + return agentID; + } + + public RescueObject[] getKnowledge() { + return knowledge; + } +} diff --git a/modules/oldsims/rescuecore/commands/KAHear.java b/modules/oldsims/rescuecore/commands/KAHear.java new file mode 100644 index 0000000000000000000000000000000000000000..65228c230173240941cd7caebbece0175f063d17 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KAHear.java @@ -0,0 +1,84 @@ +/* + * Last change: $Date: 2004/08/03 03:25:04 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class KAHear extends Command { + private int toID; + private int fromID; + private int length; + private byte[] msg; + private byte channel; + + public KAHear(int to, int from, int length, byte[] data, byte channel) { + super(RescueConstants.KA_HEAR); + toID = to; + fromID = from; + this.length = length; + msg = new byte[length]; + System.arraycopy(data,0,msg,0,length); + this.channel = channel; + } + + public KAHear(InputBuffer in) { + super(RescueConstants.KA_HEAR); + read(in); + } + + public void read(InputBuffer in) { + toID = in.readInt(); + fromID = in.readInt(); + channel = (byte)in.readInt(); + length = in.readInt(); + msg = new byte[length]; + in.readBytes(msg); + } + + public void write(OutputBuffer out) { + out.writeInt(toID); + out.writeInt(fromID); + out.writeInt(channel); + out.writeInt(length); + out.writeBytes(msg); + } + + public int getToID() { + return toID; + } + + public int getFromID() { + return fromID; + } + + public int getLength() { + return length; + } + + public byte[] getData() { + return msg; + } + + public byte getChannel() { + return channel; + } + + public String toString() { + return super.toString()+" from "+fromID+" to "+toID+": "+length+" bytes on channel "+channel; + } +} diff --git a/modules/oldsims/rescuecore/commands/KASense.java b/modules/oldsims/rescuecore/commands/KASense.java new file mode 100644 index 0000000000000000000000000000000000000000..2ee6ff006aeff1a94275085e5fe7dfd303231ff0 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KASense.java @@ -0,0 +1,63 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +public class KASense extends Command { + private int id; + private int time; + private RescueObject[] updates; + + public KASense(int id, int time, RescueObject[] updates) { + super(RescueConstants.KA_SENSE); + this.id = id; + this.time = time; + this.updates = updates; + } + + public KASense(InputBuffer in) { + super(RescueConstants.KA_SENSE); + read(in); + } + + public void read(InputBuffer in) { + id = in.readInt(); + time = in.readInt(); + updates = in.readObjects(time,RescueConstants.SOURCE_SENSE); + } + + public void write(OutputBuffer out) { + out.writeInt(id); + out.writeInt(time); + out.writeObjects(updates); + } + + public int getID() { + return id; + } + + public int getTime() { + return time; + } + + public RescueObject[] getUpdatedObjects() { + return updates; + } +} diff --git a/modules/oldsims/rescuecore/commands/KSConnectError.java b/modules/oldsims/rescuecore/commands/KSConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..b70be5f524debf6620522b1d6248a8bbc4622948 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KSConnectError.java @@ -0,0 +1,46 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class KSConnectError extends Command { + private String reason; + + public KSConnectError(String reason) { + super(RescueConstants.KS_CONNECT_ERROR); + this.reason = reason; + } + + public KSConnectError(InputBuffer in) { + super(RescueConstants.KS_CONNECT_ERROR); + read(in); + } + + public void read(InputBuffer in) { + reason = in.readString(); + } + + public void write(OutputBuffer out) { + out.writeString(reason); + } + + public String getReason() { + return reason; + } +} diff --git a/modules/oldsims/rescuecore/commands/KSConnectOK.java b/modules/oldsims/rescuecore/commands/KSConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..5ffedce6dfb7bba4a8d6c99d5dcc1945737f2d0d --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KSConnectOK.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import rescuecore.RescueObject; + +public class KSConnectOK extends Command { + private int id; + private RescueObject[] knowledge; + + public KSConnectOK(int id, RescueObject[] knowledge) { + super(RescueConstants.KS_CONNECT_OK); + this.knowledge = knowledge; + this.id = id; + } + + public KSConnectOK(InputBuffer in) { + super(RescueConstants.KS_CONNECT_OK); + read(in); + } + + public void read(InputBuffer in) { + id = in.readInt(); + knowledge = in.readObjects(0,null); + } + + public void write(OutputBuffer out) { + out.writeInt(id); + out.writeObjects(knowledge); + } + + public int getSimulatorID() { + return id; + } + + public RescueObject[] getKnowledge() { + return knowledge; + } +} diff --git a/modules/oldsims/rescuecore/commands/KVConnectError.java b/modules/oldsims/rescuecore/commands/KVConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..5630a2b388e0fd54cfb2c6efcc0d80b31aaa50e4 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KVConnectError.java @@ -0,0 +1,46 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class KVConnectError extends Command { + private String reason; + + public KVConnectError(String reason) { + super(RescueConstants.KV_CONNECT_ERROR); + this.reason = reason; + } + + public KVConnectError(InputBuffer in) { + super(RescueConstants.KV_CONNECT_ERROR); + read(in); + } + + public void read(InputBuffer in) { + reason = in.readString(); + } + + public void write(OutputBuffer out) { + out.writeString(reason); + } + + public String getReason() { + return reason; + } +} diff --git a/modules/oldsims/rescuecore/commands/KVConnectOK.java b/modules/oldsims/rescuecore/commands/KVConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..50e646290e054f3c4b0d13e8f8eb07bbbb407af1 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/KVConnectOK.java @@ -0,0 +1,47 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import rescuecore.RescueObject; + +public class KVConnectOK extends Command { + private RescueObject[] knowledge; + + public KVConnectOK(RescueObject[] knowledge) { + super(RescueConstants.KV_CONNECT_OK); + this.knowledge = knowledge; + } + + public KVConnectOK(InputBuffer in) { + super(RescueConstants.KV_CONNECT_OK); + read(in); + } + + public void read(InputBuffer in) { + knowledge = in.readObjects(0,null); + } + + public void write(OutputBuffer out) { + out.writeObjects(knowledge); + } + + public RescueObject[] getKnowledge() { + return knowledge; + } +} diff --git a/modules/oldsims/rescuecore/commands/Nozzle.java b/modules/oldsims/rescuecore/commands/Nozzle.java new file mode 100644 index 0000000000000000000000000000000000000000..94fbaed904848ca9a121177125eb4f7c9e52a08a --- /dev/null +++ b/modules/oldsims/rescuecore/commands/Nozzle.java @@ -0,0 +1,34 @@ +/* + * Last change: $Date: 2004/05/27 03:41:02 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +public class Nozzle implements java.io.Serializable { + private int target, direction, x, y, water; + + public Nozzle(int target, int direction, int x, int y, int water) { + this.target = target; + this.direction = direction; + this.x = x; + this.y = y; + this.water = water; + } + + public int getTarget() {return target;} + public int getDirection() {return direction;} + public int getX() {return x;} + public int getY() {return y;} + public int getWater() {return water;} +} diff --git a/modules/oldsims/rescuecore/commands/SKAcknowledge.java b/modules/oldsims/rescuecore/commands/SKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..720a2bfb3a47156c6556a58b77e539504ecf2c19 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/SKAcknowledge.java @@ -0,0 +1,42 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class SKAcknowledge extends Command { + private int id; + + public SKAcknowledge(int id) { + super(RescueConstants.SK_ACKNOWLEDGE); + this.id = id; + } + + public SKAcknowledge(InputBuffer in) { + super(RescueConstants.SK_ACKNOWLEDGE); + read(in); + } + + public void read(InputBuffer in) { + id = in.readInt(); + } + + public void write(OutputBuffer out) { + out.writeInt(id); + } +} diff --git a/modules/oldsims/rescuecore/commands/SKConnect.java b/modules/oldsims/rescuecore/commands/SKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..fbc08082617cffc4987dd9e5514bb6637d855cab --- /dev/null +++ b/modules/oldsims/rescuecore/commands/SKConnect.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +public class SKConnect extends Command { + private int version; + private String name; + + public SKConnect(String name) { + this(0, name); + } + + public SKConnect(int version, String name) { + super(RescueConstants.SK_CONNECT); + this.version = version; + this.name = name; + } + + public SKConnect(InputBuffer in) { + super(RescueConstants.SK_CONNECT); + read(in); + } + + public void read(InputBuffer in) { + version = in.readInt(); + name = in.readString(); + } + + public void write(OutputBuffer out) { + out.writeInt(version); + out.writeString(name); + } + + public int getVersion() { + return version; + } +} diff --git a/modules/oldsims/rescuecore/commands/SKUpdate.java b/modules/oldsims/rescuecore/commands/SKUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..83bdebf8276d37cbc258d9be9c7ca5f2b6741f35 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/SKUpdate.java @@ -0,0 +1,63 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import rescuecore.RescueObject; + +public class SKUpdate extends Command { + private int id; + private int time; + private RescueObject[] changed; + + public SKUpdate(int id, int time, RescueObject[] changed) { + super(RescueConstants.SK_UPDATE); + this.id = id; + this.time = time; + this.changed = changed; + } + + public SKUpdate(InputBuffer in) { + super(RescueConstants.SK_UPDATE); + read(in); + } + + public void read(InputBuffer in) { + id = in.readInt(); + time = in.readInt(); + changed = in.readObjects(time,null); + } + + public void write(OutputBuffer out) { + out.writeInt(id); + out.writeInt(time); + out.writeObjects(changed); + } + + public RescueObject[] getChangedObjects() { + return changed; + } + + public int getSimulatorID() { + return id; + } + + public int getTime() { + return time; + } +} diff --git a/modules/oldsims/rescuecore/commands/Update.java b/modules/oldsims/rescuecore/commands/Update.java new file mode 100644 index 0000000000000000000000000000000000000000..393a3feebe5c31182f4f1e77505254427a42b808 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/Update.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; +import rescuecore.RescueObject; + +public class Update extends Command { + private int time; + private RescueObject[] changed; + + public Update(int time, RescueObject[] changed) { + super(RescueConstants.UPDATE); + this.time = time; + this.changed = changed; + } + + public Update(InputBuffer in) { + super(RescueConstants.UPDATE); + read(in); + } + + public void read(InputBuffer in) { + time = in.readInt(); + changed = in.readObjects(time,RescueConstants.SOURCE_UPDATE); + } + + public void write(OutputBuffer out) { + out.writeInt(time); + out.writeObjects(changed); + } + + public int getTime() { + return time; + } + + public RescueObject[] getUpdatedObjects() { + return changed; + } +} diff --git a/modules/oldsims/rescuecore/commands/VKAcknowledge.java b/modules/oldsims/rescuecore/commands/VKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..d1a5eb6c7de48793147c2935d2fe896ca7c1f39b --- /dev/null +++ b/modules/oldsims/rescuecore/commands/VKAcknowledge.java @@ -0,0 +1,36 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueConstants; + +public class VKAcknowledge extends Command { + public VKAcknowledge() { + super(RescueConstants.VK_ACKNOWLEDGE); + } + + public VKAcknowledge(InputBuffer in) { + super(RescueConstants.VK_ACKNOWLEDGE); + } + + public void read(InputBuffer in) { + } + + public void write(OutputBuffer out) { + } +} diff --git a/modules/oldsims/rescuecore/commands/VKConnect.java b/modules/oldsims/rescuecore/commands/VKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..02180cb2d00af62b6e4ae44787d4470d636b1129 --- /dev/null +++ b/modules/oldsims/rescuecore/commands/VKConnect.java @@ -0,0 +1,55 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.commands; + +import rescuecore.InputBuffer; +import rescuecore.OutputBuffer; +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +public class VKConnect extends Command { + private int version; + private String name; + + public VKConnect(String name) { + this(0, name); + } + + public VKConnect(int version, String name) { + super(RescueConstants.VK_CONNECT); + this.version = version; + this.name = name; + } + + public VKConnect(InputBuffer in) { + super(RescueConstants.VK_CONNECT); + read(in); + } + + public void read(InputBuffer in) { + version = in.readInt(); + name = in.readString(); + } + + public void write(OutputBuffer out) { + out.writeInt(version); + out.writeString(name); + } + + public int getVersion() { + return version; + } +} diff --git a/modules/oldsims/rescuecore/config/Config.java b/modules/oldsims/rescuecore/config/Config.java new file mode 100644 index 0000000000000000000000000000000000000000..a29d084a1885d676327bb7c6dbf1fc73b1aff260 --- /dev/null +++ b/modules/oldsims/rescuecore/config/Config.java @@ -0,0 +1,324 @@ +package rescuecore.config; + +import java.io.IOException; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.File; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; +import java.util.Set; + +/** + This class represents a config file and any other config files that might have been included with a !include directive. Config files must be defined relative to a base directory so that includes can be resolved. + */ +public class Config { + private static final String INCLUDE = "!include"; + + /** + The raw data and caches of int/float/boolean interpretations. + */ + private Map<String, String> data; + private Map<String, Integer> intData; + private Map<String, Double> floatData; + private Map<String, Boolean> booleanData; + + /** + Create an empty config. + */ + public Config() { + data = new HashMap<String, String>(); + intData = new HashMap<String, Integer>(); + floatData = new HashMap<String, Double>(); + booleanData = new HashMap<String, Boolean>(); + } + + /** + Create a config that reads from a given file. Additional config files can be read later with the {@link read(String)} method. + @param file The config file to read. Must not be null. + @throws IOException If there is an error reading the file. + @throws ConfigException If there is an error parsing the config file or one of its descendants. + */ + public Config(File file) throws IOException, ConfigException { + this(); + read(file); + } + + /** + Read a config file and add its contents. Existing entries with the same name will be overwritten. + @param file The config file to read. Must not be null. If this is a directory then all files in the directory will be read. + @throws IOException If there is an error reading the file. + @throws ConfigException If there is an error parsing the config file or one of its descendants. + */ + public void read(File file) throws IOException, ConfigException { + if (file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + if (!file.exists()) { + throw new IllegalArgumentException("File " + file.getAbsolutePath() + " does not exist"); + } + if (file.isDirectory()) { + for (File next : file.listFiles()) { + read(next); + } + } + else { + readConfigFile(file); + } + } + + /** + Read config information from a Reader and add its contents. Existing entries with the same name will be overwritten. + @param in The Reader to read from. Must not be null. + @throws IOException If there is an error reading the file. + @throws ConfigException If there is an error parsing the config file or one of its descendants. + */ + private void readConfigFile(File in) throws IOException, ConfigException { + BufferedReader reader = new BufferedReader(new FileReader(in)); + String name = in.getAbsolutePath(); + String line = ""; + int lineNumber = 0; + try { + while (line != null) { + line = reader.readLine(); + ++lineNumber; + if (line != null) { + line = line.trim(); + // Ignore empty lines + if ("".equals(line)) { + continue; + } + // Ignore lines that start with # + if (line.startsWith("#")) { + continue; + } + // Look for a !include + else if (line.startsWith(INCLUDE)) { + if (INCLUDE.equals(line)) { + throw new ConfigException(name, "Line " + lineNumber + ": Empty include directive"); + } + String includeName = line.substring(INCLUDE.length() + 1).trim(); + if ("".equals(includeName)) { + throw new ConfigException(name, "Line " + lineNumber + ": Empty include directive"); + } + read(new File(in.getParentFile(), includeName)); + } + else { + int index = line.indexOf(':'); + if (index == -1) { + throw new ConfigException(name, "Line " + lineNumber + ": No ':' found"); + } + if (index == line.length() - 1) { + throw new ConfigException(name, "Line " + lineNumber + ": No value found"); + } + if (index == 0) { + throw new ConfigException(name, "Line " + lineNumber + ": No key found"); + } + String key = line.substring(0, index).trim(); + String value = line.substring(index + 1).trim(); + data.put(key, value); + intData.remove(key); + floatData.remove(key); + booleanData.remove(key); + } + } + } + } + finally { + reader.close(); + } + } + + /** + Write this config to a PrintWriter. + @param out The PrintWriter to write to. Must not be null. + @throws IOException If there is an error writing to the stream. + */ + public void write(PrintWriter out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output cannot be null"); + } + for (Map.Entry<String, String> next : data.entrySet()) { + out.print(next.getKey()); + out.print(" : "); + out.println(next.getValue()); + } + } + + /** + Get all keys in this config. + @return An immutable view of all keys. + */ + public Set<String> getAllKeys() { + return Collections.unmodifiableSet(data.keySet()); + } + + /** + Get the value of a key as a String. + @param key The key to look up. Must not be null. + @return The value associated with that key. + @throws NoSuchConfigOptionException If the key is not defined. + */ + public String getValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!data.containsKey(key)) { + throw new NoSuchConfigOptionException(key); + } + return data.get(key); + } + + /** + Get the value of a key as an integer. + @param key The key to look up. Must not be null. + @return The value associated with that key interpreted as an integer. + @throws NoSuchConfigOptionException If the key is not defined. + @throws NumberFormatException If the value of the key cannot be interpreted as an integer. + */ + public int getIntValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (intData.containsKey(key)) { + return intData.get(key); + } + int result = Integer.parseInt(getValue(key)); + intData.put(key, result); + return result; + } + + /** + Get the value of a key as a floating point number. + @param key The key to look up. Must not be null. + @return The value associated with that key interpreted as a floating point number. + @throws NoSuchConfigOptionException If the key is not defined. + @throws NumberFormatException If the value of the key cannot be interpreted as a floating point number. + */ + public double getFloatValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (floatData.containsKey(key)) { + return floatData.get(key); + } + double result = Double.parseDouble(getValue(key)); + floatData.put(key, result); + return result; + } + + /** + Get the value of a key as a boolean. "true", "t", "yes", "y" and "1" (case insensitive) are all interpreted as true, all other values are false. + @param key The key to look up. Must not be null. + @return The value associated with that key interpreted as a boolean. + @throws NoSuchConfigOptionException If the key is not defined. + */ + public boolean getBooleanValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (booleanData.containsKey(key)) { + return booleanData.get(key); + } + boolean result = false; + String value = getValue(key); + if ("true".equalsIgnoreCase(value) + || "t".equalsIgnoreCase(value) + || "yes".equalsIgnoreCase(value) + || "y".equalsIgnoreCase(value) + || "1".equalsIgnoreCase(value)) { + result = true; + } + booleanData.put(key, result); + return result; + } + + /** + Set the value of a key. + @param key The key to set. Must not be null. + @param value The new value. If this is null then {@link #removeKey(String)} is called with the given key. + */ + public void setValue(String key, String value) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (value == null) { + removeKey(key); + return; + } + data.put(key, value); + intData.remove(key); + floatData.remove(key); + booleanData.remove(key); + } + + /** + Set the value of a key as an integer. + @param key The key to set. Must not be null. + @param value The new value. + */ + public void setIntValue(String key, int value) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + data.put(key, Integer.valueOf(value).toString()); + intData.put(key, value); + floatData.remove(key); + booleanData.remove(key); + } + + /** + Set the value of a key as a floating point number. + @param key The key to set. Must not be null. + @param value The new value. + */ + public void setFloatValue(String key, double value) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + data.put(key, Double.valueOf(value).toString()); + intData.remove(key); + floatData.put(key, value); + booleanData.remove(key); + } + + /** + Set the value of a key as a boolean. + @param key The key to set. Must not be null. + @param value The new value. + */ + public void setBooleanValue(String key, boolean value) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + data.put(key, value ? "true" : "false"); + intData.remove(key); + floatData.remove(key); + booleanData.put(key, value); + } + + /** + Remove a key from the config. + @param key The key to remove. Must not be null. + */ + public void removeKey(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + data.remove(key); + intData.remove(key); + floatData.remove(key); + booleanData.remove(key); + } + + /** + Remove all keys. + */ + public void removeAllKeys() { + data.clear(); + intData.clear(); + floatData.clear(); + booleanData.clear(); + } +} diff --git a/modules/oldsims/rescuecore/config/ConfigException.java b/modules/oldsims/rescuecore/config/ConfigException.java new file mode 100644 index 0000000000000000000000000000000000000000..f8d80e3c1941eb334c054b590bdaec4ac70245ad --- /dev/null +++ b/modules/oldsims/rescuecore/config/ConfigException.java @@ -0,0 +1,42 @@ +package rescuecore.config; + +/** + Exception class for problems with config files. + */ +public class ConfigException extends Exception { + /** + Construct an exception with just a filename, no message or underlying cause. + @param filename The name of the config file that caused the problem. + */ + public ConfigException(final String filename) { + super(filename + ": unknown error"); + } + + /** + Construct an exception with a filename and error message. + @param filename The name of the config file that caused the problem. + @param msg A message describing the problem. + */ + public ConfigException(final String filename, final String msg) { + super(filename + ": " + msg); + } + + /** + Construct an exception with a filename and an underlying cause. + @param filename The name of the config file that caused the problem. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final Throwable cause) { + super(filename + ": " + cause.toString(), cause); + } + + /** + Construct an exception with a filename, error message and underlying cause. + @param filename The name of the config file that caused the problem. + @param msg A message describing the problem. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final String msg, final Throwable cause) { + super(filename + ": " + msg, cause); + } +} diff --git a/modules/oldsims/rescuecore/config/NoSuchConfigOptionException.java b/modules/oldsims/rescuecore/config/NoSuchConfigOptionException.java new file mode 100644 index 0000000000000000000000000000000000000000..2fac3822e87f97e1b50ef521b00052b9b38bb80e --- /dev/null +++ b/modules/oldsims/rescuecore/config/NoSuchConfigOptionException.java @@ -0,0 +1,14 @@ +package rescuecore.config; + +/** + An unchecked exception that is thrown when an application attempts to read an undefined config option. + */ +public class NoSuchConfigOptionException extends RuntimeException { + /** + Construct an exception. + @param key The name of the key that does not exist in the config. + */ + public NoSuchConfigOptionException(final String key) { + super(key); + } +} diff --git a/modules/oldsims/rescuecore/debug/CollectionHandler.java b/modules/oldsims/rescuecore/debug/CollectionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..0a210c41c6728a7549a50f88a3d339fdb3e73a55 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/CollectionHandler.java @@ -0,0 +1,83 @@ +package rescuecore.debug; + +import java.util.*; +import javax.swing.*; +import java.awt.*; +import rescuecore.view.*; +import rescuecore.*; + +public class CollectionHandler implements Handler { + private Collection current; + + private Layer layer; + private CollectionView component; + + public JComponent getComponent() { + if (component==null) { + component = new CollectionView(); + component.setObjects(current); + } + return component; + } + + public Layer getLayer() { + if (layer==null) { + layer = new Layer("Collection handler"); + layer.addObjects(current); + } + return layer; + } + + public boolean handle(Object o, int timeStep) { + if (o instanceof Collection) { + current = (Collection)o; + if (layer!=null) layer.setObjects(current); + if (component != null) component.setObjects(current); + return true; + } + return false; + } + + public void setMemory(Memory m) { + } + + private static class CollectionView extends JPanel { + private CollectionModel model; + + public CollectionView() { + super(new BorderLayout()); + model = new CollectionModel(); + add(new JScrollPane(new JList(model)),BorderLayout.CENTER); + } + + public void setObjects(Collection c) { + model.setObjects(c); + } + + private static class CollectionModel extends AbstractListModel { + private String[] data; + + public CollectionModel() { + data = new String[0]; + } + + public int getSize() { + return data.length; + } + + public Object getElementAt(int index) { + return data[index]; + } + + public void setObjects(Collection c) { + data = new String[c.size()]; + int i=0; + for (Iterator it = c.iterator();it.hasNext();++i) { + Object next = it.next(); + data[i] = next==null?"null":next.toString(); + } + fireContentsChanged(this,0,data.length); + } + } + } +} diff --git a/modules/oldsims/rescuecore/debug/CommandHandler.java b/modules/oldsims/rescuecore/debug/CommandHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..ff0ef1cd31ad1d1712175a3d37fea67f016785f0 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/CommandHandler.java @@ -0,0 +1,140 @@ +/* + * Last change: $Date: 2004/06/10 01:17:51 $ + * $Revision: 1.11 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.view.*; +import rescuecore.commands.*; +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; + +public class CommandHandler implements Handler { + + private Layer commandLayer; + private DefaultListModel messages; + private JScrollPane messagePane; + + private int timeStep = -1; + + private Memory memory; + + public CommandHandler(){ + JPanel messagePanel = new JPanel(new BorderLayout()); + Border bord = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK),"Commands"); + messagePanel.setBorder(bord); + messages = new DefaultListModel(); + JList messageList = new JList(messages); + messagePanel.add(messageList,BorderLayout.CENTER); + messagePane = new JScrollPane(messagePanel); + messagePane.setPreferredSize(new Dimension(DebugPane.HANDLER_WIDTH,80)); + } + + public void setMemory(Memory m){ + memory = m; + } + + + public JComponent getComponent(){ + return messagePane; + } + + public Layer getLayer(){ + if(commandLayer == null){ + commandLayer = new Layer("Move Commands"); + commandLayer.addRenderer(RescueObject[].class,OutlineRenderer.ORANGE); + commandLayer.addRenderer(Humanoid.class,HumanoidRenderer.outlinedHumanoidRenderer(ViewConstants.FILL_MODE_SOLID, new Color(255,255,255,96))); + commandLayer.addRenderer(ExtinguishCommand.class, new ExtinguishCommandRenderer()); + + } + return commandLayer; + } + + public boolean handle(Object o, int timeStep){ + if(this.timeStep != timeStep){ + commandLayer.removeAllObjects(); + messages.clear(); + this.timeStep = timeStep; + } + if(!(o instanceof Command)) + return false; + Command c = (Command)o; + if(c == null) + return true; + int id; + switch(c.getType()){ + case RescueConstants.AK_MOVE: + int[] path = ((AKMove)c).getPath(); + RescueObject[] rs = new RescueObject[path.length]; + rs[0] = memory.lookup(path[0]); + RescueObject last = null; + for (int i=0;i<rs.length;++i){ + rs[i] = memory.lookup(path[i]); + if(rs[i] == null) + messages.addElement("Bad ID in path. Index "+i+". ID "+path[i]); + else if(rs[i].isNode()){ + if(last != null) + commandLayer.addObject(new RescueObject[]{last,rs[i]}); + last = rs[i]; + } + } + break; + case RescueConstants.AK_SAY: + messages.addElement("Say of "+((AKSay)c).getMessage().length+" bytes."); + break; + case RescueConstants.AK_TELL: + messages.addElement("Tell of "+((AKTell)c).getMessage().length+" bytes."); + break; + case RescueConstants.AK_EXTINGUISH: + AKExtinguish ex = (AKExtinguish)c; + id = ex.getSender(); + Nozzle[] nozzles = ex.getNozzles(); + for (int i=0;i<nozzles.length;++i) { + int targetID = nozzles[i].getTarget(); + messages.addElement("Extinguishing building "+targetID); + try { + commandLayer.addObject(new ExtinguishCommand((Building)memory.lookup(targetID),(Humanoid)memory.lookup(id),memory)); + } + catch (CannotFindLocationException e) { + e.printStackTrace(); + } + } + break; + case RescueConstants.AK_CLEAR: + id = ((AKClear)c).getSender(); + commandLayer.addObject(memory.lookup(id)); + messages.addElement("Clearing road."); + break; + case RescueConstants.AK_RESCUE: + id = ((AKRescue)c).getSender(); + commandLayer.addObject(memory.lookup(id)); + messages.addElement("Rescuing."); + break; + case RescueConstants.AK_LOAD: + id = ((AKLoad)c).getSender(); + commandLayer.addObject(memory.lookup(id)); + messages.addElement("Loading."); + break; + case RescueConstants.AK_UNLOAD: + id = ((AKUnload)c).getSender(); + commandLayer.addObject(memory.lookup(id)); + messages.addElement("Unloading."); + break; + } + return true; + } +} diff --git a/modules/oldsims/rescuecore/debug/DebugEntry.java b/modules/oldsims/rescuecore/debug/DebugEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..3042e47ea199bc86f29768ed8e2542c8036b9ceb --- /dev/null +++ b/modules/oldsims/rescuecore/debug/DebugEntry.java @@ -0,0 +1,123 @@ +package rescuecore.debug; + +import java.io.Serializable; +import rescuecore.RescueObject; +import java.util.*; + +public abstract class DebugEntry implements Serializable { + private int time; + + protected DebugEntry(int time) { + this.time = time; + } + + public int getTimestep() { + return time; + } + + public static class RescueObjectEntry extends DebugEntry { + private RescueObject object; + + public RescueObjectEntry(RescueObject object, int time) { + super(time); + this.object = object; + } + + public RescueObject getObject() { + return object; + } + + public String toString() { + return "Object added: "+object; + } + } + + public static class RescueObjectCollectionEntry extends DebugEntry { + private Collection<RescueObject> objects; + + public RescueObjectCollectionEntry(Collection<RescueObject> objects, int time) { + super(time); + this.objects = new HashSet<RescueObject>(objects); + } + + public Collection<RescueObject> getObjects() { + return objects; + } + + public String toString() { + return "Object collection: "+objects.size()+" objects"; + } + } + + public static abstract class PropertyUpdateEntry extends DebugEntry { + protected int property; + protected int objectID; + + protected PropertyUpdateEntry(int objectID, int property, int time) { + super(time); + this.property = property; + this.objectID = objectID; + } + + public int getProperty() { + return property; + } + + public int getObjectID() { + return objectID; + } + } + + public static class IntPropertyUpdateEntry extends PropertyUpdateEntry { + private int newValue; + + public IntPropertyUpdateEntry(int id, int property, int value, int time) { + super(id,property,time); + newValue = value; + } + + public int getNewValue() { + return newValue; + } + + public String toString() { + return "Integer update: Object: "+objectID+" Property: "+property; + } + } + + public static class ArrayPropertyUpdateEntry extends PropertyUpdateEntry { + private int[] newValue; + + public ArrayPropertyUpdateEntry(int id, int property, int[] value, int time) { + super(id,property,time); + newValue = value; + } + + public int[] getNewValue() { + return newValue; + } + + public String toString() { + return "Array update: Object: "+objectID+" Property: "+property; + } + } + + public static class ObjectDebugEntry extends DebugEntry { + private final static long serialVersionUID = -7542261088352191771l; + + private Object object; + + public ObjectDebugEntry(Object object, int time) { + super(time); + this.object = object; + } + + public Object getObject() { + return object; + } + + public String toString() { + return "User object: "+object; + } + } +} diff --git a/modules/oldsims/rescuecore/debug/DebugLog.java b/modules/oldsims/rescuecore/debug/DebugLog.java new file mode 100644 index 0000000000000000000000000000000000000000..30ed4ea5f8c331a66d61756cf46a1a1fa3c0197f --- /dev/null +++ b/modules/oldsims/rescuecore/debug/DebugLog.java @@ -0,0 +1,68 @@ +package rescuecore.debug; + +import java.io.*; +import java.util.*; +import rescuecore.*; + +public class DebugLog { + private final static Collection<DebugEntry> NO_ENTRIES = Collections.emptySet(); + + private Map<String,List<List<DebugEntry>>> nameToEntries; + private int maxTimestep; + + public DebugLog(String fileName) throws IOException, ClassNotFoundException { + this(new File(fileName)); + } + + public DebugLog(File file) throws IOException, ClassNotFoundException { + nameToEntries = new HashMap<String,List<List<DebugEntry>>>(); + maxTimestep = 0; + try { + ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); + while (true) { + String name = in.readUTF(); + DebugEntry next = (DebugEntry)in.readObject(); + System.out.println(next); + addEntry(name,next); + maxTimestep = Math.max(maxTimestep,next.getTimestep()); + } + } + catch (EOFException e) { + } + } + + public Collection<String> getAllNames() { + return nameToEntries.keySet(); + } + + public int getMaxTimestep(String name) { + List<List<DebugEntry>> listOfLists = nameToEntries.get(name); + if (listOfLists==null) return -1; + return listOfLists.size()-1; + } + + public int getMaxTimestep() { + return maxTimestep; + } + + public Collection<DebugEntry> getEntriesForTime(String name, int time) { + List<List<DebugEntry>> listOfLists = nameToEntries.get(name); + if (listOfLists==null) return NO_ENTRIES; + if (time >= listOfLists.size()) return NO_ENTRIES; + List<DebugEntry> result = listOfLists.get(time); + if (result==null) return NO_ENTRIES; + return Collections.unmodifiableCollection(result); + } + + private void addEntry(String name, DebugEntry next) { + List<List<DebugEntry>> listOfLists = nameToEntries.get(name); + if (listOfLists==null) { + listOfLists = new ArrayList<List<DebugEntry>>(); + nameToEntries.put(name,listOfLists); + } + int time = next.getTimestep(); + while (time >= listOfLists.size()) listOfLists.add(new ArrayList<DebugEntry>()); + List<DebugEntry> thisList = listOfLists.get(time); + thisList.add(next); + } +} diff --git a/modules/oldsims/rescuecore/debug/DebugMemoryListener.java b/modules/oldsims/rescuecore/debug/DebugMemoryListener.java new file mode 100644 index 0000000000000000000000000000000000000000..3626286a612f52859ff043c2bee2de4c933e6a6d --- /dev/null +++ b/modules/oldsims/rescuecore/debug/DebugMemoryListener.java @@ -0,0 +1,50 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import rescuecore.*; +import rescuecore.event.*; + +public class DebugMemoryListener implements MemoryListener { + private RescueComponent component; + + public DebugMemoryListener(RescueComponent component){ + this.component = component; + } + + public void objectAdded(ObjectAddedEvent event){ + RescueObject object = event.getObject(); + int time = event.getTimestamp(); + DebugWriter.logObjectAdded(component,object,time); + } + + public void objectChanged(ObjectChangedEvent event){ + RescueObject object = event.getObject(); + int timeStamp = event.getTimestamp(); + int property = event.getProperty(); + Property p = object.getProperty(property); + DebugWriter.logObjectChanged(component,object,p,timeStamp); + /* + if (p instanceof IntProperty) { + int value = ((IntProperty)p).getValue(); + } + if (p instanceof ArrayProperty) { + int[] values = ((ArrayProperty)p).getValues(); + DebugWriter.log(component,new DebugEntry.ArrayPropertyUpdateEntry(object.getID(),property,values,timeStamp)); + } + */ + } +} diff --git a/modules/oldsims/rescuecore/debug/DebugPane.java b/modules/oldsims/rescuecore/debug/DebugPane.java new file mode 100644 index 0000000000000000000000000000000000000000..cfb9fa51ae2473fa3d25a3f744c233ade74c24d5 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/DebugPane.java @@ -0,0 +1,195 @@ +/* + * Last change: $Date: 2005/03/17 06:07:12 $ + * $Revision: 1.10 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import rescuecore.*; +import rescuecore.view.*; +import rescuecore.objects.*; +import javax.swing.*; +import java.awt.*; +import java.util.*; + +public class DebugPane extends JPanel{ + + private DebugLog log; + private String name; + private Memory memory; + protected JTabbedPane tabs; + private int timeStep; + private rescuecore.view.Map map; + private Layer moveHistory; + private ArrayList<Handler> handlers; + private JPanel handlerPanel; + public static final int HANDLER_WIDTH = 200; + int height = 0; + + public DebugPane(DebugLog log, String name){ + super(new BorderLayout()); + this.log = log; + this.name = name; + ObjectInspector inspector = new ObjectInspector(); + memory = new HashMemory(); + setName(name); + map = new rescuecore.view.Map(memory); + ObjectSelector selector = new ObjectSelector(map); + map.addMouseListener(selector); + selector.addObjectSelectionListener(inspector); + add(map,BorderLayout.CENTER); + map.addLayer(Layer.createBuildingLayer(memory)); + map.addLayer(Layer.createRoadLayer(memory)); + map.addLayer(Layer.createNodeLayer(memory)); + map.addLayer(Layer.createHumanoidLayer(memory)); + moveHistory = new Layer("Movement History"); + moveHistory.addRenderer(RescueObject[].class,OutlineRenderer.YELLOW); + map.addLayer(moveHistory); + + handlerPanel = new JPanel(); + handlerPanel.setLayout(new BoxLayout(handlerPanel,BoxLayout.Y_AXIS)); + JScrollPane sp = new JScrollPane(inspector,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + sp.setPreferredSize(new Dimension(HANDLER_WIDTH,200)); + + handlerPanel.add(sp); + add(new JScrollPane(handlerPanel,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS),BorderLayout.EAST); + + handlers = new ArrayList<Handler>(10); + } + + public void registerHandler(Handler h){ + h.setMemory(memory); + handlers.add(h); + JComponent comp = h.getComponent(); + if(comp != null){ + handlerPanel.add(comp); + height += (int)comp.getPreferredSize().getHeight(); + handlerPanel.setPreferredSize(new Dimension(HANDLER_WIDTH+10,height)); + } + Layer lay = h.getLayer(); + if(lay != null) + map.addLayer(lay); + } + + protected void moveToTimeStep(int timeStep){ + memory = new HashMemory(); + for (int i=0;i<=timeStep;++i) { + Collection<DebugEntry> entries = log.getEntriesForTime(name,i); + for (DebugEntry next : entries) { + processEntry(next); + } + } + map.setMemory(memory); + for (Handler next : handlers) next.setMemory(memory); + repaint(); + + /* + if(timeStep < this.timeStep) + for(int i = this.timeStep; i > timeStep; i--) + log.invertAll(i,memory); + else if(timeStep > this.timeStep) + for(int i = this.timeStep+1; i <= timeStep; i++) + log.applyAll(i,memory); + else{ + repaint(); + return; + } + this.timeStep = timeStep; + setHistory(); + repaint(); + */ + } + + private void processEntry(DebugEntry entry) { + // FIXME: Nasty nasty nasty! This sort of thing should really be in class DebugEntry. + if (entry instanceof DebugEntry.RescueObjectEntry) { + RescueObject object = ((DebugEntry.RescueObjectEntry)entry).getObject(); + memory.add(object.copy(),entry.getTimestep()); + } + else if (entry instanceof DebugEntry.RescueObjectCollectionEntry) { + Collection<RescueObject> objects = ((DebugEntry.RescueObjectCollectionEntry)entry).getObjects(); + for (RescueObject next : objects) { + memory.add(next.copy(),entry.getTimestep()); + } + } + else if (entry instanceof DebugEntry.IntPropertyUpdateEntry) { + int property = ((DebugEntry.IntPropertyUpdateEntry)entry).getProperty(); + int value = ((DebugEntry.IntPropertyUpdateEntry)entry).getNewValue(); + int id = ((DebugEntry.IntPropertyUpdateEntry)entry).getObjectID(); + RescueObject o = memory.lookup(id); + if (o!=null) { + Property p = o.getProperty(property); + if (p instanceof IntProperty) { + ((IntProperty)p).setValue(value,entry.getTimestep(),null); + } + } + } + else if (entry instanceof DebugEntry.ArrayPropertyUpdateEntry) { + int property = ((DebugEntry.ArrayPropertyUpdateEntry)entry).getProperty(); + int[] value = ((DebugEntry.ArrayPropertyUpdateEntry)entry).getNewValue(); + int id = ((DebugEntry.ArrayPropertyUpdateEntry)entry).getObjectID(); + RescueObject o = memory.lookup(id); + if (o!=null) { + Property p = o.getProperty(property); + if (p instanceof ArrayProperty) { + ((ArrayProperty)p).setValues(value,entry.getTimestep(),null); + } + } + } + else if (entry instanceof DebugEntry.ObjectDebugEntry) { + Object obj = ((DebugEntry.ObjectDebugEntry)entry).getObject(); + for(int j = handlers.size()-1; j >= 0; j--){ + Handler h = (Handler)handlers.get(j); + if(h.handle(obj,entry.getTimestep())) + break; + } + } + } + + /* + protected RescueObject getAgent(){ + return memory.lookup(log.getID()); + } + */ + + /* + protected void processObjects(){ + ArrayList objects = log.getObjects(timeStep); + for(int i = 0; i < objects.size(); i++){ + Object obj = objects.get(i); + for(int j = handlers.size()-1; j >= 0; j--){ + Handler h = (Handler)handlers.get(j); + if(h.handle(obj,timeStep)) + break; + } + } + doLayout(); + } + + protected void setHistory(){ + if(!getAgent().isHumanoid()) + return; + int[] hist = ((Humanoid)getAgent()).getPositionHistory(); + moveHistory.removeAllObjects(); + if(hist.length > 0){ + RescueObject[] rs = new RescueObject[hist.length]; + rs[0] = memory.lookup(hist[0]); + for (int i=1;i<rs.length;++i){ + rs[i] = memory.lookup(hist[i]); + //if(rs[i-1].isNode() || i==1) + moveHistory.addObject(new RescueObject[]{rs[i-1],rs[i]}); + } + } + } + */ +} diff --git a/modules/oldsims/rescuecore/debug/DebugWriter.java b/modules/oldsims/rescuecore/debug/DebugWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..a79935b01abe51c24ea9d2927b6e01d065abaee0 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/DebugWriter.java @@ -0,0 +1,121 @@ +package rescuecore.debug; + +import java.util.*; +import java.io.*; +import rescuecore.*; + +public class DebugWriter { + // private static Map<RescueComponent,List<DebugEntry>> componentToEntries = new HashMap<RescueComponent,List<DebugEntry>>(); + private static Map<RescueComponent,DebugTarget> componentTargets = new HashMap<RescueComponent,DebugTarget>(); + private static Map<RescueComponent,String> componentNames = new HashMap<RescueComponent,String>(); + private static Map<String,DebugTarget> targets = new HashMap<String,DebugTarget>(); + + public static void register(RescueComponent component,String name, String targetName) { + DebugTarget target = createTarget(targetName); + if (target!=null) { + componentTargets.put(component,target); + // componentToEntries.put(component,new ArrayList<DebugEntry>()); + componentNames.put(component,name); + } + } + + public static void logInitialObjects(RescueComponent component, Collection<RescueObject> objects) { + log(component,new DebugEntry.RescueObjectCollectionEntry(objects,0)); + } + + public static void logObjectAdded(RescueComponent component, RescueObject o, int time) { + log(component,new DebugEntry.RescueObjectEntry(o,time)); + } + + public static void logObjectChanged(RescueComponent component, RescueObject o, Property p, int time) { + if (p instanceof IntProperty) { + log(component,new DebugEntry.IntPropertyUpdateEntry(o.getID(),p.getType(),((IntProperty)p).getValue(),time)); + } + if (p instanceof ArrayProperty) { + log(component,new DebugEntry.ArrayPropertyUpdateEntry(o.getID(),p.getType(),((ArrayProperty)p).getValues(),time)); + } + } + + public static void logUserObject(RescueComponent component, Object o, int time) { + log(component,new DebugEntry.ObjectDebugEntry(o,time)); + } + + private static void log(RescueComponent component, DebugEntry entry) { + DebugTarget target = componentTargets.get(component); + String name = componentNames.get(component); + if (target==null) System.err.println("WARNING: Unregistered component tried to log something: "+component); + else { + synchronized(target) { + try { + target.write(name,entry); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void flush(RescueComponent component) { + DebugTarget target = componentTargets.get(component); + if (target!=null) { + synchronized(target) { + try { + target.flush(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void flushAll() { + for (RescueComponent next : componentTargets.keySet()) { + flush(next); + } + } + + private static DebugTarget createTarget(String target) { + DebugTarget result = targets.get(target); + if (result==null) { + try { + result = new FileDebugTarget(target); + targets.put(target,result); + } + catch (IOException e) { + System.err.println("ERROR: Could not create debug target: "+e); + return null; + } + } + return result; + } + + private abstract static class DebugTarget { + public abstract void write(String owner, DebugEntry entry) throws IOException; + public abstract void flush() throws IOException; + public abstract void close() throws IOException; + } + + private static class FileDebugTarget extends DebugTarget { + private ObjectOutputStream out; + + public FileDebugTarget(String fileName) throws IOException { + out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName))); + } + + public void write(String owner, DebugEntry entry) throws IOException { + out.writeUTF(owner); + out.writeObject(entry); + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + out = null; + } + } +} diff --git a/modules/oldsims/rescuecore/debug/Debugger.java b/modules/oldsims/rescuecore/debug/Debugger.java new file mode 100644 index 0000000000000000000000000000000000000000..d8c7a5eca1ad15d85800660f9567005cee76a1d7 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/Debugger.java @@ -0,0 +1,255 @@ +/* + * Last change: $Date: 2004/05/31 01:56:13 $ $Revision: 1.6 $ Copyright (c) + * 2004, The Black Sheep, Department of Computer Science, The University of + * Auckland All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. Neither the name of + * The Black Sheep, The Department of Computer Science or The University of + * Auckland nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package rescuecore.debug; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTabbedPane; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class Debugger extends JPanel { + + private Collection<DebugPane> panes; + // private WorldLog log; + private DebugLog log; + private JTabbedPane tabs; + private JSlider time; + + public Debugger() { + super(new BorderLayout()); + tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + // lower panel + JPanel bottom = new JPanel(); + JPanel timePanel = new JPanel(new BorderLayout()); + Border b = BorderFactory.createLineBorder(Color.BLACK); + b = BorderFactory.createTitledBorder(b, "Time Step"); + timePanel.setBorder(b); + time = new JSlider(0, 300, 0); + time.setPaintLabels(true); + time.setPaintTicks(true); + time.setSnapToTicks(true); + time.setMinorTickSpacing(1); + time.setMajorTickSpacing(50); + JButton left = new JButton(" < "); + JButton right = new JButton(" > "); + left.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + moveTime(-1); + } + }); + right.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + moveTime(1); + } + }); + time.addChangeListener(new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + setTimeStep(time.getValue()); + } + }); + timePanel.add(time, BorderLayout.CENTER); + timePanel.add(left, BorderLayout.WEST); + timePanel.add(right, BorderLayout.EAST); + + add(tabs, BorderLayout.CENTER); + add(timePanel, BorderLayout.SOUTH); + } + + public void init(File file) throws IOException, ClassNotFoundException { + // log = new WorldLog(file); + log = new DebugLog(file); + // Create one tab per rescue component + tabs.removeAll(); + // AgentLog[] aLogs = log.getAgentLogs(); + Collection<String> names = log.getAllNames(); + panes = new ArrayList<DebugPane>(names.size()); + for (String next : names) { + DebugPane pane = makeDebugPane(next, log); + panes.add(pane); + tabs.add(pane); + } + try { + registerHandler(CommandHandler.class); + registerHandler(StringHandler.class); + registerHandler(UpdateHandler.class); + } catch (Exception e) { + // Should never happen + e.printStackTrace(); + } + time.setMaximum(log.getMaxTimestep()); + setTimeStep(0); + } + + public int getTimeStep() { + return time.getValue(); + } + + public void registerHandler(String className) throws ClassNotFoundException, InstantiationException, + IllegalAccessException, InvocationTargetException, NoSuchMethodException { + registerHandler(Class.forName(className)); + } + + public void registerHandler(Class clazz) + throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { + for (DebugPane next : panes) { + Handler hand = (Handler) clazz.getDeclaredConstructor().newInstance(); + next.registerHandler(hand); + } + } + + @Deprecated + public void registerHandler(Handler h) { + try { + registerHandler(h.getClass()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void moveTime(int change) { + time.setValue(time.getValue() + change); + } + + public DebugPane makeDebugPane(String name, DebugLog log) { + return new DebugPane(log, name); + } + + private void setTimeStep(int time) { + for (DebugPane next : panes) { + next.moveToTimeStep(time); + } + } + + public static void main(String[] args) { + Debugger d = null; + String fileName = null; + Collection handlers = new ArrayList(); + for (int i = 0; i < args.length; ++i) { + if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) { + printUsage(); + return; + } else if (fileName == null) + fileName = args[i]; + else { + // Is this a file? + File f = new File(args[i]); + if (f.exists()) { + String nextClass = null; + try { + BufferedReader in = new BufferedReader(new FileReader(f)); + nextClass = in.readLine(); + while (nextClass != null) { + if (!nextClass.equals("") && !nextClass.startsWith("#")) { + handlers.add(nextClass); + } + nextClass = in.readLine(); + } + } catch (IOException e) { + System.err.println("Error reading file " + args[i] + ": " + e); + } + } else { + handlers.add(args[i]); + } + } + } + + d = new Debugger(); + + File file = null; + try { + if (fileName == null) { + JFileChooser chooser = new JFileChooser(); + int returnVal = chooser.showOpenDialog(null); + if (returnVal == JFileChooser.APPROVE_OPTION) { + file = chooser.getSelectedFile(); + } else + System.exit(0); + } else { + file = new File(fileName); + } + d.init(file); + } catch (Exception e) { + e.printStackTrace(); + System.exit(-1); + } + for (Iterator it = handlers.iterator(); it.hasNext();) { + String next = (String) it.next(); + try { + d.registerHandler(next); + } catch (Exception e) { + System.err.println("Couldn't register handler " + next + ": " + e); + } + } + JFrame frame = new JFrame("Debugger"); + frame.setContentPane(d); + frame.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + Toolkit t = Toolkit.getDefaultToolkit(); + Dimension size = t.getScreenSize(); + frame.setSize((int) size.getWidth(), (int) size.getHeight()); + frame.setVisible(true); + } + + private static void printUsage() { + System.out.println("Usage: Debugger [options] [filename [handlers]]"); + System.out.println( + "Any handlers will be loaded and registered automatically. You can specify handlers as either fully-qualified class names or file names. Each line in each file name should contain the fully-qualified class name of a handler, lines beginning with a # will be ignored"); + System.out.println("Options"); + System.out.println("-h\t--help\tPrint this message"); + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/debug/ExtinguishCommand.java b/modules/oldsims/rescuecore/debug/ExtinguishCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..265b34c4ee05221058bd63e11cc9ade2e2a192d6 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/ExtinguishCommand.java @@ -0,0 +1,43 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import java.awt.Color; +import rescuecore.*; +import rescuecore.objects.*; + +public class ExtinguishCommand{ + + private int[] coords; + private Color colour = new Color(0,64,255,160); + + public ExtinguishCommand(Building b, Humanoid h, Memory memory) throws CannotFindLocationException { + coords = new int[4]; + int[] xy = memory.getXY(memory.lookup(h.getPosition())); + coords[0] = xy[0]; + coords[1] = xy[1]; + coords[2] = b.getX(); + coords[3] = b.getY(); + } + + public Color getColour(){ + return colour; + } + + public int[] getCoords(){ + return coords; + } +} diff --git a/modules/oldsims/rescuecore/debug/ExtinguishCommandRenderer.java b/modules/oldsims/rescuecore/debug/ExtinguishCommandRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..86421aef9003168b8d2354cf4c5cda9aa77043c0 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/ExtinguishCommandRenderer.java @@ -0,0 +1,38 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import java.awt.*; +import java.awt.geom.*; +import rescuecore.view.*; +import rescuecore.*; + +public class ExtinguishCommandRenderer implements MapRenderer { + + public boolean canRender(Object o) { + return o instanceof ExtinguishCommand; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + ExtinguishCommand ec = (ExtinguishCommand)o; + int[] coords = ec.getCoords(); + Shape shape = new Line2D.Double(transform.toScreenX(coords[0]),transform.toScreenY(coords[1]),transform.toScreenX(coords[2]),transform.toScreenY(coords[3])); + shape = new BasicStroke(3).createStrokedShape(shape); + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,ec.getColour()); + ((Graphics2D)g).fill(shape); + return shape; + } +} diff --git a/modules/oldsims/rescuecore/debug/Handler.java b/modules/oldsims/rescuecore/debug/Handler.java new file mode 100644 index 0000000000000000000000000000000000000000..d3a1b4097f456587b987bfcfa322be5ed9c4c88a --- /dev/null +++ b/modules/oldsims/rescuecore/debug/Handler.java @@ -0,0 +1,45 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import rescuecore.*; +import rescuecore.view.*; +import javax.swing.*; + +public interface Handler{ + + /** + * Called once by the DebugPane when the Handler is registered. This gets + * a JComponent to add to the Debugger that this Handler may write to. + * @return A JComponent to add, or null if no JComponent is used by this Handler. + **/ + public JComponent getComponent(); + /** + * Gets a Layer for this Handler to draw to. + * @return A Layer to draw on, or null if no Layer is needed by this Handler. + **/ + public Layer getLayer(); + /** + * Handles the debugging of a transient object. + * @return Whether or not this Handler has dealt with the Object. + **/ + public boolean handle(Object o, int timeStep); + /** + * Sets up the Handler on a given memory. + **/ + public void setMemory(Memory m); + +} diff --git a/modules/oldsims/rescuecore/debug/OutlineRenderer.java b/modules/oldsims/rescuecore/debug/OutlineRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..75382feb8c67fc3e5b3d5986fb5e3fe7e916520f --- /dev/null +++ b/modules/oldsims/rescuecore/debug/OutlineRenderer.java @@ -0,0 +1,100 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import java.awt.*; +import rescuecore.view.*; +import rescuecore.objects.*; +import rescuecore.*; + +public class OutlineRenderer implements MapRenderer { + public static final OutlineRenderer RED = new OutlineRenderer(Color.RED); + public static final OutlineRenderer GREEN = new OutlineRenderer(Color.GREEN); + public static final OutlineRenderer BLUE = new OutlineRenderer(Color.BLUE); + public static final OutlineRenderer ORANGE = new OutlineRenderer(Color.ORANGE); + public static final OutlineRenderer YELLOW = new OutlineRenderer(Color.YELLOW); + public static final OutlineRenderer WHITE = new OutlineRenderer(Color.WHITE); + + private Color outline; + private Color fill; + + private OutlineRenderer(Color outline){ + this.outline = outline; + this.fill = new Color(outline.getRed(),outline.getGreen(),outline.getBlue(),96); + } + + public boolean canRender(Object o) { + if (o instanceof RescueObject[] && ((RescueObject[])o).length == 2){ + return true; + } + return false; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + RescueObject[] rs = (RescueObject[])o; + int x1 = 0; + int y1 = 0; + int x2 = 0; + int y2 = 0; + if(rs[1].isBuilding()){ + Building b = (Building)rs[1]; + Node n = (Node)rs[0]; + x2 = transform.toScreenX(b.getX()); + y2 = transform.toScreenY(b.getY()); + x1 = transform.toScreenX(n.getX()); + y1 = transform.toScreenY(n.getY()); + + } + else if(rs[0].isBuilding()){ + Building b = (Building)rs[0]; + Node n = (Node)rs[1]; + x2 = transform.toScreenX(b.getX()); + y2 = transform.toScreenY(b.getY()); + x1 = transform.toScreenX(n.getX()); + y1 = transform.toScreenY(n.getY()); + + } + else if(rs[1].isRoad() || rs[0].isRoad()){ + Road r; + if(rs[0].isRoad()) + r = (Road)rs[0]; + else + r = (Road)rs[1]; + Node head = (Node)(memory.lookup(r.getHead())); + Node tail = (Node)(memory.lookup(r.getTail())); + x2 = transform.toScreenX(head.getX()); + y2 = transform.toScreenY(head.getY()); + x1 = transform.toScreenX(tail.getX()); + y1 = transform.toScreenY(tail.getY()); + } + else if(rs[0].isNode() && rs[1].isNode()){ + Node n1 = (Node)rs[0]; + Node n2 = (Node)rs[1]; + x2 = transform.toScreenX(n1.getX()); + y2 = transform.toScreenY(n1.getY()); + x1 = transform.toScreenX(n2.getX()); + y1 = transform.toScreenY(n2.getY()); + } + else + return new Polygon(new int[0],new int[0],0); + Shape shape = new java.awt.geom.Line2D.Double(x1,y1,x2,y2); + shape = new BasicStroke(6).createStrokedShape(shape); + //RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,outline); + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,fill); + ((Graphics2D)g).fill(shape); + return shape; + } +} diff --git a/modules/oldsims/rescuecore/debug/StringHandler.java b/modules/oldsims/rescuecore/debug/StringHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..0d9f375b62f73a663591d840f47c120e653406cd --- /dev/null +++ b/modules/oldsims/rescuecore/debug/StringHandler.java @@ -0,0 +1,70 @@ +/* + * Created on 21/05/2004 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package rescuecore.debug; + +import javax.swing.JComponent; + +import rescuecore.Memory; +import rescuecore.view.Layer; +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; + +/** + * @author Jono + * + */ +public class StringHandler implements Handler { + + private int timeStep = -1; + private JScrollPane pane; + private JTextArea notes; + + /* (non-Javadoc) + * @see rescuecore.debug.Handler#getComponent() + */ + public JComponent getComponent() { + if(pane == null){ + JPanel p = new JPanel(new BorderLayout()); + Border bord = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK),"Notes"); + p.setBorder(bord); + notes = new JTextArea(50,10); + p.add(notes,BorderLayout.CENTER); + pane = new JScrollPane(p); + pane.setPreferredSize(new Dimension(DebugPane.HANDLER_WIDTH,200)); + } + return pane; + } + + /* (non-Javadoc) + * @see rescuecore.debug.Handler#getLayer() + */ + public Layer getLayer() { + return null; + } + + /* (non-Javadoc) + * @see rescuecore.debug.Handler#handle(java.lang.Object, int) + */ + public boolean handle(Object o, int timeStep) { + if(this.timeStep != timeStep){ + notes.setText(""); + this.timeStep = timeStep; + } + if(!(o instanceof String)) + return false; + notes.append(o.toString()); + notes.append("\n"); + return true; + } + + /* (non-Javadoc) + * @see rescuecore.debug.Handler#setMemory(rescuecore.Memory) + */ + public void setMemory(Memory m) {} + +} diff --git a/modules/oldsims/rescuecore/debug/UpdateHandler.java b/modules/oldsims/rescuecore/debug/UpdateHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..8077750273e8c3090cc2a304043a3ac736264be4 --- /dev/null +++ b/modules/oldsims/rescuecore/debug/UpdateHandler.java @@ -0,0 +1,76 @@ +/* + * Last change: $Date: 2004/06/10 01:17:51 $ + * $Revision: 1.11 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.debug; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.view.*; +import rescuecore.commands.*; +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; + +public class UpdateHandler implements Handler { + private DefaultListModel messages; + private JScrollPane messagePane; + + public UpdateHandler(){ + JPanel messagePanel = new JPanel(new BorderLayout()); + messagePanel.setBorder(BorderFactory.createTitledBorder("Updates")); + messages = new DefaultListModel(); + JList messageList = new JList(messages); + messagePanel.add(messageList,BorderLayout.CENTER); + messagePane = new JScrollPane(messagePanel); + messagePane.setPreferredSize(new Dimension(DebugPane.HANDLER_WIDTH,80)); + } + + public void setMemory(Memory m){ + } + + + public JComponent getComponent(){ + return messagePane; + } + + public Layer getLayer(){ + return null; + } + + public boolean handle(Object o, int timeStep){ + int time; + RescueObject[] objects; + if (o instanceof KASense) { + time = ((KASense)o).getTime(); + objects = ((KASense)o).getUpdatedObjects(); + } + else if (o instanceof Update) { + time = ((Update)o).getTime(); + objects = ((Update)o).getUpdatedObjects(); + } + else return false; + messages.clear(); + for (RescueObject next : objects) { + messages.addElement(next.toString()); + int[] known = next.getKnownPropertyTypes(); + for (int prop : known) { + if (next.getLastPropertyUpdate(prop)==time) { + messages.addElement(" "+next.getProperty(prop)); + } + } + } + return true; + } +} diff --git a/modules/oldsims/rescuecore/event/FilterMemoryListener.java b/modules/oldsims/rescuecore/event/FilterMemoryListener.java new file mode 100644 index 0000000000000000000000000000000000000000..4a9c168e5003093dfcec5d61ba439074b6a155e8 --- /dev/null +++ b/modules/oldsims/rescuecore/event/FilterMemoryListener.java @@ -0,0 +1,91 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +import rescuecore.*; + +/** + This class will filter which events get passed to a MemoryListener + */ +public class FilterMemoryListener implements MemoryListener { + private int mode; + private Class clazz; + private int type; + private MemoryListener listener; + + public final static int MODE_CLASS = 0; + public final static int MODE_TYPE = 1; + + private FilterMemoryListener(int mode, Class clazz, int type, MemoryListener l) { + this.mode = mode; + this.clazz = clazz; + this.type = type; + this.listener = l; + } + + /** + Create a new FilterMemoryListener that will only pass on notifications that concern RescueObjects of a particular class + @param clazz The Class we are interested in + @param l The MemoryListener to pass qualifying updates down to + @return A MemoryListener that will filter out all notifications that do not concern RescueObjects of the given class + */ + public static MemoryListener createClassFilter(Class clazz, MemoryListener l) { + return new FilterMemoryListener(MODE_CLASS,clazz,0,l); + } + + /** + Create a new FilterMemoryListener that will only pass on notifications that concern RescueObjects of a particular type + @param type The type we are interested in + @param l The MemoryListener to pass qualifying updates down to + @return A MemoryListener that will filter out all notifications that do not concern RescueObjects of the given type + @see RescueConstants#TYPE_CIVILIAN + @see RescueConstants#TYPE_FIRE_BRIGADE + @see RescueConstants#TYPE_AMBULANCE_TEAM + @see RescueConstants#TYPE_POLICE_FORCE + @see RescueConstants#TYPE_ROAD + @see RescueConstants#TYPE_NODE + @see RescueConstants#TYPE_RIVER + @see RescueConstants#TYPE_RIVER_NODE + @see RescueConstants#TYPE_BUILDING + @see RescueConstants#TYPE_REFUGE + @see RescueConstants#TYPE_FIRE_STATION + @see RescueConstants#TYPE_AMBULANCE_CENTER + @see RescueConstants#TYPE_POLICE_OFFICE + @see RescueConstants#TYPE_WORLD + @see RescueConstants#TYPE_CAR + */ + public static MemoryListener createTypeFilter(int type, MemoryListener l) { + return new FilterMemoryListener(MODE_TYPE,null,type,l); + } + + public void objectAdded(ObjectAddedEvent event) { + if (filter(event.getObject())) listener.objectAdded(event); + } + + public void objectChanged(ObjectChangedEvent event) { + if (filter(event.getObject())) listener.objectChanged(event); + } + + private boolean filter(RescueObject o) { + switch(mode) { + case MODE_CLASS: + return (clazz.isInstance(o)); + case MODE_TYPE: + return (o.getType()==type); + } + return false; + } +} diff --git a/modules/oldsims/rescuecore/event/MemoryEvent.java b/modules/oldsims/rescuecore/event/MemoryEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..9f679af6671160ccdd927d035300241c6c9caa5c --- /dev/null +++ b/modules/oldsims/rescuecore/event/MemoryEvent.java @@ -0,0 +1,39 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +import rescuecore.*; + +public abstract class MemoryEvent extends java.util.EventObject { + private RescueObject object; + private int timestamp; + + public final static Object DEFAULT_SOURCE = "No source"; + + protected MemoryEvent(RescueObject object, int timestamp, Object source) { + super(source==null?DEFAULT_SOURCE:source); + this.object = object; + this.timestamp = timestamp; + } + + public RescueObject getObject() { + return object; + } + + public int getTimestamp() { + return timestamp; + } +} diff --git a/modules/oldsims/rescuecore/event/MemoryListener.java b/modules/oldsims/rescuecore/event/MemoryListener.java new file mode 100644 index 0000000000000000000000000000000000000000..44918157a3de86c5c9d2859dcff450e7e3d8dcdf --- /dev/null +++ b/modules/oldsims/rescuecore/event/MemoryListener.java @@ -0,0 +1,34 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +/** + This interface allows notification of adds/changes to the Memory + @see rescuecore.Memory + */ +public interface MemoryListener { + /** + Notification that an object has been added to the memory + @param event An ObjectAddedEvent describing the change + */ + public void objectAdded(ObjectAddedEvent event); + + /** + Notification that an object in the memory has been changed + @param event An ObjectChangedEvent describing the change + */ + public void objectChanged(ObjectChangedEvent event); +} diff --git a/modules/oldsims/rescuecore/event/ObjectAddedEvent.java b/modules/oldsims/rescuecore/event/ObjectAddedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..223c64c16c87a5586da9ddeacc91d27d7c736dde --- /dev/null +++ b/modules/oldsims/rescuecore/event/ObjectAddedEvent.java @@ -0,0 +1,24 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +import rescuecore.*; + +public class ObjectAddedEvent extends MemoryEvent { + public ObjectAddedEvent(RescueObject object, int timestamp, Object source) { + super(object,timestamp,source); + } +} diff --git a/modules/oldsims/rescuecore/event/ObjectChangedEvent.java b/modules/oldsims/rescuecore/event/ObjectChangedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..c74501941a9a57d777d7e2366096261377bf188a --- /dev/null +++ b/modules/oldsims/rescuecore/event/ObjectChangedEvent.java @@ -0,0 +1,36 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +import rescuecore.*; + +public class ObjectChangedEvent extends MemoryEvent { + private int property; + + public ObjectChangedEvent(RescueObject object, int property, int timestamp, Object source) { + super(object,timestamp,source); + this.property = property; + } + + public ObjectChangedEvent(PropertyChangedEvent event) { + super(event.getObject(),event.getTimestamp(),event.getSource()); + this.property = event.getProperty(); + } + + public int getProperty() { + return property; + } +} diff --git a/modules/oldsims/rescuecore/event/PropertyChangedEvent.java b/modules/oldsims/rescuecore/event/PropertyChangedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..ae29b90bd8502685d773bd6b46970991511cdb74 --- /dev/null +++ b/modules/oldsims/rescuecore/event/PropertyChangedEvent.java @@ -0,0 +1,31 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +import rescuecore.*; + +public class PropertyChangedEvent extends MemoryEvent { + private int property; + + public PropertyChangedEvent(RescueObject object, int property, int timestamp, Object source) { + super(object,timestamp,source); + this.property = property; + } + + public int getProperty() { + return property; + } +} diff --git a/modules/oldsims/rescuecore/event/PropertyListener.java b/modules/oldsims/rescuecore/event/PropertyListener.java new file mode 100644 index 0000000000000000000000000000000000000000..fb3a4591f2d721a52385e03697c6fda59afae7bf --- /dev/null +++ b/modules/oldsims/rescuecore/event/PropertyListener.java @@ -0,0 +1,29 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.event; + +/** + This interface allows notification of changes to the properties of an object + @see rescuecore.RescueObject + @see rescuecore.Property + */ +public interface PropertyListener { + /** + Notification that a property has been changed + @param event A PropertyChangedEvent describing the change + */ + public void propertyChanged(PropertyChangedEvent event); +} diff --git a/modules/oldsims/rescuecore/log/ConvertLog.java b/modules/oldsims/rescuecore/log/ConvertLog.java new file mode 100644 index 0000000000000000000000000000000000000000..5d3cc426d62ea961abf2ab581c2554c8db6ff57b --- /dev/null +++ b/modules/oldsims/rescuecore/log/ConvertLog.java @@ -0,0 +1,48 @@ +package rescuecore.log; + +import java.io.*; + +public class ConvertLog { + public static void main(String[] args) { + String inFile = args[0]; + String outFile = args[1]; + try { + DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(inFile)))); + // Check the header + byte[] preamble = new byte[Log.HEADER_LENGTH]; + input.read(preamble,0,preamble.length); + String preambleString = new String(preamble); + if (Log.HEADER_VERSION_0.equals(preambleString)) { + // OK + System.out.println("Original log version 0"); + } + else if (Log.HEADER_VERSION_1.equals(preambleString)) { + System.out.println("Original log version 1"); + // Read and discard parameters + int length = input.readInt(); + input.skip(length); + } + else { + throw new InvalidLogException("Unknown log version: "+preambleString); + } + // Write the header + DataOutputStream out = new DataOutputStream(new FileOutputStream(new File(outFile))); + preamble = Log.HEADER_VERSION_0.getBytes(); + out.write(preamble); + byte[] buffer = new byte[1024]; + int received = 0; + do { + received = input.read(buffer); + if (received>0) { + out.write(buffer,0,received); + } + } while (received>0); + out.flush(); + out.close(); + input.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/modules/oldsims/rescuecore/log/InvalidLogException.java b/modules/oldsims/rescuecore/log/InvalidLogException.java new file mode 100644 index 0000000000000000000000000000000000000000..3fb9592a0f846916b83d4ff0a568c0c131e87040 --- /dev/null +++ b/modules/oldsims/rescuecore/log/InvalidLogException.java @@ -0,0 +1,21 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.log; + +public class InvalidLogException extends Exception { + public InvalidLogException() {super();} + public InvalidLogException(String s) {super(s);} +} diff --git a/modules/oldsims/rescuecore/log/Log.java b/modules/oldsims/rescuecore/log/Log.java new file mode 100644 index 0000000000000000000000000000000000000000..806a28027ce24570f5b32e1dc33182f4019d8e3c --- /dev/null +++ b/modules/oldsims/rescuecore/log/Log.java @@ -0,0 +1,58 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.9 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.log; + +import java.io.*; +import java.util.*; +import rescuecore.*; +import rescuecore.commands.*; + +public abstract class Log { + public final static String HEADER_VERSION_0 = "RoboCup-Rescue Prototype Log 00\0"; + public final static String HEADER_VERSION_1 = "RoboCup-Rescue Prototype Log 01\0"; + public final static String HEADER_VERSION_2 = "RoboCup-Rescue Prototype Log 02\0"; + public final static int HEADER_LENGTH = HEADER_VERSION_0.length(); + + public abstract int getMaxTimestep(); + public abstract Memory getMemory(int timestep); + public abstract Update getUpdate(int timestep); + public abstract Commands getCommands(int timestep); + public abstract Map<String,String> getConfigValues(); + + public static Log generateLog(String filename) throws IOException, InvalidLogException { + File file = new File(filename); + DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); + byte[] data = new byte[(int)file.length()]; + in.readFully(data); + InputBuffer input = new InputBuffer(data); + // Check the header + byte[] preamble = new byte[HEADER_LENGTH]; + input.readBytes(preamble); + String preambleString = new String(preamble); + if (HEADER_VERSION_0.equals(preambleString)) { + throw new InvalidLogException("Log version 0 is no longer supported"); + } + else if (HEADER_VERSION_1.equals(preambleString)) { + throw new InvalidLogException("Log version 1 is no longer supported"); + } + else if (HEADER_VERSION_2.equals(preambleString)) { + return new LogVersion2(input); + } + else { + throw new InvalidLogException("Unknown log version: "+preambleString); + } + } +} diff --git a/modules/oldsims/rescuecore/log/LogVersion2.java b/modules/oldsims/rescuecore/log/LogVersion2.java new file mode 100644 index 0000000000000000000000000000000000000000..9e6912add2850610452740d72121ac8cdf224563 --- /dev/null +++ b/modules/oldsims/rescuecore/log/LogVersion2.java @@ -0,0 +1,108 @@ +/* + * Last change: $Date: 2005/06/14 21:55:52 $ + * $Revision: 1.9 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.log; + +import java.io.*; +import java.util.*; +import rescuecore.*; +import rescuecore.commands.*; + +public class LogVersion2 extends Log { + private Map<Integer,Update> updates; + private Map<Integer,Commands> commands; + private int maxTimestep; + private Map<String,String> config; + + private Memory memory; + private int lastTime; + + public LogVersion2(InputBuffer in) throws IOException { + updates = new HashMap<Integer,Update>(); + commands = new HashMap<Integer,Commands>(); + maxTimestep = 0; + config = new HashMap<String,String>(); + // Load the preamble + int size = in.readInt(); + int configEntries = in.readInt(); + for (int i=0;i<configEntries;++i) { + String key = in.readString(); + String value = in.readString(); + config.put(key,value); + } + // Read all the commands and updates, using the old formats + Command c = null; + while (in.available()>0) { + Command[] newCommands = readCommands(in); + for (Command next : newCommands) { + if (next instanceof Update) { + Update u = (Update)next; + updates.put(u.getTime(),u); + maxTimestep = Math.max(maxTimestep,u.getTime()); + } + else if (next instanceof Commands) { + Commands o = (Commands)next; + commands.put(o.getTime(),o); + maxTimestep = Math.max(maxTimestep,o.getTime()); + } + else { + System.err.println("Unrecognised command in log: "+next); + } + } + } + getMemory(0); + } + + public int getMaxTimestep() { + return maxTimestep; + } + + public Update getUpdate(int timestep) { + return updates.get(timestep); + } + + public Commands getCommands(int timestep) { + return commands.get(timestep); + } + + public Map<String,String> getConfigValues() { + return new HashMap(config); + } + + + public Memory getMemory(int timestep) { + if (memory==null || lastTime > timestep) { + memory = new HashMemory(); + lastTime = -1; + } + try { + // Apply all updates from the last time until the required time + for (int currentTime = lastTime+1;currentTime<=timestep && currentTime<maxTimestep;++currentTime) { + Update update = getUpdate(currentTime); + if (update!=null) memory.update(update); + } + lastTime = timestep; + } + catch (Exception e) { + e.printStackTrace(); + } + return memory; + } + + private Command[] readCommands(InputBuffer in) { + int totalSize = in.readInt(); + return in.readCommands(); + } +} diff --git a/modules/oldsims/rescuecore/objects/AmbulanceCenter.java b/modules/oldsims/rescuecore/objects/AmbulanceCenter.java new file mode 100644 index 0000000000000000000000000000000000000000..931c87e1ba2e5b526960362516e90765a268a223 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/AmbulanceCenter.java @@ -0,0 +1,41 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_AMBULANCE_CENTER object + @see RescueConstants#TYPE_AMBULANCE_CENTER + */ +public class AmbulanceCenter extends Building { + public AmbulanceCenter() { + super(); + } + + public AmbulanceCenter(int x, int y, int floors, int attributes, boolean ignition, int fieryness, int brokenness, int[] entrances, int code, int ground, int total, int[] apexes, int temperature, int importance) { + super(x,y,floors,attributes,ignition,fieryness,brokenness,entrances,code,ground,total,apexes,temperature,importance); + } + + public AmbulanceCenter(Building other) { + super(other); + } + + public int getType() { + return RescueConstants.TYPE_AMBULANCE_CENTER; + } +} diff --git a/modules/oldsims/rescuecore/objects/AmbulanceTeam.java b/modules/oldsims/rescuecore/objects/AmbulanceTeam.java new file mode 100644 index 0000000000000000000000000000000000000000..9099f429e7d3420be89e342147e22d6e3252f043 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/AmbulanceTeam.java @@ -0,0 +1,37 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_AMBULANCE_TEAM object + @see RescueConstants#TYPE_AMBULANCE_TEAM + */ +public class AmbulanceTeam extends Humanoid { + public AmbulanceTeam() { + super(); + } + + public AmbulanceTeam(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury) { + super(pos,extra,dir,history,stam,health,dmg,bury); + } + + public int getType() { + return RescueConstants.TYPE_AMBULANCE_TEAM; + } +} diff --git a/modules/oldsims/rescuecore/objects/Building.java b/modules/oldsims/rescuecore/objects/Building.java new file mode 100644 index 0000000000000000000000000000000000000000..74236780315bd03389d14305088e148323d7222e --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Building.java @@ -0,0 +1,349 @@ +/* + * Last change: $Date: 2004/05/04 03:40:13 $ + * $Revision: 1.8 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_BUILDING object + @see RescueConstants#TYPE_BUILDING + */ +public class Building extends MotionlessObject { + private IntProperty x,y,floors, attributes, ignition, fieryness, brokenness, code, ground, total, temperature, importance; + private ArrayProperty entrances,apexes; + + public Building() { + x = new IntProperty(RescueConstants.PROPERTY_X); + y = new IntProperty(RescueConstants.PROPERTY_Y); + floors = new IntProperty(RescueConstants.PROPERTY_FLOORS); + attributes = new IntProperty(RescueConstants.PROPERTY_BUILDING_ATTRIBUTES); + ignition = new IntProperty(RescueConstants.PROPERTY_IGNITION); + fieryness = new IntProperty(RescueConstants.PROPERTY_FIERYNESS); + brokenness = new IntProperty(RescueConstants.PROPERTY_BROKENNESS); + entrances = new ArrayProperty(RescueConstants.PROPERTY_ENTRANCES); + // shape = new IntProperty(RescueConstants.PROPERTY_BUILDING_SHAPE_ID); + code = new IntProperty(RescueConstants.PROPERTY_BUILDING_CODE); + ground = new IntProperty(RescueConstants.PROPERTY_BUILDING_AREA_GROUND); + total = new IntProperty(RescueConstants.PROPERTY_BUILDING_AREA_TOTAL); + apexes = new ArrayProperty(RescueConstants.PROPERTY_BUILDING_APEXES); + temperature = new IntProperty(RescueConstants.PROPERTY_BUILDING_TEMPERATURE); + importance = new IntProperty(RescueConstants.PROPERTY_BUILDING_IMPORTANCE); + } + + public Building(Building other) { + this(other.getX(),other.getY(),other.getFloors(),other.getBuildingAttributes(),other.isIgnited(),other.getFieryness(),other.getBrokenness(),other.getEntrances(),other.getBuildingCode(),other.getGroundArea(),other.getTotalArea(),other.getApexes(),other.getTemperature(),other.getImportance()); + } + + public Building(int x, int y, int floors, int attributes, boolean ignition, int fieryness, int brokenness, int[] entrances, int code, int ground, int total, int[] apexes, int temperature,int importance) { + this.x = new IntProperty(RescueConstants.PROPERTY_X,x); + this.y = new IntProperty(RescueConstants.PROPERTY_Y,y); + this.floors = new IntProperty(RescueConstants.PROPERTY_FLOORS,floors); + this.attributes = new IntProperty(RescueConstants.PROPERTY_BUILDING_ATTRIBUTES,attributes); + this.ignition = new IntProperty(RescueConstants.PROPERTY_IGNITION,ignition); + this.fieryness = new IntProperty(RescueConstants.PROPERTY_FIERYNESS,fieryness); + this.brokenness = new IntProperty(RescueConstants.PROPERTY_BROKENNESS,brokenness); + this.entrances = new ArrayProperty(RescueConstants.PROPERTY_ENTRANCES,entrances); + // this.shape = new IntProperty(RescueConstants.PROPERTY_BUILDING_SHAPE_ID,shape); + this.code = new IntProperty(RescueConstants.PROPERTY_BUILDING_CODE,code); + this.ground = new IntProperty(RescueConstants.PROPERTY_BUILDING_AREA_GROUND,ground); + this.total = new IntProperty(RescueConstants.PROPERTY_BUILDING_AREA_TOTAL,total); + this.apexes = new ArrayProperty(RescueConstants.PROPERTY_BUILDING_APEXES,apexes); + this.temperature = new IntProperty(RescueConstants.PROPERTY_BUILDING_TEMPERATURE,temperature); + this.importance = new IntProperty(RescueConstants.PROPERTY_BUILDING_IMPORTANCE,importance); + } + + public int getType() { + return RescueConstants.TYPE_BUILDING; + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_X: + case RescueConstants.PROPERTY_Y: + case RescueConstants.PROPERTY_FLOORS: + case RescueConstants.PROPERTY_BUILDING_ATTRIBUTES: + case RescueConstants.PROPERTY_IGNITION: + case RescueConstants.PROPERTY_FIERYNESS: + case RescueConstants.PROPERTY_BROKENNESS: + case RescueConstants.PROPERTY_ENTRANCES: + // case RescueConstants.PROPERTY_BUILDING_SHAPE_ID: + case RescueConstants.PROPERTY_BUILDING_CODE: + case RescueConstants.PROPERTY_BUILDING_AREA_GROUND: + case RescueConstants.PROPERTY_BUILDING_AREA_TOTAL: + case RescueConstants.PROPERTY_BUILDING_APEXES: + case RescueConstants.PROPERTY_BUILDING_IMPORTANCE: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_X: + return x; + case RescueConstants.PROPERTY_Y: + return y; + case RescueConstants.PROPERTY_FLOORS: + return floors; + case RescueConstants.PROPERTY_BUILDING_ATTRIBUTES: + return attributes; + case RescueConstants.PROPERTY_IGNITION: + return ignition; + case RescueConstants.PROPERTY_FIERYNESS: + return fieryness; + case RescueConstants.PROPERTY_BROKENNESS: + return brokenness; + case RescueConstants.PROPERTY_ENTRANCES: + return entrances; + // case RescueConstants.PROPERTY_BUILDING_SHAPE_ID: + // return shape; + case RescueConstants.PROPERTY_BUILDING_CODE: + return code; + case RescueConstants.PROPERTY_BUILDING_AREA_GROUND: + return ground; + case RescueConstants.PROPERTY_BUILDING_AREA_TOTAL: + return total; + case RescueConstants.PROPERTY_BUILDING_APEXES: + return apexes; + case RescueConstants.PROPERTY_BUILDING_TEMPERATURE: + return temperature; + case RescueConstants.PROPERTY_BUILDING_IMPORTANCE: + return importance; + } + return super.getProperty(property); + } + + + public int getX() { + return x.getValue(); + } + + public boolean setX(int newX, int timestamp, Object source) { + if (x.updateValue(newX,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_X,timestamp,source); + return true; + } + return false; + } + + public int getY() { + return y.getValue(); + } + + public boolean setY(int newY, int timestamp, Object source) { + if (y.updateValue(newY,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_Y,timestamp,source); + return true; + } + return false; + } + + public int getFloors() { + return floors.getValue(); + } + + public boolean setFloors(int f, int timestamp, Object source) { + if (floors.updateValue(f,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_FLOORS,timestamp,source); + return true; + } + return false; + } + + public int getBuildingAttributes() { + return attributes.getValue(); + } + + public boolean setBuildingAttributes(int b, int timestamp, Object source) { + if (attributes.updateValue(b,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_ATTRIBUTES,timestamp,source); + return true; + } + return false; + } + + public boolean isIgnited() { + return ignition.getValue() != 0; + } + + public boolean setIgnition(boolean b, int timestamp, Object source) { + if (ignition.updateValue(b?1:0,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_IGNITION,timestamp,source); + return true; + } + return false; + } + + public int getFieryness() { + return fieryness.getValue(); + } + + public boolean setFieryness(int f, int timestamp, Object source) { + if (fieryness.updateValue(f,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_FIERYNESS,timestamp,source); + return true; + } + return false; + } + + public int getBrokenness() { + return brokenness.getValue(); + } + + public boolean setBrokenness(int b, int timestamp, Object source) { + if (brokenness.updateValue(b,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BROKENNESS,timestamp,source); + return true; + } + return false; + } + + public int[] getEntrances() { + return entrances.getValues(); + } + + public boolean setEntrances(int[] e, int timestamp, Object source) { + if (entrances.updateValues(e,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_ENTRANCES,timestamp,source); + return true; + } + return false; + } + + public void clearEntrances(int timestamp, Object source) { + entrances.clear(); + firePropertyChanged(RescueConstants.PROPERTY_ENTRANCES,timestamp,source); + } + + public void appendEntrances(int next, int timestamp, Object source) { + entrances.append(next); + firePropertyChanged(RescueConstants.PROPERTY_ENTRANCES,timestamp,source); + } + + /* + public int getBuildingShapeID() { + return shape.getValue(); + } + + public boolean setBuildingShapeID(int s, int timestamp, Object source) { + if (shape.updateValue(s,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_SHAPE_ID,timestamp,source); + return true; + } + return false; + } + */ + + public int getBuildingCode() { + return code.getValue(); + } + + public boolean setBuildingCode(int c, int timestamp, Object source) { + if (code.updateValue(c,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_CODE,timestamp,source); + return true; + } + return false; + } + + public int getGroundArea() { + return ground.getValue(); + } + + public boolean setGroundArea(int a, int timestamp, Object source) { + if (ground.updateValue(a,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_AREA_GROUND,timestamp,source); + return true; + } + return false; + } + + public int getTotalArea() { + return total.getValue(); + } + + public boolean setTotalArea(int a, int timestamp, Object source) { + if (total.updateValue(a,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_AREA_TOTAL,timestamp,source); + return true; + } + return false; + } + + public int[] getApexes() { + return apexes.getValues(); + } + + public boolean setApexes(int[] a, int timestamp, Object source) { + if (apexes.updateValues(a,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_APEXES,timestamp,source); + return true; + } + return false; + } + + public void clearApexes(int timestamp, Object source) { + apexes.clear(); + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_APEXES,timestamp,source); + } + + public void appendApex(int next, int timestamp, Object source) { + apexes.append(next); + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_APEXES,timestamp,source); + } + + public int getTemperature() { + return temperature.getValue(); + } + + public boolean setTemperature(int value, int timestamp, Object source) { + if (temperature.updateValue(value,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_TEMPERATURE,timestamp,source); + return true; + } + return false; + } + + public int getImportance() { + return importance.getValue(); + } + + public boolean setImportance(int i, int timestamp, Object source) { + if (importance.updateValue(i,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BUILDING_IMPORTANCE,timestamp,source); + return true; + } + return false; + } + + public boolean isOnFire() { + return getFieryness() > 0 && getFieryness() < 4; + } + + public boolean isExtinguished() { + return getFieryness() > 3 && getFieryness() < 7; + } + + public boolean isBurntOut() { + return getFieryness() == 7; + } + + public boolean isUnburnt() { + return getFieryness() == 0; + } +} diff --git a/modules/oldsims/rescuecore/objects/Car.java b/modules/oldsims/rescuecore/objects/Car.java new file mode 100644 index 0000000000000000000000000000000000000000..112aeeeeb3ee680b4f314619b0dcb468a3386313 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Car.java @@ -0,0 +1,37 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_CAR object + @see RescueConstants#TYPE_CAR + */ +public class Car extends Humanoid { + public Car() { + super(); + } + + public Car(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury) { + super(pos,extra,dir,history,stam,health,dmg,bury); + } + + public int getType() { + return RescueConstants.TYPE_CAR; + } +} diff --git a/modules/oldsims/rescuecore/objects/Civilian.java b/modules/oldsims/rescuecore/objects/Civilian.java new file mode 100644 index 0000000000000000000000000000000000000000..91a5826d98de70f78542aa8e96cf1c67c97f5071 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Civilian.java @@ -0,0 +1,37 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_CIVILIAN object + @see RescueConstants#TYPE_CIVILIAN + */ +public class Civilian extends Humanoid { + public Civilian() { + super(); + } + + public Civilian(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury) { + super(pos,extra,dir,history,stam,health,dmg,bury); + } + + public int getType() { + return RescueConstants.TYPE_CIVILIAN; + } +} diff --git a/modules/oldsims/rescuecore/objects/Edge.java b/modules/oldsims/rescuecore/objects/Edge.java new file mode 100644 index 0000000000000000000000000000000000000000..59915f58747c244b717f4ea68b93f6c85757d4d7 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Edge.java @@ -0,0 +1,94 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class Edge extends MotionlessObject { + protected IntProperty head,tail,length; + + protected Edge() { + head = new IntProperty(RescueConstants.PROPERTY_HEAD); + tail = new IntProperty(RescueConstants.PROPERTY_TAIL); + length = new IntProperty(RescueConstants.PROPERTY_LENGTH); + } + + protected Edge(int head, int tail, int length) { + this.head = new IntProperty(RescueConstants.PROPERTY_HEAD,head); + this.tail = new IntProperty(RescueConstants.PROPERTY_TAIL,tail); + this.length = new IntProperty(RescueConstants.PROPERTY_LENGTH,length); + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_HEAD: + case RescueConstants.PROPERTY_TAIL: + case RescueConstants.PROPERTY_LENGTH: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_HEAD: + return head; + case RescueConstants.PROPERTY_TAIL: + return tail; + case RescueConstants.PROPERTY_LENGTH: + return length; + } + return super.getProperty(property); + } + + public int getHead() { + return head.getValue(); + } + + public boolean setHead(int h, int timestamp, Object source) { + if (head.updateValue(h,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_HEAD,timestamp,source); + return true; + } + return false; + } + + public int getTail() { + return tail.getValue(); + } + + public boolean setTail(int t, int timestamp, Object source) { + if (tail.updateValue(t,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_TAIL,timestamp,source); + return true; + } + return false; + } + + public int getLength() { + return length.getValue(); + } + + public boolean setLength(int l, int timestamp, Object source) { + if (length.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_LENGTH,timestamp,source); + return true; + } + return false; + } +} diff --git a/modules/oldsims/rescuecore/objects/FireBrigade.java b/modules/oldsims/rescuecore/objects/FireBrigade.java new file mode 100644 index 0000000000000000000000000000000000000000..901915494b31c7f9c5eaafa524cdfd5944cfe148 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/FireBrigade.java @@ -0,0 +1,89 @@ +/* + * Last change: $Date: 2004/05/04 03:40:13 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_FIRE_BRIGADE object + @see RescueConstants#TYPE_FIRE_BRIGADE + */ +public class FireBrigade extends Humanoid { + private IntProperty waterQuantity;//, stretchedLength; + + public FireBrigade() { + waterQuantity = new IntProperty(RescueConstants.PROPERTY_WATER_QUANTITY); + // stretchedLength = new IntProperty(RescueConstants.PROPERTY_STRETCHED_LENGTH); + } + + public FireBrigade(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury, int water/*, int length*/) { + super(pos,extra,dir,history,stam,health,dmg,bury); + waterQuantity = new IntProperty(RescueConstants.PROPERTY_WATER_QUANTITY,water); + // stretchedLength = new IntProperty(RescueConstants.PROPERTY_STRETCHED_LENGTH,length); + } + + public int getType() { + return RescueConstants.TYPE_FIRE_BRIGADE; + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_WATER_QUANTITY: + // case RescueConstants.PROPERTY_STRETCHED_LENGTH: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_WATER_QUANTITY: + return waterQuantity; + // case RescueConstants.PROPERTY_STRETCHED_LENGTH: + // return stretchedLength; + } + return super.getProperty(property); + } + + public int getWaterQuantity() { + return waterQuantity.getValue(); + } + + public boolean setWaterQuantity(int q, int timestamp, Object source) { + if (waterQuantity.updateValue(q,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_WATER_QUANTITY,timestamp,source); + return true; + } + return false; + } + + /* + public int getStretchedLength() { + return stretchedLength.getValue(); + } + + public boolean setStretchedLength(int l, int timestamp, Object source) { + if (stretchedLength.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_STRETCHED_LENGTH,timestamp,source); + return true; + } + return false; + } + */ +} diff --git a/modules/oldsims/rescuecore/objects/FireStation.java b/modules/oldsims/rescuecore/objects/FireStation.java new file mode 100644 index 0000000000000000000000000000000000000000..0f6b9820d4f4eb8a8fa4aa1770b82999c170cf37 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/FireStation.java @@ -0,0 +1,41 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueConstants; +import rescuecore.RescueObject; + +/** + Encapsulation of a TYPE_FIRE_STATION object + @see RescueConstants#TYPE_FIRE_STATION + */ +public class FireStation extends Building { + public FireStation() { + super(); + } + + public FireStation(int x, int y, int floors, int attributes, boolean ignition, int fieryness, int brokenness, int[] entrances, int code, int ground, int total, int[] apexes, int temperature, int importance) { + super(x,y,floors,attributes,ignition,fieryness,brokenness,entrances,code,ground,total,apexes,temperature,importance); + } + + public FireStation(Building other) { + super(other); + } + + public int getType() { + return RescueConstants.TYPE_FIRE_STATION; + } +} diff --git a/modules/oldsims/rescuecore/objects/Humanoid.java b/modules/oldsims/rescuecore/objects/Humanoid.java new file mode 100644 index 0000000000000000000000000000000000000000..ca70b7b7f5baf2ed3873276f3eb38cedd7ff41ad --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Humanoid.java @@ -0,0 +1,131 @@ +/* + * Last change: $Date: 2004/05/04 03:40:13 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Base class for all humanoid objects (civilians, fire brigades, cars etc) + */ +public abstract class Humanoid extends MovingObject { + private IntProperty stamina, hp, damage, buriedness; + + public Humanoid() { + stamina = new IntProperty(RescueConstants.PROPERTY_STAMINA); + hp = new IntProperty(RescueConstants.PROPERTY_HP); + damage = new IntProperty(RescueConstants.PROPERTY_DAMAGE); + buriedness = new IntProperty(RescueConstants.PROPERTY_BURIEDNESS); + } + + public Humanoid(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury) { + super(pos,extra,dir,history); + stamina = new IntProperty(RescueConstants.PROPERTY_STAMINA,stam); + hp = new IntProperty(RescueConstants.PROPERTY_HP,health); + damage = new IntProperty(RescueConstants.PROPERTY_DAMAGE,dmg); + buriedness = new IntProperty(RescueConstants.PROPERTY_BURIEDNESS,bury); + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_STAMINA: + case RescueConstants.PROPERTY_HP: + case RescueConstants.PROPERTY_DAMAGE: + case RescueConstants.PROPERTY_BURIEDNESS: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_STAMINA: + return stamina; + case RescueConstants.PROPERTY_HP: + return hp; + case RescueConstants.PROPERTY_DAMAGE: + return damage; + case RescueConstants.PROPERTY_BURIEDNESS: + return buriedness; + } + return super.getProperty(property); + } + + public int getStamina() { + return stamina.getValue(); + } + + public boolean setStamina(int s, int timestamp, Object source) { + if (stamina.updateValue(s,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_STAMINA,timestamp,source); + return true; + } + return false; + } + + public int getHP() { + return hp.getValue(); + } + + public boolean setHP(int h, int timestamp, Object source) { + if (hp.updateValue(h,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_HP,timestamp,source); + return true; + } + return false; + } + + public int getDamage() { + return damage.getValue(); + } + + public boolean setDamage(int d, int timestamp, Object source) { + if (damage.updateValue(d,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_DAMAGE,timestamp,source); + return true; + } + return false; + } + + public int getBuriedness() { + return buriedness.getValue(); + } + + public boolean setBuriedness(int b, int timestamp, Object source) { + if (buriedness.updateValue(b,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BURIEDNESS,timestamp,source); + return true; + } + return false; + } + + public boolean isHurt() { + return getHP() < 10000; + } + + public boolean isAlive() { + return getHP() > 0; + } + + public boolean isBuried() { + return getBuriedness() > 0; + } + + public boolean isDamaged() { + return getDamage() > 0; + } +} diff --git a/modules/oldsims/rescuecore/objects/MotionlessObject.java b/modules/oldsims/rescuecore/objects/MotionlessObject.java new file mode 100644 index 0000000000000000000000000000000000000000..7fa71594f161fc06de6a7b35a2d8c562afdda7eb --- /dev/null +++ b/modules/oldsims/rescuecore/objects/MotionlessObject.java @@ -0,0 +1,31 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class MotionlessObject extends RealObject { + protected MotionlessObject() { + } + + // public boolean propertyExists(int property) { + // return super.propertyExists(property); + // } + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + return super.getProperty(property); + } +} diff --git a/modules/oldsims/rescuecore/objects/MovingObject.java b/modules/oldsims/rescuecore/objects/MovingObject.java new file mode 100644 index 0000000000000000000000000000000000000000..bcf4a71f38757fbd5700618cb8caa693dc389557 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/MovingObject.java @@ -0,0 +1,122 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class MovingObject extends RealObject { + protected IntProperty position, positionExtra, direction; + protected ArrayProperty positionHistory; + + protected MovingObject() { + position = new IntProperty(RescueConstants.PROPERTY_POSITION); + positionExtra = new IntProperty(RescueConstants.PROPERTY_POSITION_EXTRA); + direction = new IntProperty(RescueConstants.PROPERTY_DIRECTION); + positionHistory = new ArrayProperty(RescueConstants.PROPERTY_POSITION_HISTORY); + } + + protected MovingObject(int pos, int extra, int dir, int[] history) { + position = new IntProperty(RescueConstants.PROPERTY_POSITION,pos); + positionExtra = new IntProperty(RescueConstants.PROPERTY_POSITION_EXTRA,extra); + direction = new IntProperty(RescueConstants.PROPERTY_DIRECTION,dir); + positionHistory = new ArrayProperty(RescueConstants.PROPERTY_POSITION_HISTORY,history); + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_POSITION: + case RescueConstants.PROPERTY_POSITION_EXTRA: + case RescueConstants.PROPERTY_DIRECTION: + case RescueConstants.PROPERTY_POSITION_HISTORY: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_POSITION: + return position; + case RescueConstants.PROPERTY_POSITION_EXTRA: + return positionExtra; + case RescueConstants.PROPERTY_DIRECTION: + return direction; + case RescueConstants.PROPERTY_POSITION_HISTORY: + return positionHistory; + } + return super.getProperty(property); + } + + public int getPosition() { + return position.getValue(); + } + + public boolean setPosition(int p, int timestamp, Object source) { + if (position.updateValue(p,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_POSITION,timestamp,source); + return true; + } + return false; + } + + public int getPositionExtra() { + return positionExtra.getValue(); + } + + public boolean setPositionExtra(int e, int timestamp, Object source) { + if (positionExtra.updateValue(e,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_POSITION_EXTRA,timestamp,source); + return true; + } + return false; + } + + public int getDirection() { + return direction.getValue(); + } + + public boolean setDirection(int d, int timestamp, Object source) { + if (direction.updateValue(d,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_DIRECTION,timestamp,source); + return true; + } + return false; + } + + public int[] getPositionHistory() { + return positionHistory.getValues(); + } + + public boolean setPositionHistory(int[] h, int timestamp, Object source) { + if (positionHistory.updateValues(h,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_POSITION_HISTORY,timestamp,source); + return true; + } + return false; + } + + public void clearPositionHistory(int timestamp, Object source) { + positionHistory.clear(); + firePropertyChanged(RescueConstants.PROPERTY_POSITION_HISTORY,timestamp,source); + } + + public void appendPositionHistory(int next, int timestamp, Object source) { + positionHistory.append(next); + firePropertyChanged(RescueConstants.PROPERTY_POSITION_HISTORY,timestamp,source); + } +} diff --git a/modules/oldsims/rescuecore/objects/Node.java b/modules/oldsims/rescuecore/objects/Node.java new file mode 100644 index 0000000000000000000000000000000000000000..458ea0fa28dd60262c441967bfd0977e23f546fd --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Node.java @@ -0,0 +1,156 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_NODE object + @see RescueConstants#TYPE_NODE + */ + +public class Node extends Vertex { + private IntProperty signal; + private ArrayProperty shortcut, pocket, timing; + + public Node() { + signal = new IntProperty(RescueConstants.PROPERTY_SIGNAL); + shortcut = new ArrayProperty(RescueConstants.PROPERTY_SHORTCUT_TO_TURN); + pocket = new ArrayProperty(RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS); + timing = new ArrayProperty(RescueConstants.PROPERTY_SIGNAL_TIMING); + } + + public Node(int x, int y) { + this(x,y,new int[0],false,new int[0], new int[0], new int[0]); + } + + public Node(int x, int y, int[] edges, boolean signal, int[] shortcut, int[] pocket, int[] timing){ + super(x,y,edges); + this.signal = new IntProperty(RescueConstants.PROPERTY_SIGNAL,signal); + this.shortcut = new ArrayProperty(RescueConstants.PROPERTY_SHORTCUT_TO_TURN,shortcut); + this.pocket = new ArrayProperty(RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS,pocket); + this.timing = new ArrayProperty(RescueConstants.PROPERTY_SIGNAL_TIMING,timing); + } + + public int getType() { + return RescueConstants.TYPE_NODE; + } + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_SIGNAL: + return signal; + case RescueConstants.PROPERTY_SHORTCUT_TO_TURN: + return shortcut; + case RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS: + return pocket; + case RescueConstants.PROPERTY_SIGNAL_TIMING: + return timing; + } + return super.getProperty(property); + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_SIGNAL: + case RescueConstants.PROPERTY_SHORTCUT_TO_TURN: + case RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS: + case RescueConstants.PROPERTY_SIGNAL_TIMING: + return true; + } + return super.propertyExists(property); + } + */ + + public boolean hasSignal() { + return signal.getValue()!=0; + } + + public boolean setSignal(boolean b, int timestamp, Object source) { + if (signal.updateValue(b?1:0,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_SIGNAL,timestamp,source); + return true; + } + return false; + } + + public int[] getShortcutToTurn() { + return shortcut.getValues(); + } + + public boolean setShortcutToTurn(int[] s, int timestamp, Object source) { + if (shortcut.updateValues(s,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_SHORTCUT_TO_TURN,timestamp,source); + return true; + } + return false; + } + + public void clearShortcutToTurn(int timestamp, Object source) { + shortcut.clear(); + firePropertyChanged(RescueConstants.PROPERTY_SHORTCUT_TO_TURN,timestamp,source); + } + + public void appendShortcutToTurn(int next, int timestamp, Object source) { + shortcut.append(next); + firePropertyChanged(RescueConstants.PROPERTY_SHORTCUT_TO_TURN,timestamp,source); + } + + public int[] getPocketToTurnAcross() { + return pocket.getValues(); + } + + public boolean setPocketToTurnAcross(int[] p, int timestamp, Object source) { + if (pocket.updateValues(p,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS,timestamp,source); + return true; + } + return false; + } + + public void clearPocketToTurnAcross(int timestamp, Object source) { + pocket.clear(); + firePropertyChanged(RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS,timestamp,source); + } + + public void appendPocketToTurnAcross(int next, int timestamp, Object source) { + pocket.append(next); + firePropertyChanged(RescueConstants.PROPERTY_POCKET_TO_TURN_ACROSS,timestamp,source); + } + + public int[] getSignalTiming() { + return timing.getValues(); + } + + public boolean setSignalTiming(int[] t, int timestamp, Object source) { + if (timing.updateValues(t,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_SIGNAL_TIMING,timestamp,source); + return true; + } + return false; + } + + public void clearSignalTiming(int timestamp, Object source) { + timing.clear(); + firePropertyChanged(RescueConstants.PROPERTY_SIGNAL_TIMING,timestamp,source); + } + + public void appendSignalTiming(int next, int timestamp, Object source) { + timing.append(next); + firePropertyChanged(RescueConstants.PROPERTY_SIGNAL_TIMING,timestamp,source); + } +} diff --git a/modules/oldsims/rescuecore/objects/PoliceForce.java b/modules/oldsims/rescuecore/objects/PoliceForce.java new file mode 100644 index 0000000000000000000000000000000000000000..712a025329bdd96c087885bd481f151aad9962d9 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/PoliceForce.java @@ -0,0 +1,37 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_POLICE_FORCE object + @see RescueConstants#TYPE_POLICE_FORCE + */ +public class PoliceForce extends Humanoid { + public PoliceForce() { + super(); + } + + public PoliceForce(int pos, int extra, int dir, int[] history, int stam, int health, int dmg, int bury) { + super(pos,extra,dir,history,stam,health,dmg,bury); + } + + public int getType() { + return RescueConstants.TYPE_POLICE_FORCE; + } +} diff --git a/modules/oldsims/rescuecore/objects/PoliceOffice.java b/modules/oldsims/rescuecore/objects/PoliceOffice.java new file mode 100644 index 0000000000000000000000000000000000000000..f0c129ef0b989b75984c68d5f4a51bce57227e83 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/PoliceOffice.java @@ -0,0 +1,41 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_POLICE_OFFICE object + @see RescueConstants#TYPE_POLICE_OFFICE + */ +public class PoliceOffice extends Building { + public PoliceOffice() { + super(); + } + + public PoliceOffice(int x, int y, int floors, int attributes, boolean ignition, int fieryness, int brokenness, int[] entrances, int code, int ground, int total, int[] apexes, int temperature, int importance) { + super(x,y,floors,attributes,ignition,fieryness,brokenness,entrances,code,ground,total,apexes,temperature,importance); + } + + public PoliceOffice(Building other) { + super(other); + } + + public int getType() { + return RescueConstants.TYPE_POLICE_OFFICE; + } +} diff --git a/modules/oldsims/rescuecore/objects/RealObject.java b/modules/oldsims/rescuecore/objects/RealObject.java new file mode 100644 index 0000000000000000000000000000000000000000..11561ad465099cbebbd955f397046596b2c7d632 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/RealObject.java @@ -0,0 +1,32 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class RealObject extends RescueObject { + protected RealObject() { + super(); + } + + // public boolean propertyExists(int property) { + // return super.propertyExists(property); + // } + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + return super.getProperty(property); + } +} diff --git a/modules/oldsims/rescuecore/objects/Refuge.java b/modules/oldsims/rescuecore/objects/Refuge.java new file mode 100644 index 0000000000000000000000000000000000000000..a3d133fff77a71306aa841c52e0f3604129f6f3d --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Refuge.java @@ -0,0 +1,41 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.RescueObject; +import rescuecore.RescueConstants; + +/** + Encapsulation of a TYPE_REFUGE object + @see RescueConstants#TYPE_REFUGE + */ +public class Refuge extends Building { + public Refuge() { + super(); + } + + public Refuge(Building b) { + super(b); + } + + public Refuge(int x, int y, int floors, int attributes, boolean ignition, int fieryness, int brokenness, int[] entrances, int code, int ground, int total, int[] apexes, int temperature, int importance) { + super(x,y,floors,attributes,ignition,fieryness,brokenness,entrances,code,ground,total,apexes,temperature,importance); + } + + public int getType() { + return RescueConstants.TYPE_REFUGE; + } +} diff --git a/modules/oldsims/rescuecore/objects/River.java b/modules/oldsims/rescuecore/objects/River.java new file mode 100644 index 0000000000000000000000000000000000000000..1635796db17cc240a3a13922473068b89ed4b56d --- /dev/null +++ b/modules/oldsims/rescuecore/objects/River.java @@ -0,0 +1,45 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_RIVER object + @see RescueConstants#TYPE_RIVER + */ +public class River extends Edge { + public River() { + } + + public River(int head, int tail, int length) { + super(head,tail,length); + } + + public int getType() { + return RescueConstants.TYPE_RIVER; + } + + /* + public boolean propertyExists(int property) { + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + return super.getProperty(property); + } +} diff --git a/modules/oldsims/rescuecore/objects/RiverNode.java b/modules/oldsims/rescuecore/objects/RiverNode.java new file mode 100644 index 0000000000000000000000000000000000000000..e0663fb49cda5075d0b1366f9be41f4971397b2d --- /dev/null +++ b/modules/oldsims/rescuecore/objects/RiverNode.java @@ -0,0 +1,46 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_RIVER_NODE object + @see RescueConstants#TYPE_RIVER_NODE + */ + +public class RiverNode extends Vertex { + public RiverNode() { + } + + public RiverNode(int x, int y, int[] edges) { + super(x,y,edges); + } + + public int getType() { + return RescueConstants.TYPE_RIVER_NODE; + } + + /* + public boolean propertyExists(int property) { + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + return super.getProperty(property); + } +} diff --git a/modules/oldsims/rescuecore/objects/Road.java b/modules/oldsims/rescuecore/objects/Road.java new file mode 100644 index 0000000000000000000000000000000000000000..263fc69a8096ac6f38c9db6e3ab8f75f57aa1f9b --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Road.java @@ -0,0 +1,295 @@ +/* + * Last change: $Date: 2005/02/17 02:25:38 $ + * $Revision: 1.10 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_ROAD object + @see RescueConstants#TYPE_ROAD + */ + +public class Road extends Edge { + private IntProperty kind, carsToHead, carsToTail, humansToHead, humansToTail, width, block, cost, median, linesToHead, linesToTail, widthForWalkers; + + // private boolean hasFreeLines; + // private int previousBlock; + + public Road() { + kind = new IntProperty(RescueConstants.PROPERTY_ROAD_KIND); + carsToHead = new IntProperty(RescueConstants.PROPERTY_CARS_PASS_TO_HEAD); + carsToTail = new IntProperty(RescueConstants.PROPERTY_CARS_PASS_TO_TAIL); + humansToHead = new IntProperty(RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD); + humansToTail = new IntProperty(RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL); + width = new IntProperty(RescueConstants.PROPERTY_WIDTH); + block = new IntProperty(RescueConstants.PROPERTY_BLOCK); + cost = new IntProperty(RescueConstants.PROPERTY_REPAIR_COST); + median = new IntProperty(RescueConstants.PROPERTY_MEDIAN_STRIP); + linesToHead = new IntProperty(RescueConstants.PROPERTY_LINES_TO_HEAD); + linesToTail = new IntProperty(RescueConstants.PROPERTY_LINES_TO_TAIL); + widthForWalkers = new IntProperty(RescueConstants.PROPERTY_WIDTH_FOR_WALKERS); + // previousBlock = -1; + } + + public Road(int head, int tail, int length, int kind, int cth, int ctt, int hth, int htt, int width, int block, int cost, boolean median, int lth, int ltt, int wfw) { + super(head,tail,length); + this.kind = new IntProperty(RescueConstants.PROPERTY_ROAD_KIND,kind); + this.carsToHead = new IntProperty(RescueConstants.PROPERTY_CARS_PASS_TO_HEAD,cth); + this.carsToTail = new IntProperty(RescueConstants.PROPERTY_CARS_PASS_TO_TAIL,ctt); + this.humansToHead = new IntProperty(RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD,hth); + this.humansToTail = new IntProperty(RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL,htt); + this.width = new IntProperty(RescueConstants.PROPERTY_WIDTH,width); + this.block = new IntProperty(RescueConstants.PROPERTY_BLOCK,block); + this.cost = new IntProperty(RescueConstants.PROPERTY_REPAIR_COST,cost); + this.median = new IntProperty(RescueConstants.PROPERTY_MEDIAN_STRIP,median); + this.linesToHead = new IntProperty(RescueConstants.PROPERTY_LINES_TO_HEAD,lth); + this.linesToTail = new IntProperty(RescueConstants.PROPERTY_LINES_TO_TAIL,ltt); + this.widthForWalkers = new IntProperty(RescueConstants.PROPERTY_WIDTH_FOR_WALKERS,wfw); + // previousBlock = -1; + } + + public int getType() { + return RescueConstants.TYPE_ROAD; + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_ROAD_KIND: + case RescueConstants.PROPERTY_CARS_PASS_TO_HEAD: + case RescueConstants.PROPERTY_CARS_PASS_TO_TAIL: + case RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD: + case RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL: + case RescueConstants.PROPERTY_WIDTH: + case RescueConstants.PROPERTY_BLOCK: + case RescueConstants.PROPERTY_REPAIR_COST: + case RescueConstants.PROPERTY_MEDIAN_STRIP: + case RescueConstants.PROPERTY_LINES_TO_HEAD: + case RescueConstants.PROPERTY_LINES_TO_TAIL: + case RescueConstants.PROPERTY_WIDTH_FOR_WALKERS: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_ROAD_KIND: + return kind; + case RescueConstants.PROPERTY_CARS_PASS_TO_HEAD: + return carsToHead; + case RescueConstants.PROPERTY_CARS_PASS_TO_TAIL: + return carsToTail; + case RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD: + return humansToHead; + case RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL: + return humansToTail; + case RescueConstants.PROPERTY_WIDTH: + return width; + case RescueConstants.PROPERTY_BLOCK: + return block; + case RescueConstants.PROPERTY_REPAIR_COST: + return cost; + case RescueConstants.PROPERTY_MEDIAN_STRIP: + return median; + case RescueConstants.PROPERTY_LINES_TO_HEAD: + return linesToHead; + case RescueConstants.PROPERTY_LINES_TO_TAIL: + return linesToTail; + case RescueConstants.PROPERTY_WIDTH_FOR_WALKERS: + return widthForWalkers; + } + return super.getProperty(property); + } + + public int getRoadKind() { + return kind.getValue(); + } + + public boolean setRoadKind(int k, int timestamp, Object source) { + if (kind.updateValue(k,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_ROAD_KIND,timestamp,source); + return true; + } + return false; + } + + public int getCarsPassToHead() { + return carsToHead.getValue(); + } + + public boolean setCarsPassToHead(int cars, int timestamp, Object source) { + if (carsToHead.updateValue(cars,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_CARS_PASS_TO_HEAD,timestamp,source); + return true; + } + return false; + } + + public int getCarsPassToTail() { + return carsToTail.getValue(); + } + + public boolean setCarsPassToTail(int cars, int timestamp, Object source) { + if (carsToTail.updateValue(cars,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_CARS_PASS_TO_TAIL,timestamp,source); + return true; + } + return false; + } + + public int getHumansPassToHead() { + return humansToHead.getValue(); + } + + public boolean setHumansPassToHead(int h, int timestamp, Object source) { + if (humansToHead.updateValue(h,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_HUMANS_PASS_TO_HEAD,timestamp,source); + return true; + } + return false; + } + + public int getHumansPassToTail() { + return humansToTail.getValue(); + } + + public boolean setHumansPassToTail(int h, int timestamp, Object source) { + if (humansToTail.updateValue(h,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_HUMANS_PASS_TO_TAIL,timestamp,source); + return true; + } + return false; + } + + public int getWidth() { + return width.getValue(); + } + + public boolean setWidth(int w, int timestamp, Object source) { + if (width.updateValue(w,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_WIDTH,timestamp,source); + return true; + } + return false; + } + + public int getBlock() { + return block.getValue(); + } + + public boolean setBlock(int b, int timestamp, Object source) { + if (block.updateValue(b,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_BLOCK,timestamp,source); + return true; + } + return false; + } + + public int getRepairCost() { + return cost.getValue(); + } + + public boolean setRepairCost(int c, int timestamp, Object source) { + if (cost.updateValue(c,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_REPAIR_COST,timestamp,source); + return true; + } + return false; + } + + public boolean hasMedian() { + return median.getValue()!=0; + } + + public boolean setMedian(boolean b, int timestamp, Object source) { + if (median.updateValue(b?1:0,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_MEDIAN_STRIP,timestamp,source); + return true; + } + return false; + } + + public int getLinesToHead() { + return linesToHead.getValue(); + } + + public boolean setLinesToHead(int l, int timestamp, Object source) { + if (linesToHead.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_LINES_TO_HEAD,timestamp,source); + return true; + } + return false; + } + + public int getLinesToTail() { + return linesToTail.getValue(); + } + + public boolean setLinesToTail(int l, int timestamp, Object source) { + if (linesToTail.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_LINES_TO_TAIL,timestamp,source); + return true; + } + return false; + } + + public int getWidthForWalkers() { + return widthForWalkers.getValue(); + } + + public boolean setWidthForWalkers(int w, int timestamp, Object source) { + if (widthForWalkers.updateValue(w,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_WIDTH_FOR_WALKERS,timestamp,source); + return true; + } + return false; + } + + public boolean isBlocked() { + return getBlock() > 0; + } + + public boolean hasBlockedLines() { + return getBlockedLines()>0; + } + + public boolean hasFreeLines() { + return getFreeLinesToHead()>0 && getFreeLinesToTail()>0; + } + + public int getFreeLinesToHead() { + double lanes = getLinesToHead()+getLinesToTail(); + double laneWidth = getWidth()/lanes; + int blockedLanes = (int)Math.floor(getBlock()/laneWidth/2.0 + 0.5); + return getLinesToHead() - blockedLanes; + } + + public int getFreeLinesToTail() { + double lanes = getLinesToHead()+getLinesToTail(); + double laneWidth = getWidth()/lanes; + int blockedLanes = (int)Math.floor(getBlock()/laneWidth/2.0 + 0.5); + return getLinesToTail() - blockedLanes; + } + + public int getBlockedLines() { + double lanes = getLinesToHead()+getLinesToTail(); + double laneWidth = getWidth()/lanes; + int blockedLanes = (int)Math.floor(getBlock()/laneWidth/2.0 + 0.5); + return blockedLanes*2; + } +} diff --git a/modules/oldsims/rescuecore/objects/Vertex.java b/modules/oldsims/rescuecore/objects/Vertex.java new file mode 100644 index 0000000000000000000000000000000000000000..ac56822bbbb82782df3f903c4917e0a6d60ea4f4 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/Vertex.java @@ -0,0 +1,106 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class Vertex extends MotionlessObject { + protected IntProperty x,y; + protected ArrayProperty edges; + + protected Vertex() { + x = new IntProperty(RescueConstants.PROPERTY_X); + y = new IntProperty(RescueConstants.PROPERTY_Y); + edges = new ArrayProperty(RescueConstants.PROPERTY_EDGES); + } + + protected Vertex(int x, int y, int[] edges) { + this.x = new IntProperty(RescueConstants.PROPERTY_X,x); + this.y = new IntProperty(RescueConstants.PROPERTY_Y,y); + this.edges = new ArrayProperty(RescueConstants.PROPERTY_EDGES,edges); + } + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_X: + return x; + case RescueConstants.PROPERTY_Y: + return y; + case RescueConstants.PROPERTY_EDGES: + return edges; + } + return super.getProperty(property); + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_X: + case RescueConstants.PROPERTY_Y: + case RescueConstants.PROPERTY_EDGES: + return true; + } + return super.propertyExists(property); + } + */ + + + public int getX() { + return x.getValue(); + } + + public boolean setX(int newX, int timestamp, Object source) { + if (x.updateValue(newX,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_X,timestamp,source); + return true; + } + return false; + } + + public int getY() { + return y.getValue(); + } + + public boolean setY(int newY, int timestamp, Object source) { + if (y.updateValue(newY,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_Y,timestamp,source); + return true; + } + return false; + } + + public int[] getEdges() { + return edges.getValues(); + } + + public boolean setEdges(int[] newEdges, int timestamp, Object source) { + if (edges.updateValues(newEdges,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_EDGES,timestamp,source); + return true; + } + return false; + } + + public void appendEdge(int next, int timestamp, Object source) { + edges.append(next); + firePropertyChanged(RescueConstants.PROPERTY_EDGES,timestamp,source); + } + + public void clearEdges(int timestamp, Object source) { + edges.clear(); + firePropertyChanged(RescueConstants.PROPERTY_EDGES,timestamp,source); + } +} diff --git a/modules/oldsims/rescuecore/objects/VirtualObject.java b/modules/oldsims/rescuecore/objects/VirtualObject.java new file mode 100644 index 0000000000000000000000000000000000000000..77ef8b6cc5d411f0f101e9c1dfb12ac22d4ab7ec --- /dev/null +++ b/modules/oldsims/rescuecore/objects/VirtualObject.java @@ -0,0 +1,32 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +public abstract class VirtualObject extends RescueObject { + protected VirtualObject() { + super(); + } + + // public boolean propertyExists(int property) { + // return super.propertyExists(property); + // } + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + return super.getProperty(property); + } +} diff --git a/modules/oldsims/rescuecore/objects/World.java b/modules/oldsims/rescuecore/objects/World.java new file mode 100644 index 0000000000000000000000000000000000000000..342e2e138fd898d05db0371622231b4fc5917810 --- /dev/null +++ b/modules/oldsims/rescuecore/objects/World.java @@ -0,0 +1,137 @@ +/* + * Last change: $Date: 2004/05/20 23:42:00 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.objects; + +import rescuecore.*; + +/** + Encapsulation of a TYPE_WORLD object + @see RescueConstants#TYPE_WORLD + */ + +public class World extends VirtualObject { + private IntProperty startTime, longitude, latitude, windForce, windDirection; + + public World() { + startTime = new IntProperty(RescueConstants.PROPERTY_START_TIME); + longitude = new IntProperty(RescueConstants.PROPERTY_LONGITUDE); + latitude = new IntProperty(RescueConstants.PROPERTY_LATITUDE); + windForce = new IntProperty(RescueConstants.PROPERTY_WIND_FORCE); + windDirection = new IntProperty(RescueConstants.PROPERTY_WIND_DIRECTION); + } + + public World(int start, int lon, int lat, int force, int direction) { + startTime = new IntProperty(RescueConstants.PROPERTY_START_TIME,start); + longitude = new IntProperty(RescueConstants.PROPERTY_LONGITUDE,lon); + latitude = new IntProperty(RescueConstants.PROPERTY_LATITUDE,lat); + windForce = new IntProperty(RescueConstants.PROPERTY_WIND_FORCE,force); + windDirection = new IntProperty(RescueConstants.PROPERTY_WIND_DIRECTION,direction); + } + + public int getType() { + return RescueConstants.TYPE_WORLD; + } + + /* + public boolean propertyExists(int property) { + switch (property) { + case RescueConstants.PROPERTY_START_TIME: + case RescueConstants.PROPERTY_LONGITUDE: + case RescueConstants.PROPERTY_LATITUDE: + case RescueConstants.PROPERTY_WIND_FORCE: + case RescueConstants.PROPERTY_WIND_DIRECTION: + return true; + } + return super.propertyExists(property); + } + */ + + public Property getProperty(int property) /*throws UnknownPropertyException*/ { + switch (property) { + case RescueConstants.PROPERTY_START_TIME: + return startTime; + case RescueConstants.PROPERTY_LONGITUDE: + return longitude; + case RescueConstants.PROPERTY_LATITUDE: + return latitude; + case RescueConstants.PROPERTY_WIND_FORCE: + return windForce; + case RescueConstants.PROPERTY_WIND_DIRECTION: + return windDirection; + } + return super.getProperty(property); + } + + public int getStartTime() { + return startTime.getValue(); + } + + public boolean setStartTime(int s, int timestamp, Object source) { + if (startTime.updateValue(s,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_START_TIME,timestamp,source); + return true; + } + return false; + } + + public int getLongitude() { + return longitude.getValue(); + } + + public boolean setLongitude(int l, int timestamp, Object source) { + if (longitude.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_LONGITUDE,timestamp,source); + return true; + } + return false; + } + + public int getLatitude() { + return latitude.getValue(); + } + + public boolean setLatitude(int l, int timestamp, Object source) { + if (latitude.updateValue(l,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_LATITUDE,timestamp,source); + return true; + } + return false; + } + + public int getWindForce() { + return windForce.getValue(); + } + + public boolean setWindForce(int w, int timestamp, Object source) { + if (windForce.updateValue(w,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_WIND_FORCE,timestamp,source); + return true; + } + return false; + } + + public int getWindDirection() { + return windDirection.getValue(); + } + + public boolean setWindDirection(int w, int timestamp, Object source) { + if (windDirection.updateValue(w,timestamp,source)) { + firePropertyChanged(RescueConstants.PROPERTY_WIND_DIRECTION,timestamp,source); + return true; + } + return false; + } +} diff --git a/modules/oldsims/rescuecore/sample/SampleAmbulanceTeam.java b/modules/oldsims/rescuecore/sample/SampleAmbulanceTeam.java new file mode 100644 index 0000000000000000000000000000000000000000..573ae13359a261677b4feb01cff282a571b4b46f --- /dev/null +++ b/modules/oldsims/rescuecore/sample/SampleAmbulanceTeam.java @@ -0,0 +1,172 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.sample; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.event.*; +import rescuecore.commands.AKChannel; +import java.util.*; + +/** + This is a sample implementation of an ambulance team. This agent will attempt to rescue the closest humanoid it knows about. If it doesn't know anything then it moves randomly. +*/ +public class SampleAmbulanceTeam extends PlatoonAgent { + private final static byte CHANNEL = 3; + + /** + A list of known targets + */ + private List<Humanoid> targets; + + /** + Construct a new SampleAmbulanceTeam + */ + public SampleAmbulanceTeam() { + super(RescueConstants.TYPE_AMBULANCE_TEAM); // We need to specify that we can only be an ambulance team + targets = new ArrayList<Humanoid>(); + } + + /** + Get a reference the the AmbulanceTeam controlled by this agent + @return the AmbulanceTeam controlled by this agent + */ + private AmbulanceTeam me() { + return (AmbulanceTeam)memory.lookup(id); + } + + public void initialise(RescueObject[] knowledge) { + // Add a memory listener so that we get informed about changes to humanoids + super.initialise(knowledge); + memory.addMemoryListener(new MemoryListener() { + public void objectAdded(ObjectAddedEvent event) { + RescueObject o = event.getObject(); + if (o.isHumanoid()) { + Humanoid h = (Humanoid)o; + // If the object is a buried humanoid, and we know the position of the humanoid, then add it to our target list + if ((h.isBuried() || h.isDamaged()) && memory.lookup(h.getPosition())!=null) { + if (!targets.contains(h)) { + targets.add(h); + // System.out.println(SampleAmbulanceTeam.this+" adding new target "+h); + } + } + } + } + public void objectChanged(ObjectChangedEvent event) { + RescueObject o = event.getObject(); + if (o.isHumanoid()) { + Humanoid h = (Humanoid)o; + // If the object is a buried humanoid, and we know the position of the humanoid, then add it to our target list + if ((h.isBuried() || h.isDamaged()) && memory.lookup(h.getPosition())!=null) { + if (!targets.contains(h)) { + targets.add(h); + // System.out.println(SampleAmbulanceTeam.this+" adding changed target "+h); + } + } + else targets.remove(h); // Otherwise remove it + } + } + }); + } + + public void sense() { + // Is this the first timestep? + if (timeStep==1) { + // Listen on the right channel + appendCommand(new AKChannel(id,timeStep,CHANNEL)); + } + SampleSearch.sortByDistance(targets,me(),memory); + for (Humanoid next : targets) { + if (next==me()) continue; // Ignore me + // Am I transporting someone to a refuge? + if (next.getPosition()==id) { + // Am I at a refuge? + if (getLocation() instanceof Refuge) { + unload(); + tell("Unloading".getBytes(),CHANNEL); + // System.out.println(this+" unloading"); + return; + } + else { + // Plan a path to a refuge + List<RescueObject> refuges = new ArrayList<RescueObject>(); + memory.getObjectsOfType(refuges,RescueConstants.TYPE_REFUGE); + SampleSearch.sortByDistance(refuges,me(),memory); + int[] path = SampleSearch.breadthFirstSearch(getLocation(),refuges.iterator().next(),memory); + move(path); + // System.out.println(this+" heading for refuge"); + return; + } + } + // Am I at the same location as this target (and not at a refuge)? + if (!(getLocation() instanceof Refuge) && next.getPosition()==getPosition()) { + // System.out.println(this+" at same position as "+next); + // System.out.println(this+" buriedness = "+next.getBuriedness()+", damage="+next.getDamage()); + if (next.isBuried()) { + // Yes! Dig him out + // System.out.println(this+" rescueing "+next); + rescue(next); + tell(("Rescueing "+next.getID()).getBytes(),CHANNEL); + return; + } + else if (next.isDamaged()) { + // Load him + System.out.println(this+" loading "+next); + load(next); + tell(("Loading "+next.getID()).getBytes(),CHANNEL); + return; + } + } + } + // Try to plan a path to the next best target + for (Iterator<Humanoid> it = targets.iterator();it.hasNext();) { + Humanoid next = it.next(); + RescueObject targetLocation = memory.lookup(next.getPosition()); + if (!next.isBuried() || targetLocation==null) { + // If the target is not buried or we don't know its location then remove it from our target list + it.remove(); + continue; + } + int[] path = SampleSearch.breadthFirstSearch(getLocation(),targetLocation,memory); // Find a path from my current location to the target's location + if (path!=null) { + // We've found a path. Hooray! + // Send a move command and we're finished + // System.out.println(this+" moving to "+next); + move(path); + return; + } + } + // We couldn't find a good target. Pick a random road and try moving there instead + Collection<RescueObject> allRoads = memory.getObjectsOfType(RescueConstants.TYPE_ROAD); // Find all roads + Road[] roads = new Road[allRoads.size()]; + allRoads.toArray(roads); + Road target = (Road)roads[(int)(Math.random()*roads.length)]; // Pick one at random + // Plan a path + int[] path = SampleSearch.breadthFirstSearch(getLocation(),target,memory); + if (path!=null) { + // System.out.println(this+" moving randomly"); + move(path); // Move if the path is valid + } + else { + // If we couldn't move randomly then just give up + // System.out.println(this+" giving up"); + } + } + + protected void hear(int from, byte[] msg, byte channel) { + // System.out.println("Received message from "+from+": "+String.valueOf(msg)); + } +} diff --git a/modules/oldsims/rescuecore/sample/SampleCenter.java b/modules/oldsims/rescuecore/sample/SampleCenter.java new file mode 100644 index 0000000000000000000000000000000000000000..51fce408acac48b3d29a56e872cee6b9acb97363 --- /dev/null +++ b/modules/oldsims/rescuecore/sample/SampleCenter.java @@ -0,0 +1,110 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.sample; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.commands.AKChannel; + +/** + This is a sample implementation of a center agent. All this agent does is pass on messages. +*/ +public class SampleCenter extends CenterAgent { + private byte agentChannel; + + /** + Construct a new SampleCenter + */ + public SampleCenter() { + super(RescueConstants.TYPE_FIRE_STATION, RescueConstants.TYPE_AMBULANCE_CENTER, RescueConstants.TYPE_POLICE_OFFICE); + agentChannel = 0; + } + + /** + Get a reference the the Building controlled by this agent + @return the Building controlled by this agent + */ + private Building me() { + return (Building)memory.lookup(id); + } + + public void initialise(RescueObject[] knowledge) { + super.initialise(knowledge); + switch (type) { + case RescueConstants.TYPE_FIRE_STATION: + agentChannel = 1; + break; + case RescueConstants.TYPE_POLICE_OFFICE: + agentChannel = 2; + break; + case RescueConstants.TYPE_AMBULANCE_CENTER: + agentChannel = 3; + break; + } + // log("Initialised"); // Log a debugging message + } + + public void sense() { + // Is this the first timestep? + if (timeStep==1) { + // Listen on the right channels + byte[] channels = new byte[2]; + // All centers listen on channel 4 + channels[0] = 4; + channels[1] = agentChannel; + appendCommand(new AKChannel(id,timeStep,channels)); + } + } + + protected void hear(int from, byte[] msg, byte channel) { + // System.out.println("Received message from "+from+" on channel "+channel+" : "+String.valueOf(msg)); + // Pass the message through to the other channel + if (channel==4) tell(msg,agentChannel); + if (channel==agentChannel) tell(msg,(byte)4); + } + + /** + Find out if an id represents one of my platoon agents + @param id The id to test + @return true if the RescueObject specified by the id is one of my platoon agents, false otherwise + */ + private boolean isPlatoon(int id) { + int agentType = memory.lookup(id).getType(); // What is the type of the agent we heard the message from? + switch (type) { // What is my type? + case RescueConstants.TYPE_FIRE_STATION: + // I'm a fire station, so my platoon agents are fire brigades + return agentType==RescueConstants.TYPE_FIRE_BRIGADE; + case RescueConstants.TYPE_POLICE_OFFICE: + // I'm a police office, so my platoon agents are police forces + return agentType==RescueConstants.TYPE_POLICE_FORCE; + case RescueConstants.TYPE_AMBULANCE_CENTER: + // I'm an ambulance center, so my platoon agents are ambulance teams + return agentType==RescueConstants.TYPE_AMBULANCE_TEAM; + } + throw new RuntimeException("Weird type for "+this+": "+Handy.getTypeName(type)); + } + + /** + Find out if an id represents one of the centers + @param id The id to test + @return true if the RescueObject specified by the id is one of the centers + */ + private boolean isCenter(int id) { + RescueObject target = memory.lookup(id); + if (target.isFireStation() || target.isAmbulanceCenter() || target.isPoliceOffice()) return true; // Centers are fire stations, police offices and ambulance centers + return false; + } +} diff --git a/modules/oldsims/rescuecore/sample/SampleFireBrigade.java b/modules/oldsims/rescuecore/sample/SampleFireBrigade.java new file mode 100644 index 0000000000000000000000000000000000000000..0592b38dcc76955dc51a829f8149e8a286aca3ef --- /dev/null +++ b/modules/oldsims/rescuecore/sample/SampleFireBrigade.java @@ -0,0 +1,192 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.sample; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.event.*; +import rescuecore.commands.AKChannel; +import java.util.*; + +/** + This is a sample implementation of a fire brigade. This agent will attempt to extinguish the closest fire it knows about. If it doesn't know anything then it moves randomly. +*/ +public class SampleFireBrigade extends PlatoonAgent { + private final static byte CHANNEL = 1; + + private boolean enableDebug; + + /** + A list of known targets + */ + private List targets; + + /** + Construct a new SampleFireBrigade + */ + public SampleFireBrigade() { + super(RescueConstants.TYPE_FIRE_BRIGADE); + targets = new ArrayList(); + enableDebug = false; + } + + public SampleFireBrigade(String[] args) { + this(); + for (String next : args) { + if (next.equalsIgnoreCase("debug")) { + enableDebug = true; + } + } + } + + /** + Get a reference to the FireBrigade controlled by this agent + @return the FireBrigade controlled by this agent + */ + private FireBrigade me() { + return (FireBrigade)memory.lookup(id); + } + + public void initialise(RescueObject[] knowledge) { + if (enableDebug) { + try { + enableDebug("debug.log"); + } + catch (Exception e) { + e.printStackTrace(); + } + } + super.initialise(knowledge); + // Add a memory listener so that we get informed about changes to the buildings + memory.addMemoryListener(new MemoryListener() { + public void objectAdded(ObjectAddedEvent event) { + RescueObject o = event.getObject(); + if (o.isBuilding()) { + if (((Building)o).isOnFire()) targets.add(o); // Add to target list if it is on fire + } + } + public void objectChanged(ObjectChangedEvent event) { + RescueObject o = event.getObject(); + if (o.isBuilding() && event.getProperty()==RescueConstants.PROPERTY_FIERYNESS) { // We only care about the fieryness of the building - we can ignore all other updates + if (((Building)o).isOnFire()) targets.add(o); // Add to target list if it is on fire + else targets.remove(o); // Otherwise remove it from the target list + } + } + }); + // log("Initialised"); // Log a debugging message + } + + public void sense() { + // Is this the first timestep? + if (timeStep==1) { + // Listen on the right channel + appendCommand(new AKChannel(id,timeStep,CHANNEL)); + } + + // System.out.println("Fire brigade "+id+" water quantity: "+me().getWaterQuantity()); + // log("Sense"); // Log a debugging message + // Am I at a refuge? + if (getLocation().isRefuge() && me().getWaterQuantity()<RescueConstants.MAX_WATER) { + // log("Filling at "+getLocation()); + return; + } + // Am I out of water? + if (me().getWaterQuantity()==0) { + // Find the closest refuge + moveToClosestRefuge(); + return; + } + // Sort the targets by distance from me + // log("Sorting targets"); + SampleSearch.sortByDistance(targets,me(),memory); + // Find a valid target + for (Iterator it = targets.iterator();it.hasNext();) { + // Try to plan a path to the next best target + Building next = (Building)it.next(); + if (!next.isOnFire()) { + // If the next target is not on fire then remove it from our target list + it.remove(); + continue; + } + try { + if (memory.getDistance(me(),next) < RescueConstants.MAX_EXTINGUISH_DISTANCE) { + // System.out.println("Extinguishing "+next); + extinguish(next); + tell(("Extinguishing "+next.getID()).getBytes(),CHANNEL); + return; + } + } + catch (CannotFindLocationException e) { + System.err.println("PANIC! I don't know where I am!"); + System.err.println(e); + } + Node entrance = (Node)memory.lookup(next.getEntrances()[0]); // Drive to the first entrance of the building + // log("Trying to plan path to "+next+" (entrance "+entrance+")"); + int[] path = SampleSearch.breadthFirstSearch(getLocation(),entrance,memory); // Find a path from my current location to the target + if (path!=null) { + // We've found a path. Hooray! + // Send a move command and we're finished + // log("Moving to "+next); + move(path); + return; + } + // log("No path"); + } + // We couldn't find a good target. Pick a random road and try moving there instead + // log("No good targets - picking a Road at random"); + Collection<RescueObject> allRoads = memory.getObjectsOfType(RescueConstants.TYPE_ROAD); // Find all roads + Road[] roads = new Road[allRoads.size()]; + allRoads.toArray(roads); + Road target = (Road)roads[(int)(Math.random()*roads.length)]; // Pick one at random + // log("Random target: "+target+". Planning path"); + // Plan a path + int[] path = SampleSearch.breadthFirstSearch(getLocation(),target,memory); + if (path!=null) { + // log("Moving to "+target); + move(path); // Move if the path is valid + } + else { + // If we couldn't move randomly then just give up + // log("I give up"); + } + } + + protected void hear(int from, byte[] msg, byte channel) { + // System.out.println("Received message from "+from+": "+String.valueOf(msg)); + } + + private void moveToClosestRefuge() { + // log("Moving to closest refuge"); + Collection<RescueObject> allRefuges = memory.getObjectsOfType(RescueConstants.TYPE_REFUGE); + Refuge[] refuges = new Refuge[allRefuges.size()]; + allRefuges.toArray(refuges); + SampleSearch.sortByDistance(refuges,me(),memory); + for (int i=0;i<refuges.length;++i) { + Refuge next = refuges[i]; + // log("Trying to plan path to "+next); + int[] path = SampleSearch.breadthFirstSearch(getLocation(),next,memory); // Find a path from my current location to the target + if (path!=null) { + // We've found a path. Hooray! + // Send a move command and we're finished + // log("Moving to "+next); + move(path); + return; + } + // log("No path"); + } + // log("Couldn't move to refuge"); + } +} diff --git a/modules/oldsims/rescuecore/sample/SamplePoliceForce.java b/modules/oldsims/rescuecore/sample/SamplePoliceForce.java new file mode 100644 index 0000000000000000000000000000000000000000..92771506952b2162b5fc33103005c1f8778becd6 --- /dev/null +++ b/modules/oldsims/rescuecore/sample/SamplePoliceForce.java @@ -0,0 +1,133 @@ +/* + * Last change: $Date: 2004/05/04 03:09:38 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.sample; + +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.event.*; +import rescuecore.commands.AKChannel; +import java.util.*; + +/** + This is a sample implementation of a police force. This agent will attempt to clear the closest blocked road it knows about. If it doesn't know anything then it moves randomly. +*/ +public class SamplePoliceForce extends PlatoonAgent { + private final static byte CHANNEL = 2; + + /** + A list of known targets + */ + private List targets; + + /** + Construct a new SamplePoliceForce + */ + public SamplePoliceForce() { + super(RescueConstants.TYPE_POLICE_FORCE); // We need to specify that we can only be a police force + targets = new ArrayList(); + } + + /** + Get a reference the the PoliceForce controlled by this agent + @return the PoliceForce controlled by this agent + */ + private PoliceForce me() { + return (PoliceForce)memory.lookup(id); + } + + public void initialise(RescueObject[] knowledge) { + // Add a memory listener so that we get informed about changes to the roads + super.initialise(knowledge); + memory.addMemoryListener(new MemoryListener() { + public void objectAdded(ObjectAddedEvent event) { + RescueObject o = event.getObject(); + if (o.isRoad()) { + if (((Road)o).isBlocked()) targets.add(o); // Add to target list if it is a blocked road + } + } + public void objectChanged(ObjectChangedEvent event) { + RescueObject o = event.getObject(); + if (o.isRoad() && event.getProperty()==RescueConstants.PROPERTY_BLOCK) { // We only care about the blockedness of the road - we can ignore all other updates + if (((Road)o).isBlocked()) targets.add(o); // Add to target list if it is a blocked road + else targets.remove(o); // Otherwise remove it from the target list + } + } + }); + // log("Initialised"); // Log a debugging message + } + + public void sense() { + // Is this the first timestep? + if (timeStep==1) { + // Listen on the right channel + appendCommand(new AKChannel(id,timeStep,CHANNEL)); + } + // log("Sense"); // Log a debugging message + // Am I on a blocked road? + RescueObject location = getLocation(); + if (location.isRoad() && ((Road)location).isBlocked()) { + // Yes. Clear it! + // log("Clearing "+location); + clear((Road)location); + tell(("Clearing "+location.getID()).getBytes(),CHANNEL); + return; + } + // Sort the targets by distance from me + // log("Sorting targets"); + SampleSearch.sortByDistance(targets,me(),memory); + // Find a valid target + for (Iterator it = targets.iterator();it.hasNext();) { + // Try to plan a path to the next best target + Road next = (Road)it.next(); + if (!next.isBlocked()) { + // If the next target is not blocked then remove it from our target list + it.remove(); + continue; + } + // log("Trying to plan path to "+next); + int[] path = SampleSearch.breadthFirstSearch(getLocation(),next,memory); // Find a path from my current location to the target + if (path!=null) { + // We've found a path. Hooray! + // Send a move command and we're finished + // log("Moving to "+next); + move(path); + return; + } + // log("No path"); + } + // We couldn't find a good target. Pick a random road and try moving there instead + // log("No good targets - picking a Road at random"); + Collection<RescueObject> allRoads = memory.getObjectsOfType(RescueConstants.TYPE_ROAD); // Find all roads + Road[] roads = new Road[allRoads.size()]; + allRoads.toArray(roads); + Road target = (Road)roads[(int)(Math.random()*roads.length)]; // Pick one at random + // log("Random target: "+target+". Planning path"); + // Plan a path + int[] path = SampleSearch.breadthFirstSearch(getLocation(),target,memory); + if (path!=null) { + // log("Moving to "+target); + move(path); // Move if the path is valid + } + else { + // If we couldn't move randomly then just give up + // log("I give up"); + } + } + + protected void hear(int from, byte[] msg, byte channel) { + // System.out.println("Received message from "+from+": "+String.valueOf(msg)); + } +} diff --git a/modules/oldsims/rescuecore/sample/SampleSearch.java b/modules/oldsims/rescuecore/sample/SampleSearch.java new file mode 100644 index 0000000000000000000000000000000000000000..49a2730a8dc01cdd139bfa141bf459e5884e0371 --- /dev/null +++ b/modules/oldsims/rescuecore/sample/SampleSearch.java @@ -0,0 +1,125 @@ +/* + * Last change: $Date: 2004/08/03 03:25:05 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.sample; + +import rescuecore.*; +import java.util.*; + +public class SampleSearch { + /** + Do a breadth first search from one location to another + @param start The location we start at + @param goal The location we want to get to + @param memory The memory of the agent doing the searching + @return The path from start to goal, or null if no path can be found + */ + public static int[] breadthFirstSearch(RescueObject start, RescueObject goal, Memory memory) { + List open = new LinkedList(); + Map ancestors = new HashMap(); + open.add(start); + RescueObject next = null; + do { + next = (RescueObject)open.remove(0); + RescueObject[] neighbours = memory.findNeighbours(next); + if (neighbours==null) continue; + for (int i=0;i<neighbours.length;++i) { + if (neighbours[i]==null) continue; + if (neighbours[i]==goal) { + ancestors.put(neighbours[i],next); + next = neighbours[i]; + break; + } + else { + if (!ancestors.containsKey(neighbours[i]) && !neighbours[i].isBuilding()) { + open.add(neighbours[i]); + ancestors.put(neighbours[i],next); + } + } + } + } while (next != goal && next != null); + if (next==null) { + // No path + return null; + } + // Walk back from goal to start + RescueObject current = goal; + Stack path = new Stack(); + do { + path.push(current); + current = (RescueObject)ancestors.get(current); + } while (current!=start && current!=null); + int[] result = new int[path.size()]; + for (int i=0;i<result.length;++i) { + result[i] = ((RescueObject)path.pop()).getID(); + } + return result; + } + + /** + Sort a list of RescueObjects by distance. This list will be sorted in place. + @param objects The objects to be sorted. When the method returns this list will be sorted. + @param reference The RescueObject to measure distances from + @param memory The memory of the agent doing the sorting + */ + public static void sortByDistance(List objects, RescueObject reference, Memory memory) { + synchronized(DISTANCE_SORTER) { + DISTANCE_SORTER.memory = memory; + DISTANCE_SORTER.reference = reference; + Collections.sort(objects,DISTANCE_SORTER); + } + } + + /** + Sort an array of RescueObjects by distance. This array will be sorted in place. + @param objects The objects to be sorted. When the method returns this array will be sorted. + @param reference The RescueObject to measure distances from + @param memory The memory of the agent doing the sorting + */ + public static void sortByDistance(RescueObject[] objects, RescueObject reference, Memory memory) { + synchronized(DISTANCE_SORTER) { + DISTANCE_SORTER.memory = memory; + DISTANCE_SORTER.reference = reference; + Arrays.sort(objects,DISTANCE_SORTER); + } + } + + /** + A Comparator for use when sorting RescueObjects by distance + */ + private static class DistanceSorter implements Comparator { + Memory memory; + RescueObject reference; + + public int compare(Object o1, Object o2) { + try { + double d1 = memory.getDistance(reference,(RescueObject)o1); + double d2 = memory.getDistance(reference,(RescueObject)o2); + if (d1 < d2) // Object o1 is closer + return -1; + if (d1 > d2) // Object o2 is closer + return 1; + } + catch (CannotFindLocationException e) { + System.err.println(e); + } + // They are the same distance (or we couldn't find one of them). Return the lower id first + return ((RescueObject)o1).getID()-((RescueObject)o2).getID(); + + } + } + + private final static DistanceSorter DISTANCE_SORTER = new DistanceSorter(); +} diff --git a/modules/oldsims/rescuecore/tools/FixIDs.java b/modules/oldsims/rescuecore/tools/FixIDs.java new file mode 100644 index 0000000000000000000000000000000000000000..9e071cb18a0310aecb5027076138a27335194c2d --- /dev/null +++ b/modules/oldsims/rescuecore/tools/FixIDs.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import rescuecore.*; +import rescuecore.objects.*; +import java.util.*; + +public class FixIDs { + private final static String source = "FixIDs"; + public static void main(String[] args) { + try { + Road[] roads = MapFiles.loadRoads("road.bin"); + Node[] nodes = MapFiles.loadNodes("node.bin"); + Building[] buildings = MapFiles.loadBuildings("building.bin"); + fixIDs(roads,nodes,buildings); + // Write out the new files + MapFiles.writeBuildings("building.bin",buildings); + MapFiles.writeRoads("road.bin",roads); + MapFiles.writeNodes("node.bin",nodes); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private static void fixIDs(Road[] roads, Node[] nodes, Building[] buildings) { + /* + int maxID = 0; + for (int i=0;i<roads.length;++i) maxID = Math.max(maxID,roads[i].getID()); + for (int i=0;i<nodes.length;++i) maxID = Math.max(maxID,nodes[i].getID()); + for (int i=0;i<buildings.length;++i) maxID = Math.max(maxID,buildings[i].getID()); + int nextID = ++maxID; + */ + int nextID = 1; + HashMap<Integer,Integer> idMap = new HashMap<Integer,Integer>(); + HashMap<Integer,Node> nodeMap = new HashMap<Integer,Node>(); + for (int i=0;i<roads.length;++i) { + // idMap.put(roads[i].getID(),nextID); + // System.out.println("Road: "+roads[i].getID()+" -> "+nextID); + roads[i].setID(nextID++); + } + for (int i=0;i<buildings.length;++i) { + // idMap.put(buildings[i].getID(),nextID); + // System.out.println("Building: "+buildings[i].getID()+" -> "+nextID); + buildings[i].setID(nextID++); + } + for (int i=0;i<nodes.length;++i) { + idMap.put(nodes[i].getID(),nextID); + // System.out.println("Node: "+nodes[i].getID()+" -> "+nextID); + nodes[i].setID(nextID++); + // System.out.println("Node "+nodes[i].getID()+" used to have "+nodes[i].getEdges().length+" edges"); + nodes[i].clearEdges(0,null); + nodeMap.put(nodes[i].getID(),nodes[i]); + } + // Fix the road head/tail entries + for (int i=0;i<roads.length;++i) { + int headID = idMap.get(roads[i].getHead()); + int tailID = idMap.get(roads[i].getTail()); + // if (headID==0) System.out.println("Couldn't find the new head node for road "+roads[i].getID()); + // if (tailID==0) System.out.println("Couldn't find the new tail node for road "+roads[i].getID()); + roads[i].setHead(idMap.get(roads[i].getHead()),0,null); + roads[i].setTail(idMap.get(roads[i].getTail()),0,null); + Node head = nodeMap.get(roads[i].getHead()); + Node tail = nodeMap.get(roads[i].getTail()); + head.appendEdge(roads[i].getID(),0,null); + tail.appendEdge(roads[i].getID(),0,null); + } + // Fix the building entrances + for (int i=0;i<buildings.length;++i) { + int[] entrances = buildings[i].getEntrances(); + for (int j=0;j<entrances.length;++j) { + // System.out.println("Entrance "+entrances[j]+" -> "+idMap.get(entrances[j])); + entrances[j] = idMap.get(entrances[j]); + Node node = nodeMap.get(entrances[j]); + node.appendEdge(buildings[i].getID(),0,null); + } + buildings[i].setEntrances(entrances,0,null); + } + // Check that the nodes shortcut/pocket/signal timing are the right size + for (int i=0;i<nodes.length;++i) { + Node next = nodes[i]; + int numEdges = next.getEdges().length; + int shortcutSize = next.getShortcutToTurn().length; + int pocketSize = next.getPocketToTurnAcross().length; + int timingSize = next.getSignalTiming().length; + while (shortcutSize < numEdges) { + next.appendShortcutToTurn(0,0,null); + ++shortcutSize; + } + while (pocketSize < numEdges*2) { + next.appendPocketToTurnAcross(0,0,null); + ++pocketSize; + } + while (timingSize < numEdges*3) { + next.appendSignalTiming(0,0,null); + ++timingSize; + } + if (shortcutSize > numEdges) { + // System.out.println("Node "+next.getID()+" has too many shortcuts"); + next.clearShortcutToTurn(0,null); + for (int j=0;j<numEdges;++j) next.appendShortcutToTurn(0,0,null); + } + if (pocketSize > numEdges*2) { + // System.out.println("Node "+next.getID()+" has too many pockets"); + next.clearPocketToTurnAcross(0,null); + for (int j=0;j<numEdges*2;++j) next.appendPocketToTurnAcross(0,0,null); + } + if (timingSize > numEdges*3) { + // System.out.println("Node "+next.getID()+" has too many timings"); + next.clearSignalTiming(0,null); + for (int j=0;j<numEdges*3;++j) next.appendSignalTiming(0,0,null); + } + } + } +} diff --git a/modules/oldsims/rescuecore/tools/LogViewer.java b/modules/oldsims/rescuecore/tools/LogViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..7d77c773927777674e63ac16ee61c3c6973770e9 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/LogViewer.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import rescuecore.*; +import rescuecore.view.*; +import rescuecore.log.*; +import rescuecore.objects.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.border.*; +import java.util.Collection; + +public class LogViewer extends JPanel { + private ObjectInspector inspector; + private Map map; + private Log log; + private int time; + + public static void main(String[] args) { + if (args.length==0) { + System.err.println("Usage: LogViewer <filename>"); + return; + } + try { + Log log = Log.generateLog(args[0]); + JFrame frame = new JFrame("Log viewer"); + frame.setContentPane(new LogViewer(log)); + frame.addWindowListener(new WindowAdapter(){ + public void windowClosing(WindowEvent e){System.exit(0);} + }); + frame.pack(); + Toolkit t = Toolkit.getDefaultToolkit(); + Dimension d = t.getScreenSize(); + // frame.setSize(200,200); + frame.setVisible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public LogViewer(Log log) { + super(new BorderLayout()); + this.log = log; + time = 0; + map = Map.defaultMap(log.getMemory(0)); + final JSlider slider = new JSlider(0,log.getMaxTimestep()); + add(map,BorderLayout.CENTER); + JPanel timePanel = new JPanel(new BorderLayout()); + Border b = BorderFactory.createLineBorder(Color.BLACK); + b = BorderFactory.createTitledBorder(b,"Time Step"); + timePanel.setBorder(b); + slider.setPaintLabels(true); + slider.setPaintTicks(true); + slider.setSnapToTicks(true); + slider.setMinorTickSpacing(1); + slider.setMajorTickSpacing(10); + JButton left = new JButton(" < "); + JButton right = new JButton(" > "); + left.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e){slider.setValue(slider.getValue()-1);} + }); + right.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e){slider.setValue(slider.getValue()+1);} + }); + slider.addChangeListener(new ChangeListener(){ + public void stateChanged(ChangeEvent e){if (!slider.getValueIsAdjusting()) setTimeStep(slider.getValue());} + }); + timePanel.add(slider,BorderLayout.CENTER); + timePanel.add(left,BorderLayout.WEST); + timePanel.add(right,BorderLayout.EAST); + add(timePanel,BorderLayout.SOUTH); + inspector = new ObjectInspector(); + add(inspector,BorderLayout.EAST); + ObjectSelector selector = new ObjectSelector(map); + selector.addObjectSelectionListener(inspector); + slider.setValue(0); + } + + private void setTimeStep(int t) { + if (t < 0 || t > log.getMaxTimestep()) return; + time = t; + Memory m = log.getMemory(time); + map.setMemory(m); + map.repaint(); + Collection<RescueObject> all = m.getAllObjects(); + for (RescueObject next : all) { + if (next instanceof Road) { + Road r = (Road)next; + if (r.getBlock() > r.getWidth()) System.out.println("Overblocked"); + if (r.getLinesToHead() != r.getLinesToTail()) System.out.println("Asymmetric road"); + } + } + } + + private void moveTime(int delta) { + setTimeStep(time+delta); + } +} diff --git a/modules/oldsims/rescuecore/tools/MapFiles.java b/modules/oldsims/rescuecore/tools/MapFiles.java new file mode 100644 index 0000000000000000000000000000000000000000..97ac0ca89ad6ee483ad7eea5cdd9b280928fa26f --- /dev/null +++ b/modules/oldsims/rescuecore/tools/MapFiles.java @@ -0,0 +1,383 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import rescuecore.*; +import rescuecore.objects.*; +import java.io.*; + +public class MapFiles { + private final static int GIS_TYPE_AMBULANCE_CENTER = 2; + private final static int GIS_TYPE_FIRE_STATION = 3; + private final static int GIS_TYPE_POLICE_OFFICE = 4; + private final static int GIS_TYPE_REFUGE = 5; + private final static int GIS_TYPE_CIVILIAN = 6; + private final static int GIS_TYPE_AMBULANCE_TEAM = 7; + private final static int GIS_TYPE_FIRE_BRIGADE = 8; + private final static int GIS_TYPE_POLICE_FORCE = 9; + private final static int GIS_TYPE_FIRE = 10; + private final static String[] GIS_TYPES = {"","","AmbulanceCenter","FireStation","PoliceOffice","Refuge","Civilian","AmbulanceTeam","FireBrigade","PoliceForce","FirePoint"}; + + + private static int readInt(InputStream in) throws IOException { + int result = (in.read()&0xFF) | ((in.read() << 8) & 0xFF00) | ((in.read() << 16) & 0xFF0000) | ((in.read() << 24) & 0xFF000000); + return result; + } + + private static void writeInt(OutputStream out, int value) throws IOException { + out.write(value&0xFF); + out.write((value>>8)&0xFF); + out.write((value>>16)&0xFF); + out.write((value>>24)&0xFF); + } + + public static Node[] loadNodes() throws IOException { + return loadNodes("node.bin"); + } + + public static Node[] loadNodes(File parentDir) throws IOException { + return loadNodes(parentDir.getAbsolutePath() + File.separator + "node.bin"); + } + + public static Node[] loadNodes(String file) throws IOException { + System.out.print("Loading nodes from "+file); + InputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(file)); + // Skip something, not sure what + readInt(in); + readInt(in); + readInt(in); + int num = readInt(in); + // System.out.println("Loading "+num+" nodes"); + Node[] result = new Node[num]; + for (int i=0;i<num;++i) { + int size = readInt(in); + int id = readInt(in); + int x = readInt(in); + int y = readInt(in); + int numEdges = readInt(in); + int[] edges = new int[numEdges]; + int[] shortcut = new int[numEdges]; + int[] pocket = new int[numEdges*2]; + int[] signalTiming = new int[numEdges*3]; + for (int j=0;j<numEdges;++j) edges[j] = readInt(in); + int signal = readInt(in); + for (int j=0;j<numEdges;++j) shortcut[j] = readInt(in); // shortcutToTurn + for (int j=0;j<numEdges;++j) { + pocket[j*2] = readInt(in); // pocketToTurnAcross + pocket[j*2 + 1] = readInt(in); + } + for (int j=0;j<numEdges;++j) { + signalTiming[j*3] = readInt(in); // signalTiming + signalTiming[j*3 + 1] = readInt(in); + signalTiming[j*3 + 2] = readInt(in); + } + result[i] = new Node(x,y,edges,signal!=0,shortcut,pocket,signalTiming); + result[i].setID(id); + System.out.print("."); + } + System.out.println(); + return result; + } + finally { + if (in!=null) in.close(); + } + } + + public static Road[] loadRoads() throws IOException { + return loadRoads("road.bin"); + } + + public static Road[] loadRoads(File parentDir) throws IOException { + return loadRoads(parentDir.getAbsolutePath() + File.separator + "road.bin"); + } + + public static Road[] loadRoads(String file) throws IOException { + System.out.print("Loading roads from "+file); + InputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(file)); + readInt(in); + readInt(in); + readInt(in); + int num = readInt(in); + Road[] result = new Road[num]; + for (int i=0;i<num;++i) { + int size = readInt(in); + int id = readInt(in); + int head = readInt(in); + int tail = readInt(in); + int length = readInt(in); + int roadKind = readInt(in); + int carsToHead = readInt(in); + int carsToTail = readInt(in); + int humansToHead = readInt(in); + int humansToTail = readInt(in); + int width = readInt(in); + int block = readInt(in); + int repairCost = readInt(in); + int median = readInt(in); + int linesToHead = readInt(in); + int linesToTail = readInt(in); + int widthForWalkers = readInt(in); + result[i] = new Road(head,tail,length,roadKind,carsToHead,carsToTail,humansToHead,humansToTail,width,block,repairCost,median!=0,linesToHead,linesToTail,widthForWalkers); + result[i].setID(id); + System.out.print("."); + } + System.out.println(); + return result; + } + finally { + if (in!=null) in.close(); + } + } + + public static Building[] loadBuildings() throws IOException { + return loadBuildings("building.bin"); + } + + public static Building[] loadBuildings(File parentDir) throws IOException { + return loadBuildings(parentDir.getAbsolutePath() + File.separator + "building.bin"); + } + + public static Building[] loadBuildings(String file) throws IOException { + System.out.print("Loading buildings from "+file); + InputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(file)); + readInt(in); + readInt(in); + readInt(in); + int num = readInt(in); + Building[] result = new Building[num]; + for (int i=0;i<num;++i) { + int size = readInt(in); + int id = readInt(in); + int x = readInt(in); + int y = readInt(in); + int floors = readInt(in); + int attributes = readInt(in); + int ignition = readInt(in); + int fieryness = readInt(in); + int brokenness = readInt(in); + int numEntrances = readInt(in); + int[] entrances = new int[numEntrances]; + for (int j=0;j<numEntrances;++j) entrances[j] = readInt(in); + int shapeID = readInt(in); + int area = readInt(in); + int totalArea = readInt(in); + int code = readInt(in); + int numApexes = readInt(in); + int[] apexes = new int[numApexes*2]; + for (int j=0;j<numApexes;++j) { + // Apexes + apexes[j*2] = readInt(in); + apexes[j*2 + 1] = readInt(in); + } + result[i] = new Building(x,y,floors,attributes,ignition!=0,fieryness,brokenness,entrances,code,area,totalArea,apexes,0,1); + result[i].setID(id); + System.out.print("."); + } + System.out.println(); + return result; + } + finally { + if (in!=null) in.close(); + } + } + + public static void writeBuildings(String file, Building[] bs) throws IOException{ + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + writeInt(out,0); + writeInt(out,0); + writeInt(out,0); + + writeInt(out,bs.length); + for(int i = 0; i < bs.length; i++){ + writeInt(out,getSize(bs[i])); + writeInt(out,bs[i].getID()); + writeInt(out,bs[i].getX()); + writeInt(out,bs[i].getY()); + writeInt(out,bs[i].getFloors()); + writeInt(out,bs[i].getBuildingAttributes()); + writeInt(out,bs[i].isIgnited()?1:0); + writeInt(out,bs[i].getFieryness()); + writeInt(out,bs[i].getBrokenness()); + int[] ent = bs[i].getEntrances(); + writeInt(out,ent.length); + for(int j = 0; j < ent.length; j++) + writeInt(out,ent[j]); + writeInt(out,0/*bs[i].getBuildingShapeID()*/); + writeInt(out,bs[i].getGroundArea()); + writeInt(out,bs[i].getTotalArea()); + writeInt(out,bs[i].getBuildingCode()); + int[] ap = bs[i].getApexes(); + writeInt(out,ap.length/2); + for(int j = 0; j < ap.length; j++) + writeInt(out,ap[j]); + } + } + finally { + if (out!=null) out.close(); + } + } + + + public static void writeNodes(String file, Node[] ns) throws IOException{ + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + writeInt(out,5); + writeInt(out,-152950000); + writeInt(out,52050000); + + writeInt(out,ns.length); + // System.out.println("Writing "+ns.length+" nodes"); + for(int i = 0; i < ns.length; i++){ + writeInt(out,getSize(ns[i])); + writeInt(out,ns[i].getID()); + writeInt(out,ns[i].getX()); + writeInt(out,ns[i].getY()); + int[] ed = ns[i].getEdges(); + writeInt(out,ed.length); + // System.out.println("Node "+ns[i].getID()+" has "+ed.length+" edges"); + for(int j = 0; j < ed.length; j++) + writeInt(out,ed[j]); + writeInt(out,ns[i].hasSignal()?1:0); + int[] sh = ns[i].getShortcutToTurn(); + for(int j = 0; j < sh.length; j++) + writeInt(out,sh[j]); + int[] p = ns[i].getPocketToTurnAcross(); + for(int j = 0; j < p.length; j++) + writeInt(out,p[j]); + int[] st = ns[i].getSignalTiming(); + for(int j = 0; j < st.length; j++) + writeInt(out,st[j]); + } + } + finally { + if (out!=null) out.close(); + } + } + + public static void writeRoads(String file, Road[] roads) throws IOException { + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + writeInt(out,5); + writeInt(out,-152950000); + writeInt(out,52050000); + + writeInt(out,roads.length); + for(int i = 0; i < roads.length; i++){ + writeInt(out,getSize(roads[i])); + writeInt(out,roads[i].getID()); + writeInt(out,roads[i].getHead()); + writeInt(out,roads[i].getTail()); + writeInt(out,roads[i].getLength()); + writeInt(out,roads[i].getRoadKind()); + writeInt(out,roads[i].getCarsPassToHead()); + writeInt(out,roads[i].getCarsPassToTail()); + writeInt(out,roads[i].getHumansPassToHead()); + writeInt(out,roads[i].getHumansPassToTail()); + writeInt(out,roads[i].getWidth()); + writeInt(out,roads[i].getBlock()); + writeInt(out,roads[i].getRepairCost()); + writeInt(out,roads[i].hasMedian()?1:0); + writeInt(out,roads[i].getLinesToHead()); + writeInt(out,roads[i].getLinesToTail()); + writeInt(out,roads[i].getWidthForWalkers()); + } + } + finally { + if (out!=null) out.close(); + } + } + + public static void writeGISMotionlessObjects(PrintWriter out, FireStation[] fire, PoliceOffice[] police, AmbulanceCenter[] ambulance, Refuge[] refuge) { + out.println("# Motionless Objects"); + for (int i=0;i<fire.length;++i) writeFixedObjectData(out,GIS_TYPE_FIRE_STATION,fire[i]); + for (int i=0;i<police.length;++i) writeFixedObjectData(out,GIS_TYPE_POLICE_OFFICE,police[i]); + for (int i=0;i<ambulance.length;++i) writeFixedObjectData(out,GIS_TYPE_AMBULANCE_CENTER,ambulance[i]); + for (int i=0;i<refuge.length;++i) writeFixedObjectData(out,GIS_TYPE_REFUGE,refuge[i]); + } + + public static void writeGISMovingObjects(PrintWriter out, FireBrigade[] fire, PoliceForce[] police, AmbulanceTeam[] ambulance, Civilian[] civ, Memory m) { + out.println("# Moving Objects"); + for (int i=0;i<civ.length;++i) writeMovingObjectData(out,GIS_TYPE_CIVILIAN,m.lookup(civ[i].getPosition()),m); + for (int i=0;i<ambulance.length;++i) writeMovingObjectData(out,GIS_TYPE_AMBULANCE_TEAM,m.lookup(ambulance[i].getPosition()),m); + for (int i=0;i<fire.length;++i) writeMovingObjectData(out,GIS_TYPE_FIRE_BRIGADE,m.lookup(fire[i].getPosition()),m); + for (int i=0;i<police.length;++i) writeMovingObjectData(out,GIS_TYPE_POLICE_FORCE,m.lookup(police[i].getPosition()),m); + } + + public static void writeGISFires(PrintWriter out, Building[] fires) { + out.println("# Fires"); + for (int i=0;i<fires.length;++i) { + writeFixedObjectData(out,GIS_TYPE_FIRE,fires[i]); + } + } + + public static void writeGISImportantBuildings(PrintWriter out, Building[] buildings) { + out.println("# Important buildings"); + for (int i=0;i<buildings.length;++i) { + if (buildings[i].getImportance()>1) { + out.print("ImportantBuilding "); + out.print(buildings[i].getID()); + out.print("="); + out.println(buildings[i].getImportance()); + } + } + } + + private static void writeFixedObjectData(PrintWriter out, int type, Building b) { + // Fixed objects are of them form TYPE = id + out.print(GIS_TYPES[type]); + out.print("="); + out.print(b.getID()); + out.println(); + } + + private static void writeMovingObjectData(PrintWriter out, int type, RescueObject location, Memory m) { + // Moving objects are of the form TYPE = position [,positionExtra] + out.print(GIS_TYPES[type]); + out.print("="); + out.print(location.getID()); + if (location.isRoad()) { + Road r = (Road)location; + Node head = m.getHead(r); + Node tail = m.getTail(r); + int extra = (int)((Math.random()*r.getLength())/1000); + out.print(","); + out.print(extra); + } + out.println(); + } + + private static int getSize(Building b){ + return (15 + b.getEntrances().length + b.getApexes().length)*RescueConstants.INT_SIZE; + } + + private static int getSize(Node n){ + return (6 + n.getEdges().length*7)*RescueConstants.INT_SIZE; + } + + private static int getSize(Road r){ + return 16*RescueConstants.INT_SIZE; + } +} diff --git a/modules/oldsims/rescuecore/tools/PlaceBlockages.java b/modules/oldsims/rescuecore/tools/PlaceBlockages.java new file mode 100644 index 0000000000000000000000000000000000000000..41794ce06571733b8a1e0771bf0fc2b9a3b82c19 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/PlaceBlockages.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import rescuecore.*; +import rescuecore.view.*; +import rescuecore.objects.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import javax.swing.*; +import java.io.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; + +public class PlaceBlockages { + private Road[] allRoads; + private Node[] allNodes; + + private Memory memory; + private Map map; + private Point pressPoint; + private Point dragPoint; + private Layer overlay; + private Layer roadLayer; + + public PlaceBlockages(Road[] roads, Node[] nodes) { + allRoads = roads; + allNodes = nodes; + memory = new HashMemory(); + for (int i=0;i<roads.length;++i) { + memory.add(roads[i],0,this); + } + for (int i=0;i<nodes.length;++i) { + if (memory.lookup(nodes[i].getID())!=null) System.err.println("WARNING: Duplicate node ID: "+nodes[i].getID()+", this is is already used by "+memory.lookup(nodes[i].getID())); + memory.add(nodes[i],0,this); + } + Arrays.sort(allRoads,new RoadSorter(memory)); + map = new Map(memory); + roadLayer = Layer.createRoadLayer(memory); + roadLayer.addRenderer(Road.class,new BigRoadRenderer()); + map.addLayer(roadLayer); + map.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + handleClick(e); + } + public void mousePressed(MouseEvent e) { + handlePress(e); + } + public void mouseReleased(MouseEvent e) { + handleRelease(e); + } + }); + map.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent e) { + handleDrag(e); + } + }); + JToolBar toolbar = new JToolBar(); + Action saveAction = new AbstractAction("Save") { + public void actionPerformed(ActionEvent e) { + save(); + } + }; + toolbar.add(saveAction); + JFrame frame = new JFrame("Road Blockage Placement"); + JPanel top = new JPanel(new BorderLayout()); + top.add(toolbar,BorderLayout.NORTH); + top.add(map,BorderLayout.CENTER); + frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) { + System.exit(0); + }}); + frame.getContentPane().add(top); + frame.pack(); + frame.setVisible(true); + } + + private void handleClick(MouseEvent e) { + Object[] all = map.getObjectsAtPoint(e.getPoint()); + update(all); + map.repaint(); + } + + private void handlePress(MouseEvent e) { + pressPoint = e.getPoint(); + } + + private void handleRelease(MouseEvent e) { + if (dragPoint==null) return; + int x1 = Math.min(pressPoint.x,dragPoint.x); + int y1 = Math.min(pressPoint.y,dragPoint.y); + int x2 = Math.max(pressPoint.x,dragPoint.x); + int y2 = Math.max(pressPoint.y,dragPoint.y); + Rectangle2D box = new Rectangle2D.Double(x1,y1,x2-x1,y2-y1); + Object[] objects = map.getObjectsInArea(box); + update(objects); + pressPoint = null; + dragPoint = null; + overlay.removeAllObjects(); + map.repaint(); + } + + private void handleDrag(MouseEvent e) { + if (pressPoint!=null) { + dragPoint = e.getPoint(); + int dx = pressPoint.x - dragPoint.x; + int dy = pressPoint.y - dragPoint.y; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + if (dx > 5 || dy > 5) { + // Draw a rectangle on the view + Rectangle r = new Rectangle(Math.min(pressPoint.x,dragPoint.x),Math.min(pressPoint.y,dragPoint.y),dx,dy); + overlay.setObject(r); + map.repaint(); + } + } + } + + private void update(Object[] os) { + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Road) updateRoad((Road)os[i]); + } + map.repaint(); + } + + private void updateRoad(Road road) { + int lanes = road.getLinesToHead(); + int empty = lanes; //road.getFreeLinesToHead(); FIXME + int width = road.getWidth(); + --empty; + if (empty==-1) empty = lanes; + int blocked = lanes-empty; + int laneWidth = road.getWidth()/(road.getLinesToHead()+road.getLinesToTail()); + int blockNeeded = blocked * laneWidth * 2; + if (blockNeeded > road.getWidth()) { + System.out.println("Trying to set block to "+blockNeeded+" but width is only "+road.getWidth()); + blockNeeded = road.getWidth(); + } + road.setBlock(blockNeeded,0,this); + // Check that we have blocked the right number of lanes + int free = road.getLinesToHead(); //road.getFreeLinesToHead(); FIXME + if (free != empty) System.out.println("We have "+free+" empty lanes instead of "+empty+"!"); + } + + private void save() { + try { + // Write out the blocked roads + PrintWriter out = new PrintWriter(new FileWriter(new File("blockades.lst"))); + for (int i=0;i<allRoads.length;++i) { + out.println(allRoads[i].getBlock()); + } + out.flush(); + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private static class BigRoadRenderer implements MapRenderer { + public boolean canRender(Object o) { + return o instanceof Road; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Road road = (Road)o; + Node roadHead = (Node)memory.lookup(road.getHead()); + Node roadTail = (Node)memory.lookup(road.getTail()); + int headX = transform.toScreenX(roadHead.getX()); + int headY = transform.toScreenY(roadHead.getY()); + int tailX = transform.toScreenX(roadTail.getX()); + int tailY = transform.toScreenY(roadTail.getY()); + Shape shape = new java.awt.geom.Line2D.Double(headX,headY,tailX,tailY); + shape = new BasicStroke(10).createStrokedShape(shape); + Color c = Color.BLACK; + int lanes = road.getLinesToHead(); + int free = lanes; //road.getFreeLinesToHead(); FIXME + c = Color.ORANGE; + if (free==0) c = Color.BLACK; + if (free==lanes) c = Color.WHITE; + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,c); + ((Graphics2D)g).draw(shape); + return shape; + } + } + + public static void main(String[] args) { + try { + Road[] r = MapFiles.loadRoads("road.bin"); + Node[] n = MapFiles.loadNodes("node.bin"); + new PlaceBlockages(r,n); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private class RoadSorter implements Comparator { + private Memory m; + + public RoadSorter(Memory m) { + this.m = m; + } + + public int compare(Object o1, Object o2) { + Road r1 = (Road)o1; + Road r2 = (Road)o2; + Node h1 = (Node)m.lookup(r1.getHead()); + Node t1 = (Node)m.lookup(r1.getTail()); + Node h2 = (Node)m.lookup(r2.getHead()); + Node t2 = (Node)m.lookup(r2.getTail()); + int x1 = (h1.getX()+t1.getX())/2; + int y1 = (h1.getY()+t1.getY())/2; + int x2 = (h2.getX()+t2.getX())/2; + int y2 = (h2.getY()+t2.getY())/2; + if (x1 < x2) return -1; + if (x1 > x2) return 1; + if (y1 < y2) return -1; + if (y2 > y1) return 1; + return 0; + } + } +} diff --git a/modules/oldsims/rescuecore/tools/RandomConfig.java b/modules/oldsims/rescuecore/tools/RandomConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..a841c3d9efae64444734a4e6ac8a0b705e722083 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/RandomConfig.java @@ -0,0 +1,334 @@ +/* + * Last change: $Date: 2004/08/10 20:46:17 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import java.io.*; +import java.util.*; +import rescuecore.objects.*; +import rescuecore.*; + +/** + This class will take a city data file and produce a random gisini.txt file for use on that city. + @author Cameron Skinner + */ +public class RandomConfig { + private Node[] allNodes; + private Road[] allRoads; + private Building[] allBuildings; + + public static void main(String[] args) { + new RandomConfig(args); + } + + RandomConfig(String[] args) { + Limits fireBrigades = new Limits(10,15,"fireBrigades","fire brigades"); + Limits policeForces = new Limits(10,15,"policeForces","police forces"); + Limits ambulanceTeams = new Limits(5,8,"ambulanceTeams","ambulance teams"); + Limits fireStations = new Limits(1,1,"fireStations","fire stations"); + Limits policeStations = new Limits(1,1,"policeStations","police stations"); + Limits ambulanceCenters = new Limits(1,1,"ambulanceCenters","ambulance centers"); + Limits civilians = new Limits(70,90,"civilians","civilians"); + Limits refuges = new Limits(1,5,"refuges","refuges"); + Limits fires = new Limits(2,8,"fires","fires"); + Limits fireRadius = new Limits(0,20000,"fireradius","fire radius"); + Limits[] allLimits = new Limits[] {fireBrigades,policeForces,ambulanceTeams,fireStations,policeStations,ambulanceCenters,civilians,refuges,fires,fireRadius}; + boolean allowTrappedAgents = false; + boolean bigFires = false; + boolean trappedCivilians = true; + + String dir = ""; + + for (int i=0;i<args.length;++i) { + if (args[i].startsWith("-min-")) { + for (int j=0;j<allLimits.length;++j) { + if (args[i].equalsIgnoreCase("-min-"+allLimits[j].prefix)) allLimits[j].min = Integer.parseInt(args[++i]); + } + } + else if (args[i].startsWith("-max-")) { + for (int j=0;j<allLimits.length;++j) { + if (args[i].equalsIgnoreCase("-max-"+allLimits[j].prefix)) allLimits[j].max = Integer.parseInt(args[++i]); + } + } + else if (args[i].startsWith("-no-")) { + for (int j=0;j<allLimits.length;++j) { + if (args[i].equalsIgnoreCase("-no-"+allLimits[j].prefix)) { + allLimits[j].min = 0; + allLimits[j].max = 0; + } + } + } + else if (args[i].startsWith("-set-")) { + int num = Integer.parseInt(args[i+1]); + for (int j=0;j<allLimits.length;++j) { + if (args[i].equalsIgnoreCase("-set-"+allLimits[j].prefix)) { + allLimits[j].min = num; + allLimits[j].max = num; + } + } + ++i; + } + else if (args[i].equalsIgnoreCase("-t") || args[i].equalsIgnoreCase("--allow-trapped-agents")) { + allowTrappedAgents = true; + } + else if (args[i].equalsIgnoreCase("-c") || args[i].equalsIgnoreCase("--allow-untrapped-civilians")) { + trappedCivilians = false; + } + else if (args[i].equalsIgnoreCase("-b") || args[i].equalsIgnoreCase("--big-fires")) { + bigFires = true; + } + + else if (args[i].equalsIgnoreCase("-d") || args[i].equalsIgnoreCase("--dir")) { + dir = args[i+1]; + } + + else if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) { + System.out.println("Usage: RandomConfig [options]"); + System.out.println("This program will read from \"road.bin\", \"node.bin\" and \"building.bin\" and produce a randomised \"gisini.txt\""); + System.out.println("Options"); + System.out.println("======="); + for (int j=0;j<allLimits.length;++j) { + System.out.println("-min-"+allLimits[j].prefix+"\tSet the minimum number of "+allLimits[j].name+" (currently "+allLimits[j].min+")"); + System.out.println("-max-"+allLimits[j].prefix+"\tSet the maximum number of "+allLimits[j].name+" (currently "+allLimits[j].max+")"); + System.out.println("-no-"+allLimits[j].prefix+"\tSet the minimum and maximum of "+allLimits[j].name+" to zero"); + } + System.out.println("-t\t--allow-trapped-agents\tAllow rescue agents (fire brigades, police forces and ambulance teams) to be placed inside buildings (default OFF)"); + System.out.println("-c\t--allow-untrapped-civilians\tAllow civilians to be placed outside buildings (default OFF)"); + System.out.println("-b\t--big-fires\tAllow big fires"); + System.out.println("-d\t--dir\tSet output directory (use full path)"); + System.out.println("-h\t--help\tPrint this message"); + return; + } + } + try { + + File parentDir = new File(dir); + File gisini = new File(parentDir, "gisini.txt"); + + // Open the output + + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(gisini))); + + // Build the city data + allNodes = MapFiles.loadNodes(parentDir); + allRoads = MapFiles.loadRoads(parentDir); + allBuildings = MapFiles.loadBuildings(parentDir); + + Memory memory = new HashMemory(); + for (int i=0;i<allNodes.length;++i) memory.add(allNodes[i],0); + for (int i=0;i<allRoads.length;++i) memory.add(allRoads[i],0); + for (int i=0;i<allBuildings.length;++i) memory.add(allBuildings[i],0); + // Place items + int numFireBrigades = fireBrigades.getNumber(); + int numPoliceForces = policeForces.getNumber(); + int numAmbulanceTeams = ambulanceTeams.getNumber(); + int numFireStations = fireStations.getNumber(); + int numPoliceStations = policeStations.getNumber(); + int numAmbulanceCenters = ambulanceCenters.getNumber(); + int numRefuges = refuges.getNumber(); + int numCivilians = civilians.getNumber(); + int numFires = fires.getNumber(); + + FireStation[] fireStationBuildings = new FireStation[numFireStations]; + PoliceOffice[] policeOfficeBuildings = new PoliceOffice[numPoliceStations]; + AmbulanceCenter[] ambulanceCenterBuildings = new AmbulanceCenter[numAmbulanceCenters]; + Refuge[] refugeBuildings = new Refuge[numRefuges]; + Building[] normalBuildings = placeMotionlessObjects(fireStationBuildings,policeOfficeBuildings,ambulanceCenterBuildings,refugeBuildings,allBuildings); + MapFiles.writeGISMotionlessObjects(out,fireStationBuildings,policeOfficeBuildings,ambulanceCenterBuildings,refugeBuildings); + + + FireBrigade[] fireBrigadeObjects = new FireBrigade[numFireBrigades]; + PoliceForce[] policeForceObjects = new PoliceForce[numPoliceForces]; + AmbulanceTeam[] ambulanceTeamObjects = new AmbulanceTeam[numAmbulanceTeams]; + Civilian[] civilianObjects = new Civilian[numCivilians]; + // placeMovingObjects(fireBrigadeObjects,policeForceObjects,ambulanceTeamObjects,civilianObjects,allBuildings,allRoads,allNodes,allowTrappedAgents,trappedCivilians); + placeMovingObjects(fireBrigadeObjects,policeForceObjects,ambulanceTeamObjects,civilianObjects,allBuildings,new Road[0],allNodes,allowTrappedAgents,trappedCivilians); + MapFiles.writeGISMovingObjects(out,fireBrigadeObjects,policeForceObjects,ambulanceTeamObjects,civilianObjects,memory); + + Building[] fireBuildings; + if (bigFires) fireBuildings = placeBigFires(numFires,normalBuildings,fireRadius); + else fireBuildings = placeNormalFires(numFires,normalBuildings); + MapFiles.writeGISFires(out,fireBuildings); + + out.flush(); + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + /* + Place all motionless objects (fire stations, police offices, ambulance teams and refuges) + @param fire An array to be filled with fire stations + @param police An array to be filled with police offices + @param ambulance An array to be filled with ambulance centres + @param refuge An array to be filled with refuges + @param allBuildings All buildings in the map + @return All ordinary buildings + */ + public static Building[] placeMotionlessObjects(FireStation[] fire, PoliceOffice[] police, AmbulanceCenter[] ambulance, Refuge[] refuge, Building[] allBuildings) { + List remaining = new ArrayList(); + for (int i=0;i<allBuildings.length;++i) remaining.add(allBuildings[i]); + Collections.shuffle(remaining); + System.out.println("Placing "+ambulance.length+" ambulance centers"); + Iterator it = remaining.iterator(); + for (int i=0;i<ambulance.length;++i) { + Building location = (Building)it.next(); + it.remove(); + AmbulanceCenter a = new AmbulanceCenter(location); + a.setID(location.getID()); + ambulance[i] = a; + // writeFixedObjectData(out,TYPE_AMBULANCE_CENTER,i,location); + } + System.out.println("Placing "+fire.length+" fire stations"); + for (int i=0;i<fire.length;++i) { + Building location = (Building)it.next(); + it.remove(); + FireStation a = new FireStation(location); + a.setID(location.getID()); + fire[i] = a; + // writeFixedObjectData(out,TYPE_FIRE_STATION,i,location); + // System.out.print("."); + } + System.out.println("Placing "+police.length+" police stations"); + for (int i=0;i<police.length;++i) { + Building location = (Building)it.next(); + it.remove(); + PoliceOffice a = new PoliceOffice(location); + a.setID(location.getID()); + police[i] = a; + // writeFixedObjectData(out,TYPE_POLICE_OFFICE,i,location); + // System.out.print("."); + } + System.out.println("Placing "+refuge.length+" refuges"); + for (int i=0;i<refuge.length;++i) { + Building location = (Building)it.next(); + it.remove(); + Refuge a = new Refuge(location); + a.setID(location.getID()); + refuge[i] = a; + // writeFixedObjectData(out,TYPE_REFUGE,i,location); + // System.out.print("."); + } + // System.out.println(); + return (Building[])remaining.toArray(new Building[0]); + } + + public static void placeMovingObjects(FireBrigade[] fire, PoliceForce[] police, AmbulanceTeam[] ambulance, Civilian[] civ, Building[] b, Road[] r, Node[] n, boolean allowTrappedAgents, boolean trappedCivilians) { + RescueObject[] allLocations = new RescueObject[b.length+r.length+n.length]; + System.arraycopy(b,0,allLocations,0,b.length); + System.arraycopy(r,0,allLocations,b.length,r.length); + System.arraycopy(n,0,allLocations,b.length+r.length,n.length); + RescueObject[] outsideLocations = new RescueObject[r.length+n.length]; + System.arraycopy(r,0,outsideLocations,0,r.length); + System.arraycopy(n,0,outsideLocations,r.length,n.length); + System.out.println("Placing "+civ.length+" civilians"); + for (int i=0;i<civ.length;++i) { + civ[i] = new Civilian(); + civ[i].setPosition(randomLocation(trappedCivilians?b:allLocations).getID(),0,null); + // writeMovingObjectData(out,TYPE_CIVILIAN,i,randomLocation(trappedCivilians?b:allLocations)); + // System.out.print("."); + } + // System.outln.println(); + System.out.println("Placing "+ambulance.length+" ambulance teams"); + for (int i=0;i<ambulance.length;++i) { + ambulance[i] = new AmbulanceTeam(); + ambulance[i].setPosition(randomLocation(allowTrappedAgents?allLocations:outsideLocations).getID(),0,null); + // writeMovingObjectData(out,TYPE_AMBULANCE_TEAM,i,randomLocation(allowTrappedAgents?allLocations:outsideLocations)); + // System.out.print("."); + } + // System.out.println(); + System.out.println("Placing "+fire.length+" fire brigades"); + for (int i=0;i<fire.length;++i) { + fire[i] = new FireBrigade(); + fire[i].setPosition(randomLocation(allowTrappedAgents?allLocations:outsideLocations).getID(),0,null); + // writeMovingObjectData(out,TYPE_FIRE_BRIGADE,i,randomLocation(allowTrappedAgents?allLocations:outsideLocations)); + // System.out.print("."); + } + // System.out.println(); + System.out.println("Placing "+police.length+" police forces"); + for (int i=0;i<police.length;++i) { + police[i] = new PoliceForce(); + police[i].setPosition(randomLocation(allowTrappedAgents?allLocations:outsideLocations).getID(),0,null); + // writeMovingObjectData(out,TYPE_POLICE_FORCE,i,randomLocation(allowTrappedAgents?allLocations:outsideLocations)); + // System.out.print("."); + } + // System.out.println(); + } + + public static Building[] placeNormalFires(int num, Building[] b) { + List remaining = new ArrayList(); + for (int i=0;i<b.length;++i) remaining.add(b[i]); + Collections.shuffle(remaining); + Building[] result = new Building[num]; + System.out.println("Placing "+num+" fires"); + Iterator it = remaining.iterator(); + for (int i=0;i<num;++i) result[i] = (Building)it.next(); + return result; + } + + public static Building[] placeBigFires(int num, Building[] b, Limits radius) { + List remaining = new ArrayList(); + for (int i=0;i<b.length;++i) remaining.add(b[i]); + Collections.shuffle(remaining); + Collection fires = new HashSet(); + System.out.print("Placing "+num+" big fires"); + Iterator it = remaining.iterator(); + for (int i=0;i<num;++i) { + Building center = (Building)it.next(); + fires.add(center); + long r = radius.getNumber(); + long distanceSquared = r*r; + // Check for close buildings + for (int j=0;j<b.length;++j) { + long dx = center.getX()-b[j].getX(); + long dy = center.getY()-b[j].getY(); + long distance = (dx*dx) + (dy*dy); + if (distance <= distanceSquared) fires.add(b[j]); + } + } + return (Building[])fires.toArray(new Building[0]); + } + + public static RescueObject randomLocation(RescueObject[] possible) { + return possible[(int)(Math.random()*possible.length)]; + } + + // private static Node findNode(int id) { + // for (int i=0;i<allNodes.length;++i) if (allNodes[i].getID()==id) return allNodes[i]; + // return null; + // } + + private class Limits { + int min; + int max; + String prefix; + String name; + + Limits(int min, int max, String prefix, String name) { + this.min = min; + this.max = max; + this.prefix = prefix; + this.name = name; + } + + int getNumber() { + if (min==max) return min; + int range = max-min; + return (int)(Math.random()*range)+min; + } + } +} diff --git a/modules/oldsims/rescuecore/tools/RandomPolydata.java b/modules/oldsims/rescuecore/tools/RandomPolydata.java new file mode 100644 index 0000000000000000000000000000000000000000..3e48e8005a472741dc170a724dd850c8ece3e51c --- /dev/null +++ b/modules/oldsims/rescuecore/tools/RandomPolydata.java @@ -0,0 +1,246 @@ +/* + * Last change: $Date: 2004/08/10 20:47:10 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import java.util.*; +import java.io.*; +import rescuecore.objects.Node; +import rescuecore.objects.Building; + +public class RandomPolydata { + private final static int DEFAULT_CENTERS = 4; + private final static int DEFAULT_MAX_LEVEL = 4; + private final static int DEFAULT_MIN_LEVEL = 1; + private final static int DEFAULT_REPEAT = 1; + private final static int DEFAULT_MAX_SIZE = 50; + private final static int DEFAULT_MIN_SIZE = 10; + + private static void printUsage() { + System.out.println("Usage: RandomPolydata [options]"); + System.out.println("Options"); + System.out.println("======="); + System.out.println("-n\t--num-centers\tThe number of epicenters"); + System.out.println("-l\t--max-level\tThe maximum magnitude level"); + System.out.println("-e\t--min-level\tThe minimum magnitude level"); + System.out.println("-r\t--repeat-levels\tThe number of times to repeat levels"); + System.out.println("-x\t--max-size\tThe maximum size of the polygons produced by each center as a proportion of the map size (100 means 100% of the map can be covered)"); + System.out.println("-m\t--min-size\tThe minimum size of the polygons produced by each center as a proportion of the map size"); + System.out.println("--box=left,right,top,bottom,magnitude"); + } + + public static void main(String[] args) { + int numCenters = DEFAULT_CENTERS; + int maxLevel = DEFAULT_MAX_LEVEL; + int minLevel = DEFAULT_MIN_LEVEL; + int repeat = DEFAULT_REPEAT; + int maxSize = DEFAULT_MAX_SIZE; + int minSize = DEFAULT_MIN_SIZE; + Collection boxes = new ArrayList(); + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) { + printUsage(); + return; + } + else if (args[i].equalsIgnoreCase("-n") || args[i].equalsIgnoreCase("--num-centers")) { + numCenters = Integer.parseInt(args[++i]); + } + else if (args[i].equalsIgnoreCase("-l") || args[i].equalsIgnoreCase("--max-level")) { + maxLevel = Integer.parseInt(args[++i]); + } + else if (args[i].equalsIgnoreCase("-e") || args[i].equalsIgnoreCase("--min-level")) { + minLevel = Integer.parseInt(args[++i]); + } + else if (args[i].equalsIgnoreCase("-r") || args[i].equalsIgnoreCase("--repeat-levels")) { + repeat = Integer.parseInt(args[++i]); + } + else if (args[i].equalsIgnoreCase("-x") || args[i].equalsIgnoreCase("--max-size")) { + maxSize = Integer.parseInt(args[++i]); + } + else if (args[i].equalsIgnoreCase("-m") || args[i].equalsIgnoreCase("--min-size")) { + minSize = Integer.parseInt(args[++i]); + } + else if (args[i].startsWith("--box")) { + StringTokenizer tokens = new StringTokenizer(args[i].substring(6),","); + double left = Double.parseDouble(tokens.nextToken()); + double right = Double.parseDouble(tokens.nextToken()); + double top = Double.parseDouble(tokens.nextToken()); + double bottom = Double.parseDouble(tokens.nextToken()); + int magnitude = Integer.parseInt(tokens.nextToken()); + boxes.add(new Box(left,right,top,bottom,magnitude)); + } + } + // Find the size of the city + Node[] allNodes; + Building[] allBuildings; + try { + allNodes = MapFiles.loadNodes(); + allBuildings = MapFiles.loadBuildings(); + } + catch (IOException e) { + e.printStackTrace(); + return; + } + int minX = allNodes[0].getX(); + int minY = allNodes[0].getY(); + int maxX = minX; + int maxY = minY; + for (int i=0;i<allNodes.length;++i) { + minX = Math.min(minX,allNodes[i].getX()); + maxX = Math.max(maxX,allNodes[i].getX()); + minY = Math.min(minY,allNodes[i].getY()); + maxY = Math.max(maxY,allNodes[i].getY()); + } + for (int i=0;i<allBuildings.length;++i) { + minX = Math.min(minX,allBuildings[i].getX()); + maxX = Math.max(maxX,allBuildings[i].getX()); + minY = Math.min(minY,allBuildings[i].getY()); + maxY = Math.max(maxY,allBuildings[i].getY()); + } + int xRange = maxX-minX; + int yRange = maxY-minY; + + Polygon[] shindo; + Polygon[] gal; + + if (boxes.size()==0) { + List shindoPolygons = new ArrayList(); + List galPolygons = new ArrayList(); + int levelRange = maxLevel-minLevel; + // Place the epicenters + for (int i=0;i<numCenters;++i) { + int centerX = ((int)(Math.random()*xRange)) + minX; + int centerY = ((int)(Math.random()*yRange)) + minY; + int level = ((int)(Math.random()*levelRange)) + minLevel; + int repeats = ((int)(Math.random()*repeat)) + repeat; + // Generate the polygon + double xSize = Math.random()*(maxSize-minSize) + minSize; + int xExtent = (int)(xRange*xSize/100.0); + double ySize = Math.random()*(maxSize-minSize) + minSize; + int yExtent = (int)(yRange*ySize/100.0); + System.out.println("Placing level "+level+" epicenter at "+centerX+","+centerY+" with "+repeats+" repeats. Size is "+xExtent+" x "+yExtent); + // Let's make it an octagon numbered clockwise from zero at the top + int[] xs = new int[8]; + int[] ys = new int[8]; + xs[0] = xs[4] = centerX; + xs[1] = xs[3] = centerX + xExtent/2; + xs[2] = centerX + xExtent; + xs[5] = xs[7] = centerX - xExtent/2; + xs[6] = centerX - xExtent; + ys[0] = centerY + yExtent; + ys[1] = ys[7] = centerY + yExtent/2; + ys[2] = ys[6] = centerY; + ys[3] = ys[5] = centerY - yExtent/2; + ys[4] = centerY - yExtent; + for (int j=0;j<repeat;++j) { + shindoPolygons.add(new Polygon(level,xs,ys,8)); + galPolygons.add(new Polygon(level,xs,ys,8)); + } + } + shindo = new Polygon[shindoPolygons.size()]; + gal = new Polygon[galPolygons.size()]; + shindoPolygons.toArray(shindo); + galPolygons.toArray(gal); + } + else { + shindo = new Polygon[boxes.size()]; + gal = new Polygon[boxes.size()]; + int i=0; + System.out.println("World extends from "+minX+","+minY+" to "+maxX+","+maxY); + for (Iterator it = boxes.iterator();it.hasNext();i++) { + Box next = (Box)it.next(); + int[] xs = new int[4]; + int[] ys = new int[4]; + int left = (int)((xRange * next.left)+minX); + int right = (int)((xRange * next.right)+minX); + int top = (int)((yRange * next.top)+minY); + int bottom = (int)((yRange * next.bottom)+minY); + xs[0] = xs[3] = left; + xs[1] = xs[2] = right; + ys[0] = ys[1] = top; + ys[2] = ys[3] = bottom; + shindo[i] = new Polygon(next.size,xs,ys,4); + gal[i] = new Polygon(next.size,xs,ys,4); + System.out.println("Putting box from "+left+","+top+" to "+right+","+bottom); + } + } + try { + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("shindopolydata.dat"))); + writePolydata(shindo,out); + out.flush(); + out.close(); + out = new PrintWriter(new BufferedWriter(new FileWriter("galpolydata.dat"))); + writePolydata(gal,out); + out.flush(); + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private static void writePolydata(Polygon[] polygons, PrintWriter out) throws IOException { + // olydata format is: + // Line 1: <number of values to read, including the <level>,<number of vertices> pairs>,<number of polygons> + // Each polygon then has: + // <level>,<number of vertices> + // Followed by <number of vertices> pairs of coordinates + int sum = 0; + for (int i=0;i<polygons.length;++i) { + sum += polygons[i].numPoints*2; + sum += 2; + } + out.print(sum); + out.print(","); + out.println(polygons.length); + for (int i=0;i<polygons.length;++i) { + out.print(polygons[i].level); + out.print(","); + out.println(polygons[i].numPoints); + for (int j=0;j<polygons[i].numPoints;++j) { + out.print(polygons[i].xs[j]); + out.print(","); + out.println(polygons[i].ys[j]); + } + } + } + + private static class Polygon { + int level; + int[] xs; + int[] ys; + int numPoints; + + Polygon(int l, int[] x, int[] y, int num) { + level = l; + xs = x; + ys = y; + numPoints = num; + } + } + + private static class Box { + double left,right,top,bottom; + int size; + + public Box(double l, double r, double t, double b, int s) { + left = l; + right = r; + top = t; + bottom = b; + size = s; + } + } +} diff --git a/modules/oldsims/rescuecore/tools/Score.java b/modules/oldsims/rescuecore/tools/Score.java new file mode 100644 index 0000000000000000000000000000000000000000..8710f33fe5764229c1752cea559981ce39cd52e8 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/Score.java @@ -0,0 +1,276 @@ +/* + * Last change: $Date: 2005/02/18 03:34:34 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools; + +import java.io.*; +import java.util.*; +import rescuecore.*; +import rescuecore.objects.*; +import rescuecore.log.*; + +/** + This class will calculate the score of a particular log file + */ +public class Score { + private final static String HEADER = "RoboCup-Rescue Prototype Log 00\0"; + + private final static String SILENT_FLAG = "--silent"; + private final static String JUST_SCORE_FLAG = "--just-score"; + private final static String KILL_FLAG = "--no-kill-civilians"; + + private static boolean silent; + private static boolean justScore; + private static boolean killCiviliansAtEnd; + + private final static int VERBOSITY_NONE = 0; + private final static int VERBOSITY_SOME = 1; + private final static int VERBOSITY_LOTS = 2; + + private final static int MODE_MAXIMUM = 0; + private final static int MODE_MINIMUM = 1; + private final static int MODE_AVERAGE = 2; + private final static int MODE_VARIANCE = 3; + private final static int MODE_STANDARD_DEVIATION = 4; + + public static void main(String[] args) { + silent = false; + justScore = false; + killCiviliansAtEnd = true; + if (args.length==0) { + printUsage(); + return; + } + List<String> fileNames = new ArrayList<String>(); + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase(SILENT_FLAG)) { + silent = true; + } + else if (args[i].equalsIgnoreCase(JUST_SCORE_FLAG)) { + justScore = true; + } + else if (args[i].equalsIgnoreCase(KILL_FLAG)) { + killCiviliansAtEnd = false; + } + else { + fileNames.add(args[i]); + } + } + if (fileNames.size()==0) { + printUsage(); + return; + } + List<LogScore> allScores = new ArrayList<LogScore>(); + for (String next : fileNames) { + try { + if (!silent) System.err.println("Reading log "+next); + LogScore score = calculateScore(next); + if (score!=null) allScores.add(score); + } + catch (Exception e) { + e.printStackTrace(); + } + } + LogScore[] scores = new LogScore[allScores.size()]; + allScores.toArray(scores); + if (justScore || scores.length==1) { + for (int i=0;i<scores.length;++i) System.out.println(scores[i].score); + } + else { + LogScore max = new LogScore("Highest",scores,MODE_MAXIMUM); + LogScore min = new LogScore("Lowest",scores,MODE_MINIMUM); + LogScore average = new LogScore("Average",scores,MODE_AVERAGE); + LogScore variance = new LogScore("Variance",scores,MODE_VARIANCE); + LogScore sd = new LogScore("Standard deviation",scores,MODE_STANDARD_DEVIATION); + System.out.println("Run\tBuilding area left\tBuilding area total\tHP left\tHP total\tCivilians alive\tTotal civilians\tAgents alive\tTotal agents\tScore from HP\tScore from buildings\tOverall score"); + for (int i=0;i<scores.length;++i) { + scores[i].write(); + } + System.out.println(); + if (scores.length>1) { + System.out.println("\tBuilding area left\tBuilding area total\tHP left\tHP total\tCivilians alive\tTotal civilians\tAgents alive\tTotal agents\tScore from HP\tScore from buildings\tOverall score"); + max.write(); + min.write(); + average.write(); + variance.write(); + sd.write(); + } + } + } + + private static LogScore calculateScore(String filename) throws IOException, InvalidLogException { + Log log = Log.generateLog(filename); + return new LogScore(filename,log.getMemory(log.getMaxTimestep()),killCiviliansAtEnd); + } + + private static void printUsage() { + System.out.println("Usage: Score [options] <log files>"); + System.out.println("Options"); + System.out.println("======="); + System.out.println(SILENT_FLAG+"\tSilent mode. No progress indicators will be emitted."); + System.out.println(JUST_SCORE_FLAG+"\tDon't calculate any statistics about the scores."); + System.out.println(KILL_FLAG+"\tDon't consider buried or damaged civilians to be dead."); + } + + private static double calculate(double[] values, int mode) { + double result, average; + switch (mode) { + case MODE_MAXIMUM: + result = values[0]; + for (int i=0;i<values.length;++i) result = Math.max(result,values[i]); + return result; + case MODE_MINIMUM: + result = values[0]; + for (int i=0;i<values.length;++i) result = Math.min(result,values[i]); + return result; + case MODE_AVERAGE: + result = 0; + for (int i=0;i<values.length;++i) result += values[i]; + return result/values.length; + case MODE_VARIANCE: + average = 0; + for (int i=0;i<values.length;++i) average += values[i]; + average/=values.length; + result = 0; + for (int i=0;i<values.length;++i) result += (values[i]-average)*(values[i]-average); + return result/(values.length-1); + case MODE_STANDARD_DEVIATION: + average = 0; + for (int i=0;i<values.length;++i) average += values[i]; + average/=values.length; + result = 0; + for (int i=0;i<values.length;++i) result += (values[i]-average)*(values[i]-average); + return Math.sqrt(result/(values.length-1)); + } + throw new RuntimeException("Unknown mode: "+mode); + } + + private static class LogScore { + String name; + double score, HPscore, buildingScore, areaMax, areaLeft, hpMax, hpLeft, civilians, civiliansAlive, agents, agentsAlive; + + public LogScore(String name, LogScore[] scores, int mode) { + this.name = name; + double[][] values = new double[11][scores.length]; + for (int i=0;i<scores.length;++i) { + values[0][i] = scores[i].score; + values[1][i] = scores[i].HPscore; + values[2][i] = scores[i].buildingScore; + values[3][i] = scores[i].areaMax; + values[4][i] = scores[i].areaLeft; + values[5][i] = scores[i].hpMax; + values[6][i] = scores[i].hpLeft; + values[7][i] = scores[i].civilians; + values[8][i] = scores[i].civiliansAlive; + values[9][i] = scores[i].agents; + values[10][i] = scores[i].agentsAlive; + } + score = calculate(values[0],mode); + HPscore = calculate(values[1],mode); + buildingScore = calculate(values[2],mode); + areaMax = calculate(values[3],mode); + areaLeft = calculate(values[4],mode); + hpMax = calculate(values[5],mode); + hpLeft = calculate(values[6],mode); + civilians = calculate(values[7],mode); + civiliansAlive = calculate(values[8],mode); + agents = calculate(values[9],mode); + agentsAlive = calculate(values[10],mode); + } + + public LogScore(String name, Memory state, boolean killCivilians) { + this.name = name; + Collection<RescueObject> allBuildings = state.getObjectsOfType(RescueConstants.TYPE_BUILDING,RescueConstants.TYPE_REFUGE,RescueConstants.TYPE_FIRE_STATION,RescueConstants.TYPE_POLICE_OFFICE,RescueConstants.TYPE_AMBULANCE_CENTER); + Collection<RescueObject> allAgents = state.getObjectsOfType(RescueConstants.TYPE_CIVILIAN,RescueConstants.TYPE_FIRE_BRIGADE,RescueConstants.TYPE_POLICE_FORCE,RescueConstants.TYPE_AMBULANCE_TEAM); + areaMax = 0; + areaLeft = 0; + hpMax = 0; + hpLeft = 0; + civilians = 0; + civiliansAlive = 0; + agents = allAgents.size(); + agentsAlive = 0; + // System.out.println(allBuildings.length+" buildings"); + for (RescueObject b : allBuildings) { + Building next = (Building)b; + double area = next.getTotalArea(); + areaMax += area; + // System.out.println(next.toLongString()); + // System.out.println("Next building area, fieryness: "+area+", "+next.getFieryness()); + switch (next.getFieryness()) { + case RescueConstants.FIERYNESS_NOT_BURNT: + areaLeft += area; + break; + case RescueConstants.FIERYNESS_HEATING: + case RescueConstants.FIERYNESS_SLIGHTLY_BURNT: + case RescueConstants.FIERYNESS_WATER_DAMAGE: + areaLeft += area*2.0/3.0; + break; + case RescueConstants.FIERYNESS_BURNING: + case RescueConstants.FIERYNESS_MODERATELY_BURNT: + areaLeft += area/3.0; + + case RescueConstants.FIERYNESS_VERY_BURNT: + case RescueConstants.FIERYNESS_INFERNO: + case RescueConstants.FIERYNESS_BURNT_OUT: + break; + } + // System.out.println("New total area, area left: "+areaMax+", "+areaLeft); + } + for (RescueObject a : allAgents) { + Humanoid next = (Humanoid)a; + if (killCivilians) { + if (next.isCivilian() && (next.isDamaged() || next.isBuried())) next.setHP(0,3000,null); + } + if (next.isAlive()) ++agentsAlive; + if (next.isCivilian()) { + ++civilians; + if (next.isAlive()) ++civiliansAlive; + } + hpMax += RescueConstants.MAX_HP; + hpLeft += next.getHP(); + } + HPscore = agentsAlive + (hpLeft/hpMax); + buildingScore = Math.sqrt(areaLeft/areaMax); + score = HPscore * buildingScore; + } + + public void write() { + System.out.print(name); + System.out.print("\t"); + System.out.print(areaLeft); + System.out.print("\t"); + System.out.print(areaMax); + System.out.print("\t"); + System.out.print(hpLeft); + System.out.print("\t"); + System.out.print(hpMax); + System.out.print("\t"); + System.out.print(civiliansAlive); + System.out.print("\t"); + System.out.print(civilians); + System.out.print("\t"); + System.out.print(agentsAlive); + System.out.print("\t"); + System.out.print(agents); + System.out.print("\t"); + System.out.print(HPscore); + System.out.print("\t"); + System.out.print(buildingScore); + System.out.print("\t"); + System.out.println(score); + } + } +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/AntsRoadWeighter.java b/modules/oldsims/rescuecore/tools/mapgenerator/AntsRoadWeighter.java new file mode 100644 index 0000000000000000000000000000000000000000..edb1cc7b48f5085c75572da4df6f59b976459580 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/AntsRoadWeighter.java @@ -0,0 +1,133 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Random; + +/** + * A road weighter that decides on road importance by making 10,000 trips + * through the RescueMap between random pairs of nodes. + * + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class AntsRoadWeighter implements RoadWeighter { + /** Number of runs to use to determine main roads. **/ + private static final int RUNS = 15000; + /** Base percentage of 3 lane roads. **/ + private static final int THREELANE = 10; + /** Base percentage of 2 lane roads. **/ + private static final int TWOLANE = 20; + + /** A count of how many times each road (adjacent node pair) has been used. **/ + private int[][] usedCount; + + /** The Euclidean distances between all pairs of nodes. **/ + private int[][] distances; + + public void connect(RescueMap rm, int uniformity, boolean nooneway, Random rand) { + int nodes = rm.getNodeCount(); + // store all the distances + distances = new int[nodes][nodes]; + for (int i = 0; i < nodes; i++) + for (int j = 0; j < i; j++) { + int x = rm.getX(i) - rm.getX(j); + int y = rm.getY(i) - rm.getY(j); + distances[i][j] = (int) Math.sqrt(x * x + y * y); + distances[j][i] = distances[i][j]; + } + // record how often each road is used + usedCount = new int[nodes][nodes]; + System.out.print("Simulating road use."); + System.out.flush(); + int steps = RUNS / 20; + // two arrays to be used in the searches + int[] prevs = new int[nodes]; + int[] dists = new int[nodes]; + for (int i = 0; i < RUNS; i++) { + int[] picked = pickNodes(rm, rand); + runPath(prevs, dists, rm, picked[0], picked[1]); + if (i % steps == 0) { + System.out.print("."); + System.out.flush(); + } + } + System.out.println("done."); + + // find the two cutoffs + ArrayList l = new ArrayList(nodes * 5); + for (int i = 0; i < nodes; i++) + for (int j = 0; j < nodes; j++) + if (rm.getRoad(i, j) > 0) { + l.add(Integer.valueOf(usedCount[i][j])); + } + Collections.sort(l); + int index1 = (int) (l.size() * (1 - THREELANE / 100.0)); + int v1 = ((Integer) (l.get(index1))).intValue(); + int v2 = ((Integer) (l.get(index1 - (int) (l.size() * TWOLANE / 100.0)))).intValue(); + // now upgrade the roads + for (int i = 0; i < nodes; i++) + for (int j = 0; j < nodes; j++) { + if (usedCount[i][j] >= v1 || (nooneway && usedCount[j][i] >= v1)) { + rm.setRoad(i, j, 3); + if (nooneway) + rm.setRoad(j, i, 3); + } else if (usedCount[i][j] >= v2 || (nooneway && usedCount[j][i] >= v2)) { + rm.setRoad(i, j, 2); + if (nooneway) + rm.setRoad(j, i, 2); + } + } + } + + public int[] pickNodes(RescueMap rm, Random rand) { + return new int[] { (int) (rand.nextDouble() * rm.getNodeCount()), (int) (rand.nextDouble() * rm.getNodeCount()) }; + } + + private void runPath(int[] prevs, int[] dists, RescueMap rm, int start, int end) { + int nodes = rm.getNodeCount(); + // find shortest path with A* + for (int i = 0; i < dists.length; i++) + dists[i] = Integer.MAX_VALUE; + PairHeap q = new PairHeap(); + prevs[start] = -1; + dists[start] = distances[start][end]; + int next = start; + while (next != end) { + // update every neighbour of next - add if necessary + for (int j = 0; j < nodes; j++) { + if (j != next && rm.getRoad(next, j) > 0) { // connected + int guess = dists[next] + distances[next][j] + distances[j][end]; + if (dists[j] > guess) { + // update! + dists[j] = guess; + prevs[j] = next; + q.insert(j, guess); + } + } + } + next = q.deleteMin(); + } + // now extract the path + int prev = end; + while (prevs[prev] != -1) { + usedCount[prevs[prev]][prev]++; + prev = prevs[prev]; + } + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/BasicBuildingGenerator.java b/modules/oldsims/rescuecore/tools/mapgenerator/BasicBuildingGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..7ed5b9857ffbd3cde97df390a9ff86833dd8f4c0 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/BasicBuildingGenerator.java @@ -0,0 +1,217 @@ +/* + * Last change: $Date: 2004/08/10 21:20:24 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.*; +/** + * A BuildingGenerator that recursively chops a block into rectangular buildings. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class BasicBuildingGenerator extends BlocksBuildingGenerator{ + /** + * Divides a block into a number of rectangular buildings. + * @param rm The RescueMap containing the block. + * @param block A list of nodes in anti-clockwise order around the block. + * @param density The density of buildings to add. + * @param rand The random number generator. + * @return Buildings to add as an array of form [building][apex][coordinate] + **/ + protected int[][][] fillBlock(RescueMap rm, int[] block,int density, Random rand){ + //make a building filling the whole block + int[][] build = new int[block.length+1][2]; + for(int i = 0; i < block.length; i++){ + build[i][0] = rm.getX(block[i]); + build[i][1] = rm.getY(block[i]); + if(i > 0 && i < block.length-1) + shift(rm,block[i-1],block[i],block[i+1],build[i]); + } + shift(rm,block[block.length-1],block[0],block[1],build[0]); + shift(rm,block[block.length-2],block[block.length-1],block[0],build[build.length-2]); + build[build.length-1][0] = build[0][0]; + build[build.length-1][1] = build[0][1]; + //now break it up + int minArea = 500000/(density+1); + ArrayList builds = processBuilding(build,minArea,rand); + int[][][] bs = new int[builds.size()][][]; + for(int i = 0; i < bs.length; i++) { + bs[i] = (int[][])builds.get(i); + shrink(bs[i],rand.nextDouble()*0.4); + } + return bs; + } + + private void shrink(int[][] building, double amount) { + int xMin = building[0][0]; + int xMax = building[0][0]; + int yMin = building[0][1]; + int yMax = building[0][1]; + for (int i=1;i<building.length;++i) { + xMin = Math.min(xMin,building[i][0]); + xMax = Math.max(xMax,building[i][0]); + yMin = Math.min(yMin,building[i][1]); + yMax = Math.max(yMax,building[i][1]); + } + int xCenter = (xMin+xMax)/2; + int yCenter = (yMin+yMax)/2; + for (int i=0;i<building.length;++i) { + double dx = building[i][0]-xCenter; + double dy = building[i][1]-yCenter; + building[i][0] -= (int)(dx*amount); + building[i][1] -= (int)(dy*amount); + } + } + + /** + * No buildings returned - just have an empty outer rim. + * @param rm The RescueMap containing the block. + * @param block A list of nodes in anti-clockwise order around the block. + * @param density The density of buildings to add. + * @param rand The random number generator. + * @return Buildings to add as an array of form [building][apex][coordinate] + **/ + protected int[][][] fillOuterBlock(RescueMap rm, int[] block, int density,Random rand){ + return new int[0][][]; + } + + + /** + * Shifts a set of coordinates in the direction normal to the two adjacent road. + * Assumes n1 --> n2 --> n3 is an anticlockwise motion. + * @param n1 First node of the roads. + * @param n2 Middle node of the two roads + * @param n3 The last node of the two roads + * @param c1 The set of coordinates to shift + **/ + private static void shift(RescueMap rm, int n1, int n2, int n3, int[] c){ + //get the normal to the first road + long n1y = rm.getX(n2) - rm.getX(n1); + long n1x = rm.getY(n1) - rm.getY(n2); + //get normal to the second road + long n2y = rm.getX(n3) - rm.getX(n2); + long n2x = rm.getY(n2) - rm.getY(n3); + //get length of each normal + double len1 = Math.sqrt(n1y*n1y+n1x*n1x); + double len2 = Math.sqrt(n2y*n2y+n2x*n2x); + + int d = 3000;//Math.max(rm.getRoad(n1,n2),rm.getRoad(n2,n3))*2000 +500; + + int x1 = rm.getX(n1) - (int)(n1x*d*1.0/len1); + int x2 = rm.getX(n2) - (int)(n1x*d*1.0/len1); + int y1 = rm.getY(n1) - (int)(n1y*d*1.0/len1); + int y2 = rm.getY(n2) - (int)(n1y*d*1.0/len1); + int x3 = rm.getX(n2) - (int)(n2x*d*1.0/len2); + int x4 = rm.getX(n3) - (int)(n2x*d*1.0/len2); + int y3 = rm.getY(n2) - (int)(n2y*d*1.0/len2); + int y4 = rm.getY(n3) - (int)(n2y*d*1.0/len2); + + int[] intersect = intersection(x1,y1,x2,y2,x3,y3,x4,y4); + if(intersect == null){ + c[0] -= (n1x/len1)*d; + c[1] -= (n1y/len1)*d; + } + else{ + c[0] = intersect[0]; + c[1] = intersect[1]; + } + } + + /** + * Where two line segments intersect. + **/ + private static int[] intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){ + + double b1 = (y2 - y1)/(x2 - x1); + double a1 = y1 - (b1*x1); + double b2 = (y4 - y3)/(x4 - x3); + double a2 = y3 - (b2*x3); + if(x2-x1 == 0) //vertical + return new int[]{(int)x1,(int)(a2+(b2*x1))}; + if(x3-x4 == 0) //vertical + return new int[]{(int)x3,(int)(a1+(b1*x3))}; + /* if(Math.abs(b1-b2) < 0.001) + return null; + double intX = -(a1-a2)/(b1-b2); + double intY = a1+(b1*intX);*/ + double d = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + if(d == 0) + return null; //parallel roads + double a = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))*1.0/d; + int intX = (int)x1+(int)(a*(x2-x1)); + int intY = (int)y1+(int)(a*(y2-y1)); + return new int[]{(int)intX,(int)intY}; + } + + /** + * Recursively splits a building in half, until it is lower than the given threshold + * or gets lucky when within a reasonable size. + * @param building The initial building to split. + * @param minArea The lower threshold that forms the base case. + * @param rand The random number generator. + * @return A list containing all buildings split through recursive calls. + **/ + private ArrayList processBuilding(int[][] building, int minArea, Random rand){ + int a = RescueMapToolkit.area(building); + //System.out.println(minArea+", "+a); + if(a < 1000) //kill these ones... + return new ArrayList(0); + if(a < minArea){ //primary base case + ArrayList l = new ArrayList(1); + l.add(building); + return l; + } + int lower = (int)(rand.nextDouble()*minArea); + lower = lower*4; + if(a < lower){ //probabilistic base case + ArrayList l = new ArrayList(1); + l.add(building); + return l; + } + //find the max and min points + int minX = building[0][0]; + int minY = building[0][1]; + int maxX = building[0][0]; + int maxY = building[0][1]; + for(int i = 1; i < building.length; i++){ + if(minX > building[i][0]) minX = building[i][0]; + if(maxX < building[i][0]) maxX = building[i][0]; + if(minY > building[i][1]) minY = building[i][1]; + if(maxY < building[i][1]) maxY = building[i][1]; + } + int midX = (minX+maxX)/2; + int midY = (minY+maxY)/2; + //split the building in half + int[][][] split; + if(maxX-minX > maxY-minY) + split = RescueMapToolkit.split(building,midX,minY,midX,maxY); + else + split = RescueMapToolkit.split(building,minX,midY,maxX,midY); + + if(split == null || RescueMapToolkit.area(split[0]) == 0 || RescueMapToolkit.area(split[1]) == 0) + return new ArrayList(0); + + //and recurse + ArrayList a1 = processBuilding(split[0],minArea,rand); + ArrayList a2 = processBuilding(split[1],minArea,rand); + ArrayList toRet = new ArrayList(a1.size()+a2.size()); + for(int i = 0; i < a1.size(); i++) + toRet.add(a1.get(i)); + for(int i = 0; i < a2.size(); i++) + toRet.add(a2.get(i)); + return toRet; + } + +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/BlocksBuildingGenerator.java b/modules/oldsims/rescuecore/tools/mapgenerator/BlocksBuildingGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..9a3977173a4b4e5308114f8197771d20fc0e8857 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/BlocksBuildingGenerator.java @@ -0,0 +1,192 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +/** + * An abstract class that provides functionality for adding buildings to a + * RescueMap one city block at a time. + * + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public abstract class BlocksBuildingGenerator implements BuildingGenerator { + /** The outer ring of roads. **/ + private int[] outerBlock; + + /** + * Adds the buildings. + * + * @param rm The RescueMap to add to. + * @param uniformity The regularity amongst buildings. + * @param buildDensity The density of the buildings. + * @param rand The random number generator. + **/ + public void addBuildings(RescueMap rm, int uniformity, int buildDensity, Random rand) { + int[][] blocks = getBlocks(rm); + System.out.println("Filling blocks"); + for (int i = 0; i < blocks.length; i++) { + int[][][] builds = fillBlock(rm, blocks[i], buildDensity, rand); + for (int j = 0; j < builds.length; j++) { + int[] centre = RescueMapToolkit.centre(builds[j]); + rm.addBuilding(builds[j], RescueMapToolkit.makeEntrance(rm, centre), (int) (rand.nextDouble() * 4) + 1, 0); + } + System.out.println(i + " of " + blocks.length); + } + int[][][] builds = fillOuterBlock(rm, outerBlock, buildDensity, rand); + for (int j = 0; j < builds.length; j++) { + int[] centre = RescueMapToolkit.centre(builds[j]); + rm.addBuilding(builds[j], RescueMapToolkit.makeEntrance(rm, centre), (int) (rand.nextDouble() * 4) + 1, 0); + } + } + + /** + * Give a list of buildings for a block. This should not do the adding of + * buildings. + * + * @param rm The RescueMap containing the block. + * @param block A list of nodes in anti-clockwise order around the block. + * @param buildDensity The density of buildings to add. + * @param rand The random number generator. + * @return Buildings to add as an array of form [building][apex][coordinate] + **/ + protected abstract int[][][] fillBlock(RescueMap rm, int[] block, int buildDensity, Random rand); + + /** + * Give a list of buildings for the outside of the city. + * + * @param rm The RescueMap containing the block. + * @param block A list of nodes in anti-clockwise order around the block. + * @param buildDensity The density of buildings to add. + * @param rand The random number generator. + * @return Buildings to add as an array of form [building][apex][coordinate] + **/ + protected abstract int[][][] fillOuterBlock(RescueMap rm, int[] block, int buildDensity, Random rand); + + /** + * Gets a list of city blocks. + * + * @param m The RescueMap to find the blocks of. + * @return Blocks as an array of arrays of nodes: [blocks][nodes] + **/ + private int[][] getBlocks(RescueMap m) { + int[][] rs = m.getRoads(); + HashMap visited = new HashMap(); + ArrayList blocks = new ArrayList(100); + for (int i = 0; i < rs.length; i++) { + Integer vis = (Integer) visited.get(hash(rs[i])); + int v = 0; + if (vis != null) + v = vis.intValue(); + if ((v & 1) == 0) { + v = v | 1; + visited.put(hash(rs[i]), Integer.valueOf(v)); + // walk head-tail circuit + int[] c = walkCircuit(m, rs[i], visited, true); + if (c.length > 0) + blocks.add(c); + } + if ((v & 2) == 0) { + v = v | 2; + visited.put(hash(rs[i]), Integer.valueOf(v)); + // walk tail-head circuit + int[] c = walkCircuit(m, rs[i], visited, false); + if (c.length > 0) + blocks.add(c); + } + } + int[][] blks = new int[blocks.size()][]; + blocks.toArray(blks); + return blks; + } + + /** + * Creates a hash key for a road. + **/ + private static Integer hash(int[] road) { + int a = Math.min(road[0], road[1]); + int b = Math.max(road[0], road[1]); + return Integer.valueOf(b * 10000 + a); + } + + /** + * Walks around a block in an anti-clockwise direction This also marks each road + * visited in the 'visited' map. + * + * @param curr The road to begin with. + * @param visited A mapping from nodes to their visited status (either not + * visited, visited in one direction, or visited in both + * directions. + * @param isTail Whether the next road to visit starts at curr's tail or not. + * @return An inorder list of nodes in the circuit (block). + **/ + private int[] walkCircuit(RescueMap m, int[] curr, HashMap visited, boolean isTail) { + ArrayList circ = new ArrayList(20); + double totalAngle = 0; + int t = 0; + int s = 1; + if (isTail) { + t = 1; + s = 0; + } + int[] first = { curr[t], curr[s] }; + circ.add(Integer.valueOf(curr[t])); + do { + circ.add(Integer.valueOf(curr[s])); + int[] nbs = m.getUnderlyingNeighbours(curr[s]); + // get the neighbour with largest angle + int ind = 0; + double maxAngle = -Math.PI; + double angle; + for (int k = 0; k < nbs.length; k++) { + if (nbs[k] == curr[t]) // no going back! + continue; + angle = RescueMapToolkit.angle(m, curr[t], curr[s], nbs[k]); + // we want the smallest angle greater than 180, otherwise the smallest less than + // 180. + if (angle > maxAngle) { + maxAngle = angle; + ind = k; + } + } + totalAngle += maxAngle; + // make it current, and set its visited status + curr[t] = curr[s]; + curr[s] = nbs[ind]; + Integer val = (Integer) visited.get(hash(curr)); + int v = 0; + if (val != null) + v = val.intValue(); + + if (curr[s] > curr[t]) + visited.put(hash(curr), Integer.valueOf(v | 1)); + else + visited.put(hash(curr), Integer.valueOf(v | 2)); + } while (curr[s] != first[0]); + + int[] rs = new int[circ.size()]; + for (int i = 0; i < rs.length; i++) + rs[i] = ((Integer) circ.get(i)).intValue(); + if (totalAngle <= 0) { // wrong direction - happens with boundary roads + outerBlock = rs; + return new int[0]; + } + return rs; + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/BuildingGenerator.java b/modules/oldsims/rescuecore/tools/mapgenerator/BuildingGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..13c43bafc304e8d2674b69fde9e76c1ecd775f81 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/BuildingGenerator.java @@ -0,0 +1,33 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.Random; +/** + * Interface to classes that add buildings to a RescueMap. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public interface BuildingGenerator{ + /** + * Adds the buildings. + * @param rm The RescueMap to add to. + * @param uniformity The regularity amongst buildings. + * @param buildDensity The density of the buildings. + * @param rand The random number generator. + **/ + public void addBuildings(RescueMap rm, int uniformity, int buildDensity,Random rand); +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/MapGenerator.java b/modules/oldsims/rescuecore/tools/mapgenerator/MapGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..c1f159b581eaa77e1a114397156b5a67da7e0793 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/MapGenerator.java @@ -0,0 +1,184 @@ +/* + * Last change: $Date: 2004/08/10 21:20:24 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.io.*; +import java.util.Random; + +/** + * The main class of the mapgenerator package. + * This creates a new random city map for use in the Robocup Rescue simulation. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class MapGenerator{ + + /** + * Creates a RescueMap object to specification. + * @param rotate Whether to randomly rotate the nodes. + * @param nooneway Whether to allow different number of lanes in each direction on a road. + * @param roadDensity How dense the roads are - 0..100. + * @param buildDensity How dense the buildings are - 0..100. + * @param width Approximate width to make the map in millimeters. + * @param height Approximate height to make the map in millimeters. + * @param uniformity How uniform (non-random) to make the map - 0..100. + * @param movement The maximum amount we can adjust nodes by in meters. + * @param smooth Total number of lanes a road must be before it has corners smoothed. + * @param initScheme The scheme to use to initialise the map. + * @param connScheme The scheme to use to decide on number of lanes per road. + * @param buildScheme The scheme to use to add buildings to the map. + * @param seed The random seed. + * @return A new RescueMap. + **/ + public static RescueMap generateMap(boolean rotate, boolean nooneway, int roadDensity, int roadRemoval, int buildDensity, int width, int height, int uniformity, int movement, int smooth, String initScheme, String connScheme, String buildScheme, long seed){ + Random rand = new Random(seed); + //add and connect nodes of a RoadSet + RescueMap rm = RoadSetFactory.createRoadSet(initScheme,width,height,roadDensity,rand); + //randomise the RoadSet + RescueMapToolkit.randomise(rm,uniformity, roadDensity, roadRemoval,movement, nooneway,rand); + //rotate the nodes + if(rotate) + RescueMapToolkit.rotate(rm,rand.nextDouble()*Math.PI*2); + //add intersections for overlapping roads + RescueMapToolkit.findIntersections(rm); + //weight the roads + weightRoads(rm,uniformity,connScheme,nooneway,rand); + //smooth main roads + RescueMapToolkit.smoothRoads(rm,smooth); + //add buildings and convert to a memory + addBuildings(rm,uniformity,buildDensity,buildScheme,rand); + //make sure we have no negative coordinates + rm.align(); + System.out.println("Construction complete."); + return rm; + } + + /** + * Applies the road weighting scheme. + * @param rm The RescueMap to alter. + * @param uniformity The uniformity of the map - 0..100. + * @param connScheme The scheme to use. + * @param nooneway Whether roads can be different widths in each direction. + * @param rand The random number generator. + **/ + private static void weightRoads(RescueMap rm, int uniformity, String connScheme, boolean nooneway, Random rand){ + RoadWeighter rw; + if(connScheme.equals("ants")) + rw = new AntsRoadWeighter(); + else + return; + + rw.connect(rm,uniformity,nooneway,rand); + + } + /** + * Applies the building scheme. + * @param rm The RescueMap to alter. + * @param uniformity The uniformity of the map - 0..100. + * @param buildDensity The density of buildings - 0..100. + * @param buildScheme The scheme to use. + * @param rand The random number generator. + **/ + private static void addBuildings(RescueMap rm, int uniformity, int buildDensity, String buildScheme, Random rand){ + BuildingGenerator bg; + if(buildScheme.equals("blocks")) + bg = new BasicBuildingGenerator(); + else + return; + + bg.addBuildings(rm,uniformity,buildDensity,rand); + + } + + /** + * Runs the program from the command line. + **/ + public static void main(String[] args){ + int roadDensity = 60; + int roadRemoval = 15; + int buildDensity = 50; + int width = 1000000; + int height = 1000000; + int uniformity = 75; + int movement = 20; + int smooth = 5; + boolean rotate = false; + boolean nooneway = false; + String initScheme = "grid"; + String connScheme = "none"; + String buildScheme = "blocks"; + long seed = System.currentTimeMillis(); + //get the switches + for(int i = 0; i < args.length; i+=2){ + if(args[i].equals("-rd")) + roadDensity = Integer.parseInt(args[i+1]); + else if(args[i].equals("-rr")) + roadRemoval = Integer.parseInt(args[i+1]); + else if(args[i].equals("-bd")) + buildDensity = Integer.parseInt(args[i+1]); + else if(args[i].equals("-w")) + width = Integer.parseInt(args[i+1]); + else if(args[i].equals("-h")) + height = Integer.parseInt(args[i+1]); + else if(args[i].equals("-u")) + uniformity = Integer.parseInt(args[i+1]); + else if(args[i].equals("-m")) + movement = Integer.parseInt(args[i+1]); + else if(args[i].equals("-sm")) + smooth = Integer.parseInt(args[i+1]); + else if(args[i].equals("-init")) + initScheme = args[i+1]; + else if(args[i].equals("-weight")) + connScheme = args[i+1]; + else if(args[i].equals("-build")) + buildScheme = args[i+1]; + else if(args[i].equals("-r")) + seed = Long.parseLong(args[i+1]); + else if(args[i].equals("-rotate")){ + rotate = true; + i--; + } + else if(args[i].equals("-nooneway")){ + nooneway = true; + i--; + } + else{ + System.out.println("Switches: [-switch] [valid range] [default] [description]"); + System.out.println("-rd [0..100] 60 \t\tRoad density"); + System.out.println("-rr [5..90] 15 \t\tRoad removal percentage"); + System.out.println("-bd [0..100] 50 \t\tBuilding density"); + System.out.println("-u [0..100] 75 \t\t\tRoad uniformity"); + System.out.println("-w [0..*] 1000000 \t\tMap width (millimeters)"); + System.out.println("-h [0..*] 1000000 \t\tMap height (millimeters)"); + System.out.println("-m [0..*] 20 \t\t\tMax node movement (meters)"); + System.out.println("-sm [1..8] 5 \t\t\tSmooth wide roads (lanes)"); + System.out.println("-init [grid] grid \t\tMap initialisation scheme"); + System.out.println("-weight [none,ants] none \tRoad weighting scheme"); + System.out.println("-build [blocks] \t\tblocks Building scheme"); + System.out.println("-rotate \t\t\tSet random map rotation flag"); + System.out.println("-nooneway \t\t\tSwitch off one way streets"); + return; + } + } + RescueMap m = generateMap(rotate,nooneway,roadDensity,roadRemoval,buildDensity,width,height,uniformity,movement,smooth,initScheme,connScheme,buildScheme,seed); + try { + m.toFile(); + } + catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/PairHeap.java b/modules/oldsims/rescuecore/tools/mapgenerator/PairHeap.java new file mode 100644 index 0000000000000000000000000000000000000000..769068fb016525498f4e2d5359a86b49f0274a08 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/PairHeap.java @@ -0,0 +1,223 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +public class PairHeap{ + + private int size; + private PairNode root; + + /** + * Construct the pairing heap. + */ + public PairHeap(){ + size = 0; + root = null; + } + + /** + * Insert into the priority queue. + * @param val The value to insert + * @param priority The priority to give the value + */ + public PairNode insert(int val, int priority){ + PairNode newNode = new PairNode(val,priority); + size++; + if(root == null) + root = newNode; + else + root = compareAndLink(root, newNode); + return newNode; + } + + + /** + * Insert into the priority queue, and return a PairNode + * that can be used by decreaseKey. + * Duplicates are allowed. + * @param val the value to insert. + * @param priority The priority to give the value + * @return the node containing the newly inserted item. + */ + public PairNode addItem(int val, int priority){ + PairNode newNode = insert(val,priority); + return newNode; + } + + /** + * Find the highest priority value in the priority queue. + * @return the highest priority value. + */ + public int findMin(){ + if(isEmpty()) + return -1; + return root.getValue(); + } + + /** + * Remove the smallest item from the priority queue. + * @exception Underflow if the priority queue is empty. + */ + public int deleteMin(){ + int val = findMin( ); + if(root.getLeftChild() == null) + root = null; + else + root = combineSiblings(root.getLeftChild()); + + size--; + return val; + } + + /** + * Change the value of the item stored in the pairing heap. + * @param p any node returned by addItem. + * @param newPriority - must decrease + */ + public void decreaseKey(PairNode p, int newPriority ){ + if(p.getPriority() > newPriority) + return; + p.setPriority(newPriority); + if( p != root ){ + if(p.getNextSibling() != null ) + p.getNextSibling().setPrev(p.getPrev()); + if(p.getPrev().getLeftChild() == p ) + p.getPrev().setLeftChild(p.getNextSibling()); + else + p.getPrev().setNextSibling(p.getNextSibling()); + + p.setNextSibling(null); + root = compareAndLink(root, p); + } + } + + /** + * Test if the priority queue is logically empty. + * @return true if empty, false otherwise. + */ + public boolean isEmpty( ){ + return size == 0; + } + + /** + * Make the priority queue logically empty. + */ + public void makeEmpty( ){ + size = 0; + root = null; + } + + /** + * Internal method that is the basic operation to maintain order. + * Links first and second together to satisfy heap order. + * @param first root of tree 1, which may not be null. + * first.nextSibling MUST be null on entry. + * @param second root of tree 2, which may be null. + * @return result of the tree merge. + */ + private PairNode compareAndLink(PairNode first, PairNode second){ + if(second == null) + return first; + + if(second.getPriority() < first.getPriority()){ + // Attach first as leftmost child of second + second.setPrev(first.getPrev()); + first.setPrev(second); + first.setNextSibling(second.getLeftChild()); + if(first.getNextSibling() != null) + first.getNextSibling().setPrev(first); + second.setLeftChild(first); + return second; + } + else{ + // Attach second as leftmost child of first + second.setPrev(first); + first.setNextSibling(second.getNextSibling()); + if(first.getNextSibling() != null) + first.getNextSibling().setPrev(first); + second.setNextSibling(first.getLeftChild()); + if(second.getNextSibling() != null) + second.getNextSibling().setPrev(second); + first.setLeftChild(second); + return first; + } + } + + + private PairNode[] doubleIfFull( PairNode[] array, int index ){ + if( index == array.length ){ + PairNode[] oldArray = array; + + array = new PairNode[index * 2]; + for( int i = 0; i < index; i++ ) + array[i] = oldArray[i]; + } + return array; + } + + // The tree array for combineSiblings + private PairNode[] treeArray = new PairNode[5]; + + /** + * Internal method that implements two-pass merging. + * @param firstSibling the root of the conglomerate; + * assumed not null. + */ + private PairNode combineSiblings(PairNode firstSibling){ + if(firstSibling.getNextSibling() == null) + return firstSibling; + + int numSiblings = 0; + for(;firstSibling != null; numSiblings++){ + treeArray = doubleIfFull(treeArray, numSiblings); + treeArray[numSiblings] = firstSibling; + firstSibling.getPrev().setNextSibling(null); // break links + firstSibling = firstSibling.getNextSibling(); + } + treeArray = doubleIfFull(treeArray, numSiblings); + treeArray[numSiblings] = null; + int i = 0; + for(;i + 1 < numSiblings; i += 2) + treeArray[i] = compareAndLink(treeArray[i], treeArray[i+1]); + + int j = i-2; + if(j == numSiblings-3) + treeArray[j] = compareAndLink(treeArray[j], treeArray[j+2]); + for(;j >= 2; j-=2) + treeArray[j-2] = compareAndLink(treeArray[j-2], treeArray[j]); + + return treeArray[0]; + } + + // Test program + public static void main(String [ ] args){ + PairHeap h = new PairHeap( ); + int numItems = 40000; + int i = 37; + for( i = 37; i != 0; i = ( i + 37 ) % numItems ) + h.insert(i,i); + for( i = numItems-1; i >0; i--) + if(h.deleteMin() != i ) + System.out.println( "Oops! " + i ); +/* + for( i = 37; i != 0; i = ( i + 37 ) % numItems ) + h.insert(i,i); + for( i = 1; i <= numItems; i++ ) + if(h.deleteMin() != i ) + System.out.println( "Oops! " + i + " " ); +*/ + } +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/PairNode.java b/modules/oldsims/rescuecore/tools/mapgenerator/PairNode.java new file mode 100644 index 0000000000000000000000000000000000000000..f56ca22ba8eeef5edb18fff3474f056f452b1832 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/PairNode.java @@ -0,0 +1,61 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +public class PairNode{ + + private int value; + private int priority; + private PairNode leftChild; + private PairNode nextSibling; + private PairNode prev; + + /** + * Construct the PairNode. + * @param value the value stored in the node. + */ + PairNode(int value, int priority){ + this.value = value; + this.priority = priority; + } + public int getValue(){ + return value; + } + public int getPriority(){ + return priority; + } + public void setPriority(int priority){ + this.priority = priority; + } + public PairNode getPrev(){ + return prev; + } + public PairNode getNextSibling(){ + return nextSibling; + } + public PairNode getLeftChild(){ + return leftChild; + } + public void setPrev(PairNode prev){ + this.prev = prev; + } + public void setNextSibling(PairNode nextSibling){ + this.nextSibling = nextSibling; + } + public void setLeftChild(PairNode leftChild){ + this.leftChild = leftChild; + } +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/RescueMap.java b/modules/oldsims/rescuecore/tools/mapgenerator/RescueMap.java new file mode 100644 index 0000000000000000000000000000000000000000..fd5cefa331e1edf6bf23ca20f57adbcb70f6a44d --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/RescueMap.java @@ -0,0 +1,628 @@ +/* + * Last change: $Date: 2004/07/11 22:51:53 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * An internal representation of a city map used in Robocup Rescue simulation. + * This includes information on nodes, roads and buildings. + * + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class RescueMap { + /** The number of potential intersections. **/ + private int nodeCount; + /** The number of roads / connections in the graph. **/ + private int roadCount; + /** The x coordinates of each node. **/ + private int[] xs; + /** The y coordinates of each node. **/ + private int[] ys; + /** The adjacency matrix of the graph. **/ + private int[][] roads; + /** Approximate width of the map. **/ + private int width; + /** Approximate height of the map. **/ + private int height; + + /** In-degree of the nodes. **/ + private int[] inDegree; + /** Out-degree of the nodes. **/ + private int[] outDegree; + /** All Euclidean distances between nodes. **/ + private int[][] distances; + /** Whether the distance to or from a node must be recomputed. **/ + private boolean[] invalid; + + /** x coordinates of each building centre. **/ + private int[] buildingXs; + /** y coordinates of each building centre. **/ + private int[] buildingYs; + /** Coordinates of each building's apexes. **/ + private int[][][] buildingApi; + /** A single entrance for each building. **/ + private int[] buildingEntrances; + /** Number of floors for each building. **/ + private int[] buildingFloors; + /** Type of each building. **/ + private int[] buildingTypes; + /** Number of buildings. **/ + private int buildingCount; + + /** + * Constructs new RescueMap. + * + * @param nodeCount The number of potential intersections. + * @param width The approximate width of the map. + * @param height The approximate height of the map. + **/ + public RescueMap(int nodeCount, int width, int height) { + this.width = width; + this.height = height; + this.nodeCount = nodeCount; + xs = new int[nodeCount * 2]; + ys = new int[nodeCount * 2]; + inDegree = new int[nodeCount * 2]; + outDegree = new int[nodeCount * 2]; + roads = new int[nodeCount * 2][nodeCount * 2]; + distances = new int[nodeCount * 2][nodeCount * 2]; + invalid = new boolean[nodeCount * 2]; + roadCount = 0; + + buildingXs = new int[nodeCount * 2]; + buildingYs = new int[nodeCount * 2]; + buildingEntrances = new int[nodeCount * 2]; + buildingApi = new int[nodeCount * 2][][]; + buildingFloors = new int[nodeCount * 2]; + buildingTypes = new int[nodeCount * 2]; + } + + /** + * Set the x coordinate of the node. + * + * @param node The node to change. + * @param x The new x value. + **/ + public void setX(int node, int x) { + if (x != xs[node]) + invalid[node] = true; + xs[node] = x; + } + + /** + * Set the y coordinate of the node. + * + * @param node The node to change. + * @param y The new y value. + **/ + public void setY(int node, int y) { + if (y != ys[node]) + invalid[node] = true; + ys[node] = y; + } + + /** + * Add, remove or resize a road between two nodes. + * + * @param a The 'from' node. + * @param b The 'to' node. + * @param size The number of lanes. + **/ + public void setRoad(int a, int b, int size) { + if (size != 0 && roads[a][b] == 0) { + roadCount++; + inDegree[b]++; + outDegree[a]++; + } else if (size == 0 && roads[a][b] != 0) { + roadCount--; + inDegree[b]--; + outDegree[a]--; + } + roads[a][b] = size; + } + + /** + * Get the number of nodes in this map. + * + * @return The number of nodes. + **/ + public int getNodeCount() { + return nodeCount; + } + + /** + * Get the x coordinate of a node. + * + * @param node The node to get info from. + * @return The x coordinate. + **/ + public int getX(int node) { + return xs[node]; + } + + /** + * Get the y coordinate of a node. + * + * @param node The node to get info from. + * @return The y coordinate. + **/ + public int getY(int node) { + return ys[node]; + } + + /** + * Get the width of the map. + * + * @return The approcimate width. + **/ + public int getWidth() { + return width; + } + + /** + * Get the height of the map. + * + * @return The approximate height. + **/ + public int getHeight() { + return height; + } + + /** + * Gets the indegree of a node. + * + * @param node The node to get info from. + * @return The indegree. + **/ + public int getInDegree(int node) { + return inDegree[node]; + } + + /** + * Gets the outdegree of a node. + * + * @param node The node to get info from. + * @return The outdegree. + **/ + public int getOutDegree(int node) { + return outDegree[node]; + } + + /** + * Gets the size of the road between two nodes. + * + * @param n1 The 'from' node. + * @param n2 The 'to' node. + * @return The number of lanes on the road. + **/ + public int getRoad(int n1, int n2) { + return roads[n1][n2]; + } + + /** + * Adds a node to the map. + **/ + public void addNode() { + if (nodeCount + 1 > xs.length) + resize(); + nodeCount++; + } + + /** + * Adds a new building. Uses the RescueMapToolit function centre() to find the + * centre. + * + * @param api The apexes of the building. + * @param entrance The entrance node. + **/ + public void addBuilding(int[][] api, int entrance, int floors, int type) { + if (buildingCount + 1 > buildingXs.length) + resizeBs(); + buildingCount++; + buildingApi[buildingCount - 1] = new int[api.length][2]; + for (int i = 0; i < api.length; i++) { + buildingApi[buildingCount - 1][i][0] = api[i][0]; + buildingApi[buildingCount - 1][i][1] = api[i][1]; + + } + int[] cent = RescueMapToolkit.centre(api); + buildingXs[buildingCount - 1] = cent[0]; + buildingYs[buildingCount - 1] = cent[1]; + + buildingEntrances[buildingCount - 1] = entrance; + buildingFloors[buildingCount - 1] = floors; + buildingTypes[buildingCount - 1] = type; + } + + /** + * Get a list of roads in the underlying graph. + * + * @return Roads in an array of size [roads][2], with the second dimension + * holing x,y coodinates. + **/ + public int[][] getRoads() { + ArrayList rs = new ArrayList(nodeCount * 4); + for (int i = 0; i < nodeCount; i++) + for (int j = i + 1; j < nodeCount; j++) + if (roads[i][j] > 0 || roads[j][i] > 0) { + rs.add(Integer.valueOf(j)); + rs.add(Integer.valueOf(i)); + } + int[][] rds = new int[rs.size() / 2][2]; + for (int i = 0; i < rs.size(); i += 2) { + rds[i / 2][0] = ((Integer) rs.get(i)).intValue(); + rds[i / 2][1] = ((Integer) rs.get(i + 1)).intValue(); + } + return rds; + } + + /** + * Get a list of neighbours of a node. + * + * @param node The node to get the neighbours of. + * @return A list of neighbouring nodes. + **/ + public int[] getNeighbours(int node) { + ArrayList ns = new ArrayList(10);// should be plenty + for (int i = 0; i < nodeCount; i++) + if (roads[node][i] > 0) + ns.add(Integer.valueOf(i)); + int[] nbs = new int[ns.size()]; + for (int i = 0; i < ns.size(); i++) + nbs[i] = ((Integer) ns.get(i)).intValue(); + return nbs; + } + + /** + * Get a list of neighbours of a node in the underlying graph. + * + * @param node The node to get the neighbours of. + * @return A list of neighbouring nodes in the underlying graph. + **/ + public int[] getUnderlyingNeighbours(int node) { + ArrayList ns = new ArrayList(10);// should be plenty + for (int i = 0; i < nodeCount; i++) + if (roads[node][i] > 0 || roads[i][node] > 0) + ns.add(Integer.valueOf(i)); + int[] nbs = new int[ns.size()]; + for (int i = 0; i < ns.size(); i++) + nbs[i] = ((Integer) ns.get(i)).intValue(); + return nbs; + } + + /** + * Get a list of edge IDs for a node. + * + * @param bIds The IDs assigned to each building. + * @param rIds The IDs assigned to each road. + * @param n The node to find the edges of. + * @return A list of the IDs of all adjacent objects. + **/ + public int[] edges(int[] bIds, int[] rIds, int[][] rs, int n) { + ArrayList es = new ArrayList(10); + for (int i = 0; i < buildingCount; i++) + if (buildingEntrances[i] == n) + es.add(Integer.valueOf(bIds[i])); + for (int i = 0; i < rs.length; i++) + if (rs[i][0] == n || rs[i][1] == n) + es.add(Integer.valueOf(rIds[i])); + int[] edges = new int[es.size()]; + for (int i = 0; i < edges.length; i++) + edges[i] = ((Integer) es.get(i)).intValue(); + return edges; + } + + /** + * Writes the RescueMap to files in the current directory. + */ + public void toFile() throws IOException { + toFile(null); + } + + /** + * Writes this RescueMap to three files - road.bin, building.bin and node.bin in + * the given parent directory + **/ + public void toFile(File parentDir) throws IOException { + // do road.bin + File road = new File(parentDir, "road.bin"); + DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(road))); + + writeInt(out, 0); + writeInt(out, 0); + writeInt(out, 0); + int[][] rds = getRoads(); + writeInt(out, rds.length); + int[] rIds = new int[rds.length]; + int id = nodeCount + 2; + for (int i = 0; i < rds.length; i++) { + writeInt(out, 17); // size + rIds[i] = id; + writeInt(out, id++); // id + writeInt(out, rds[i][0] + 1); // head + writeInt(out, rds[i][1] + 1); // tail + writeInt(out, distance(rds[i][0], rds[i][1])); // length + writeInt(out, 0); // kind + writeInt(out, 0); // cars to head + writeInt(out, 0); // cars to tail + writeInt(out, 0); // humans to head + writeInt(out, 0); // humans to tail + writeInt(out, (roads[rds[i][1]][rds[i][0]] + roads[rds[i][0]][rds[i][1]]) * 2000);// width + writeInt(out, 0); // block + writeInt(out, 0); // repair cost + writeInt(out, 0); // median strip + writeInt(out, roads[rds[i][1]][rds[i][0]]); // lines to head + writeInt(out, roads[rds[i][0]][rds[i][1]]); // lines to tail + writeInt(out, 0); // width for walkers... sorry peds + } + out.flush(); + out.close(); + + // do building.bin + File building = new File(parentDir, "building.bin"); + out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(building))); + + int[] bIds = new int[buildingCount]; + writeInt(out, 0); + writeInt(out, 0); + writeInt(out, 0); + writeInt(out, buildingCount); + for (int i = 0; i < buildingCount; i++) { + writeInt(out, 16 + buildingApi[i].length * 2); // size + bIds[i] = id; + writeInt(out, id++); // id + writeInt(out, buildingXs[i]); // x + writeInt(out, buildingYs[i]); // y + writeInt(out, buildingFloors[i]); // floors + writeInt(out, buildingTypes[i]); // type + writeInt(out, 0); // ignition + writeInt(out, 0); // fieryness + writeInt(out, 0); // brokenness + writeInt(out, 1); // entrances - only 1 + writeInt(out, buildingEntrances[i] + 1); // entrance + writeInt(out, 0); // shape + int area = RescueMapToolkit.area(buildingApi[i]); + writeInt(out, area); // floor area + writeInt(out, area * buildingFloors[i]); // total area + writeInt(out, 0); // building code + writeInt(out, buildingApi[i].length); // num apexes + for (int j = 0; j < buildingApi[i].length; j++) { + writeInt(out, buildingApi[i][j][0]); // x + writeInt(out, buildingApi[i][j][1]); // y + } + } + out.flush(); + out.close(); + + // do node.bin + File node = new File(parentDir, "node.bin"); + out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(node))); + + writeInt(out, 0); + writeInt(out, 0); + writeInt(out, 0); + writeInt(out, nodeCount); // number of nodes + for (int i = 0; i < nodeCount; i++) { + // build the edges list + int[] edges = edges(bIds, rIds, rds, i); + writeInt(out, 6 + 7 * edges.length); + writeInt(out, i + 1); // id + writeInt(out, xs[i]); // x + writeInt(out, ys[i]); // y + writeInt(out, edges.length); // edges + for (int j = 0; j < edges.length; j++) + writeInt(out, edges[j]); // object id + writeInt(out, 0); // signal + for (int j = 0; j < edges.length; j++) + writeInt(out, 0); // shortcut + for (int j = 0; j < edges.length; j++) { + writeInt(out, 0); // pocket + writeInt(out, 0); + } + for (int j = 0; j < edges.length; j++) { + writeInt(out, 0); // signal timing + writeInt(out, 0); + writeInt(out, 0); + } + } + out.flush(); + out.close(); + + } + + /** + * Writes an int to the stream. + * + * @param out The output stream. + * @param n The integer to write. + **/ + private void writeInt(DataOutputStream out, int n) throws IOException { + int result = ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24); + out.writeInt(result); + } + + /** + * Gets the Euclidean distance between two nodes. + * + * @param n1 The first node. + * @param n2 The second node. + * @return The distance between them. + **/ + public int distance(int n1, int n2) { + if (invalid[n1]) { + for (int i = 0; i < nodeCount; i++) { + distances[n1][i] = 0; + distances[i][n1] = 0; + } + invalid[n1] = false; + } + if (invalid[n2]) { + for (int i = 0; i < nodeCount; i++) { + distances[n2][i] = 0; + distances[i][n2] = 0; + } + invalid[n2] = false; + } + if (distances[n1][n2] == 0) { + long x = xs[n1] - xs[n2]; + long y = ys[n1] - ys[n2]; + distances[n1][n2] = (int) Math.sqrt(x * x + y * y); + distances[n2][n1] = distances[n1][n2]; + } + return distances[n1][n2]; + } + + /** + * Checks whether a path exists between two nodes. + * + * @param start The node to start the path from. + * @param end The node we wish to reach. + * @return True if a path exists, otherwise false. + **/ + public boolean pathExists(int start, int end) { + boolean[] visited = new boolean[nodeCount]; + // find a path with a greedy search + PairHeap q = new PairHeap(); + visited[start] = true; + int next = start; + try { + while (next != end) { + // check every neighbour of next - add if necessary + for (int j = 0; j < nodeCount; j++) { + if (roads[next][j] > 0 && !visited[j]) { // connected + visited[j] = true; + int x = xs[j] - xs[end]; + int y = ys[j] - ys[end]; + int d = (int) Math.sqrt(x * x + y * y); + q.insert(j, d); + } + } + next = q.deleteMin(); + } + return true; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Finds the lowest x and y coordinates and aligns the map so these have value + * 1. + **/ + public void align() { + // get min values + int x = Integer.MAX_VALUE; + int y = x; + for (int i = 0; i < nodeCount; i++) { + if (xs[i] < x) + x = xs[i]; + if (ys[i] < y) + y = ys[i]; + } + for (int i = 0; i < buildingCount; i++) { + if (buildingXs[i] < x) + x = buildingXs[i]; + if (buildingYs[i] < y) + y = buildingYs[i]; + for (int j = 0; j < buildingApi[i].length; j++) { + if (buildingApi[i][j][0] < x) + x = buildingApi[i][j][0]; + if (buildingApi[i][j][1] < y) + y = buildingApi[i][j][1]; + } + } + // now alter them all + int dx = x - 1; + int dy = y - 1; + System.out.println("Shifting by (" + (-dx) + "," + (-dy) + ")."); + for (int i = 0; i < nodeCount; i++) { + xs[i] -= dx; + ys[i] -= dy; + } + for (int i = 0; i < buildingCount; i++) { + buildingXs[i] -= dx; + buildingYs[i] -= dy; + for (int j = 0; j < buildingApi[i].length; j++) { + buildingApi[i][j][0] -= dx; + buildingApi[i][j][1] -= dy; + } + } + } + + /** + * Adjusts the data structures when number of nodes gets too large. + **/ + private void resize() { + int newSize = (xs.length * 3) / 2; + int[] newXs = new int[newSize]; + int[] newYs = new int[newSize]; + int[] newIn = new int[newSize]; + int[] newOut = new int[newSize]; + int[][] newRoads = new int[newSize][newSize]; + int[][] newDist = new int[newSize][newSize]; + boolean[] newVal = new boolean[newSize]; + System.arraycopy(xs, 0, newXs, 0, nodeCount); + System.arraycopy(ys, 0, newYs, 0, nodeCount); + System.arraycopy(inDegree, 0, newIn, 0, nodeCount); + System.arraycopy(outDegree, 0, newOut, 0, nodeCount); + System.arraycopy(invalid, 0, newVal, 0, nodeCount); + for (int i = 0; i < nodeCount; i++) { + System.arraycopy(roads[i], 0, newRoads[i], 0, nodeCount); + System.arraycopy(distances[i], 0, newDist[i], 0, nodeCount); + } + xs = newXs; + ys = newYs; + inDegree = newIn; + outDegree = newOut; + roads = newRoads; + distances = newDist; + invalid = newVal; + } + + /** + * Adjusts the data structures when number of buildings gets too large. + **/ + private void resizeBs() { + int newSize = (buildingXs.length * 2); + int[] newXs = new int[newSize]; + int[] newYs = new int[newSize]; + int[] newEnt = new int[newSize]; + int[] newFloors = new int[newSize]; + int[] newTypes = new int[newSize]; + int[][][] newApi = new int[newSize][][]; + System.arraycopy(buildingXs, 0, newXs, 0, buildingCount); + System.arraycopy(buildingYs, 0, newYs, 0, buildingCount); + System.arraycopy(buildingEntrances, 0, newEnt, 0, buildingCount); + System.arraycopy(buildingFloors, 0, newFloors, 0, buildingCount); + System.arraycopy(buildingTypes, 0, newTypes, 0, buildingCount); + for (int i = 0; i < buildingCount; i++) + newApi[i] = buildingApi[i]; + + buildingXs = newXs; + buildingYs = newYs; + buildingEntrances = newEnt; + buildingFloors = newFloors; + buildingTypes = newTypes; + buildingApi = newApi; + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/RescueMapToolkit.java b/modules/oldsims/rescuecore/tools/mapgenerator/RescueMapToolkit.java new file mode 100644 index 0000000000000000000000000000000000000000..9a93e1207c10628aa805fac6a7efd7e46bea762d --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/RescueMapToolkit.java @@ -0,0 +1,580 @@ +/* + * Last change: $Date: 2004/07/11 22:51:54 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.*; + +/** + * A toolkit class with useful methods for manipulating RescueMaps. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class RescueMapToolkit{ + + /** + * Shifts some nodes in the map by random distances and removes + * a number of randomly selected roads. + * @param m The RescueMap to alter. + * @param uniformity Indicates how likely any given node is to be moved, from 0..100. + * @param density Indicates how likely it is that a road be removed, from 0..100. + * @param movement The maximum distance a node will be moved in meters. + * @param nooneway Whether we are allowed to remove roads in only one direction at a time. + * @param rand The random number generator. + **/ + public static void randomise(RescueMap m, int uniformity, int density, int removal, int movement, boolean nooneway, Random rand){ + int nodeCount = m.getNodeCount(); + //between 100% and 0% of roads moved + System.out.println("Adjusting nodes by up to "+movement+"m."); + for(int i = 0; i < nodeCount; i++){ + if(rand.nextDouble()*100 > uniformity){ + m.setX(i,m.getX(i)+(int)((rand.nextDouble()-0.5)*movement*2000)); + m.setY(i,m.getY(i)+(int)((rand.nextDouble()-0.5)*movement*2000)); + } + } + //remove 5% - 90% of the roads + double thresh = 0.01*removal; + System.out.println("Removing about "+((int)(thresh*100))+"% of roads."); + int[][] roads = m.getRoads(); + for(int i = 0; i < roads.length; i++){ + if(rand.nextDouble() < thresh){ + //try both directions + m.setRoad(roads[i][0],roads[i][1],0); + m.setRoad(roads[i][1],roads[i][0],0); + //now test connectedness + if(!m.pathExists(roads[i][1],roads[i][0])){ + //try one direction + m.setRoad(roads[i][1],roads[i][0],1); + if(!m.pathExists(roads[i][0],roads[i][1])) + m.setRoad(roads[i][0],roads[i][1],1); //replace both directions.. + } + else if(!m.pathExists(roads[i][0],roads[i][1])) + m.setRoad(roads[i][0],roads[i][1],1); //reset the other road + } + else if(!nooneway && rand.nextDouble() < thresh*0.2){ + //let's make it 1-way! + m.setRoad(roads[i][0],roads[i][1],0); + if(!m.pathExists(roads[i][0],roads[i][1])) + m.setRoad(roads[i][0],roads[i][1],1); + } + } + } + + /** + * Rotate all nodes of a RescueMap. This should be applied BEFORE any + * buildings are added. + * @param m The RescueMap to rotate. + * @param radians The number of radians to rotate by. + **/ + public static void rotate(RescueMap m,double radians){ + int width = m.getWidth(); + int height = m.getHeight(); + int nodeCount = m.getNodeCount(); + for(int i = 0; i < nodeCount; i++){ + double x = m.getX(i) - width/2; + double y = m.getY(i) - height/2; + double radius = Math.sqrt(x*x+y*y); + double theta = Math.acos(x/radius); + if(y > 0) + theta = Math.PI*2-theta; + x = radius*Math.cos(theta+radians); + y = radius*Math.sin(theta+radians); + m.setX(i,(int)x+width/2); + m.setY(i,(int)y+height/2); + } + } + + /** + * Finds any overlapping roads and creates a new node + * at their intersection. + * @param m The RescueMap to work on. + **/ + public static void findIntersections(RescueMap m){ + int[][] rs = m.getRoads(); + //now check all pairs of roads + for(int i = 0; i < rs.length; i++){ + int n1 = rs[i][0]; + int n2 = rs[i][1]; + for(int j = 0; j < rs.length; j++){ + int n3 = rs[j][0]; + int n4 = rs[j][1]; + if(n1 == n3 || n1 == n4 || n2 == n3 || n2 == n4) + continue; //shared node + int x1 = m.getX(n1); + int x2 = m.getX(n2); + int x3 = m.getX(n3); + int x4 = m.getX(n4); + int y1 = m.getY(n1); + int y2 = m.getY(n2); + int y3 = m.getY(n3); + int y4 = m.getY(n4); + //get intersection + int d = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + if(d == 0) + continue; //parallel roads + double a = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))*1.0/d; + int x = x1+(int)(a*(x2-x1)); + int y = y1+(int)(a*(y2-y1)); + //check it is within the roads + if(x > Math.min(x1,x2) && x < Math.max(x1,x2) + && x < Math.max(x3,x4) && x > Math.min(x3,x4) + && y > Math.min(y1,y2) && y < Math.max(y1,y2) + && y < Math.max(y3,y4) && y > Math.min(y3,y4)){ //they cross!! + intersect(m,n1,n2,n3,n4,x,y); + rs = m.getRoads(); + //start again + i = Math.max(0,i-4); + j = 0; + break; + } + } + } + } + /** + * Used by findIntersections() to deal with a single pair of + * overlapping roads. + **/ + private static void intersect(RescueMap m, int n1, int n2, int n3, int n4, int x, int y){ + m.addNode(); + int nodeCount = m.getNodeCount(); + //make the node + m.setX(nodeCount-1,x); + m.setY(nodeCount-1,y); + //split the two roads + m.setRoad(n1,nodeCount-1,m.getRoad(n1,n2)); + m.setRoad(n2,nodeCount-1,m.getRoad(n2,n1)); + m.setRoad(n3,nodeCount-1,m.getRoad(n3,n4)); + m.setRoad(n4,nodeCount-1,m.getRoad(n4,n3)); + m.setRoad(nodeCount-1,n1,m.getRoad(n2,n1)); + m.setRoad(nodeCount-1,n2,m.getRoad(n1,n2)); + m.setRoad(nodeCount-1,n3,m.getRoad(n4,n3)); + m.setRoad(nodeCount-1,n4,m.getRoad(n3,n4)); + //delete the old roads + m.setRoad(n1,n2,0); + m.setRoad(n2,n1,0); + m.setRoad(n3,n4,0); + m.setRoad(n4,n3,0); + } + + /** + * Rounds the corners of wide roads. + * @param m The RescueMap to smooth the roads of. + * @param size The minimum size of roads to smooth - given in total lanes for both directions. + **/ + public static void smoothRoads(RescueMap m, int size){ + int max = m.getNodeCount(); + for(int i = 0; i < max; i++){ + if(m.getOutDegree(i) >= 2 && m.getInDegree(i) >= 2){ //candidate + //find two roads + int n1 = -1; + int n3 = -1; + for(int j = 0; j < m.getNodeCount(); j++){ + if(m.getRoad(i,j) + m.getRoad(j,i) >= size){ + if(n1 == -1) + n1 = j; + else if(n3 == -1) + n3 = j; + else{ + n1 = -1; + n3 = -1; + break; + } + } + } + if(n1 != -1 && n3 != -1) + smooth(m,n1,i,n3); + } + } + } + + /** + * Used by smoothRoads() to deal with a single corner. + **/ + private static void smooth(RescueMap m, int n1, int n2, int n3){ + //find the mid point + int x = (m.getX(n1)+m.getX(n3))/2; + int y = (m.getY(n1)+m.getY(n3))/2; + //make two nodes halfway up each road + m.addNode(); + m.addNode(); + int nodeCount = m.getNodeCount(); + m.setX(nodeCount-2,(m.getX(n1)+m.getX(n2))/2); + m.setY(nodeCount-2,(m.getY(n1)+m.getY(n2))/2); + m.setX(nodeCount-1,(m.getX(n3)+m.getX(n2))/2); + m.setY(nodeCount-1,(m.getY(n3)+m.getY(n2))/2); + //.. and drag toward mid point + m.setX(nodeCount-2,(int)(m.getX(nodeCount-2)*0.75+x*0.25)); + m.setY(nodeCount-2,(int)(m.getY(nodeCount-2)*0.75+y*0.25)); + m.setX(nodeCount-1,(int)(m.getX(nodeCount-1)*0.75+x*0.25)); + m.setY(nodeCount-1,(int)(m.getY(nodeCount-1)*0.75+y*0.25)); + //shift the old roads + m.setRoad(n1,nodeCount-2,m.getRoad(n1,n2)); + m.setRoad(nodeCount-2,n1,m.getRoad(n2,n1)); + m.setRoad(n2,nodeCount-2,m.getRoad(n2,n1)); + m.setRoad(nodeCount-2,n2,m.getRoad(n1,n2)); + m.setRoad(n3,nodeCount-1,m.getRoad(n3,n2)); + m.setRoad(nodeCount-1,n3,m.getRoad(n2,n3)); + m.setRoad(n2,nodeCount-1,m.getRoad(n2,n3)); + m.setRoad(nodeCount-1,n2,m.getRoad(n3,n2)); + m.setRoad(n1,n2,0); + m.setRoad(n2,n1,0); + m.setRoad(n3,n2,0); + m.setRoad(n2,n3,0); + //get the mid point of the new nodes + x = (m.getX(nodeCount-2)+m.getX(nodeCount-1))/2; + y = (m.getY(nodeCount-2)+m.getY(nodeCount-1))/2; + //shift the middle node toward them + m.setX(n2,(int)(m.getX(n2)*0.25+x*0.75)); + m.setY(n2,(int)(m.getY(n2)*0.25+y*0.75)); + } + + /** + * Gets the angle between two connected sets of nodes. + * @param m The RescueMap holding the nodes. + * @param n1 The first node. + * @param n2 The connecting (middle) node. + * @param n3 The third node. + * @return A number between 0 and Pi*2. Eg: 0.01 indicates a road turning slightly left, 6.28 indicates one turning slightly right. + **/ + public static double angle(RescueMap m, int n1, int n2, int n3){ + //get the vector for the two roads + double x1 = m.getX(n2) - m.getX(n1); + double y1 = m.getY(n2) - m.getY(n1); + double x2 = m.getX(n3) - m.getX(n2); + double y2 = m.getY(n3) - m.getY(n2); + //get the length of the vectors + double mag1 = Math.sqrt(x1*x1+y1*y1); + double mag2 = Math.sqrt(x2*x2+y2*y2); + //and the angles + double theta1 = Math.acos(x1/mag1); + double theta2 = Math.acos(x2/mag2); + if(y1 < 0) + theta1 = Math.PI*2 - theta1; + if(y2 < 0) + theta2 = Math.PI*2 - theta2; + double theta = theta2-theta1; + if(theta < 0) + theta += Math.PI*2; + //just a hack here - should be merged above this + if(theta > Math.PI) + theta = Math.PI*2 - theta; + else + theta = -theta; + return theta; + } + + /** + * Gets the centre of a building from its apexes. + * Not a smart algorithm. + * @param api The apexes of the building. + * @return An integer array of form {x,y} giving the centre coordinates. + **/ + public static int[] centre(int[][] api){ + int[] centre = new int[2]; + for(int i = 0; i < api.length-1; i++){ + centre[0] += api[i][0]; + centre[1] += api[i][1]; + } + centre[0] /= (api.length-1); + centre[1] /= (api.length-1); + return centre; + } + + /** + * Find the area of a building (in 100ths of sqr meters) from the apexes. + * The apexes are assumed to double up on first and last vertices. + * @param api The apexes of the building. + * @return The area. + **/ + public static int area(int[][] api){ + long area = 0; + long[] distances = new long[api.length]; + for(int i = 0; i < api.length; i++){ + //use point 0,0 as reference for triangles - compute 'em! + long a = (long)api[i][0]; + long b = (long)api[i][1]; + distances[i] = (long)Math.sqrt(a*a+b*b); + } + //now do the areas + + for(int i = 0; i < api.length-1; i++){ + long ar = tArea(api[i][0],api[i][1],distances[i],api[i+1][0],api[i+1][1],distances[i+1]); + if(isLeft(0,0,api[i][0],api[i][1],api[i+1][0],api[i+1][1])) + area -= ar; + else + area += ar; + } + area /= 10000; + return (int)area; + } + + /** + * Area of the triangle formed by two vectors. + * @param x1 x coordinate of the first vector. + * @param y1 y coordinate of the first vector. + * @param m1 The magnitude of the first vector. + * @param x2 x coordinate of the second vector. + * @param y2 y coordinate of the second vector. + * @param m2 The magnitude of the second vector. + * @return The area of the triangle. + **/ + public static long tArea(int x1, int y1, long m1, int x2, int y2, long m2){ + double theta1 = 0; + double theta2 = 0; + if(m1 != 0) + theta1 = Math.asin(y1*1.0/m1); + if(m2 != 0) + theta2 = Math.asin(y2*1.0/m2); + if(theta1-theta2 == 0) + return 0; + double a = m1*m2*Math.sin(theta1-theta2)/2; + return (long)Math.abs(a); + } + + /** + * Whether a point is left of a directed line. + * @param x The x coordinate of the point. + * @param y The y coordinate of the point. + * @param ax The x coordinate of the first point of the line. + * @param ay The y coordinate of the first point of the line. + * @param bx The x coordinate of the second point of the line. + * @param by The y coordinate of the second point of the line. + * @return True if x,y is to the left of the line, otherwise false. + **/ + public static boolean isLeft(int x, int y, int ax, int ay, int bx, int by){ + //make a the origin + x -= ax; + bx -= ax; + y -= by; + by -= ay; + //get the normal to a-b + long nx = (long)-by; + long ny = (long)bx; + //find magnitude of projection of x,y onto the normal + long m = nx*x + ny*y; + if(m > 0) + return true; + return false; + } + + /** + * Gets the closest point on a road to a given point. + * @param m The RescueMap holding the road. + * @param x The x coordinate of the point. + * @param y The y coordinate of the point. + * @param n1 The first node of the road. + * @param n2 The second node of the road. + * @return The coordinates of the nearest point as an array of form {x,y}. + **/ + public static int[] nearestPoint(RescueMap m, int x, int y, int n1, int n2){ + //use n1 as origin + long x2 = m.getX(n2) - m.getX(n1); + long y2 = m.getY(n2) - m.getY(n1); + x -= m.getX(n1); + y -= m.getY(n1); + //project x,y onto n2 + int d = (int)Math.sqrt(x2*x2+y2*y2); + int len = (int)((x*x2 + y*y2)/d); + //check for points out of the road's bounds + if(len >= d) + return new int[]{m.getX(n2),m.getY(n2)}; + if(len <= 0) + return new int[]{m.getX(n1),m.getY(n1)}; + + double alt = len*1.0/d; + + return new int[]{(int)(x2*alt)+m.getX(n1),(int)(y2*alt)+m.getY(n1)}; + } + + /** + * Splits a road to make a new entrance node. This will be the nearest + * point on the nearest road in the map. + * @param rm The RescueMap to add the entrance node to. + * @param centre The centre of the building that needs an entrance. + * @return The new entrance node. + **/ + public static int makeEntrance(RescueMap rm, int[] centre){ + int[][] roads = rm.getRoads(); + int[] min = null; + int minD = Integer.MAX_VALUE; + int ind = -1; + for(int i = 0; i < roads.length; i++){ + int[] p = RescueMapToolkit.nearestPoint(rm,centre[0],centre[1],roads[i][0],roads[i][1]); + long dx = centre[0]-p[0]; + long dy = centre[1]-p[1]; + int d = (int)Math.sqrt(dx*dx + dy*dy); + if(minD > d){ + min = p; + minD = d; + ind = i; + } + } + //check to see if we are using an existing node + if(min[0] == rm.getX(roads[ind][0]) && min[1] == rm.getY(roads[ind][0])) + return roads[ind][0]; + if(min[0] == rm.getX(roads[ind][1]) && min[1] == rm.getY(roads[ind][1])) + return roads[ind][1]; + //make a new node at the closest point + rm.addNode(); + int nodeCount = rm.getNodeCount(); + rm.setX(nodeCount-1,min[0]); + rm.setY(nodeCount-1,min[1]); + rm.setRoad(roads[ind][0],nodeCount-1,rm.getRoad(roads[ind][0],roads[ind][1])); + rm.setRoad(nodeCount-1,roads[ind][0],rm.getRoad(roads[ind][1],roads[ind][0])); + rm.setRoad(roads[ind][1],nodeCount-1,rm.getRoad(roads[ind][1],roads[ind][0])); + rm.setRoad(nodeCount-1,roads[ind][1],rm.getRoad(roads[ind][0],roads[ind][1])); + rm.setRoad(roads[ind][1],roads[ind][0],0); + rm.setRoad(roads[ind][0],roads[ind][1],0); + return nodeCount-1; + } + + /** + * Splits a building across a line. The building should not yet be added + * to a RescueMap. + * @param build The apexes of the building. + * @param x1 The x coordinates of the first point on the line. + * @param y1 The y coordinates of the first point on the line. + * @param x2 The x coordinates of the second point on the line. + * @param y2 The y coordinates of the second point on the line. + * @return The two new polygons. An array of size [2][polygonsize][coordinates]. + **/ + public static int[][][] split(int[][] build, int x1, int y1, int x2, int y2){ + //find two points to split by + int splitA = -1; //indices of sides to split + int splitB = -1; + int[] spA = null; + int[] spB = null; + + for(int i = 0; i < build.length-1; i++){ + int[] coords = intersectionI(x1,y1,x2,y2,build[i][0],build[i][1],build[i+1][0],build[i+1][1]); + if(coords[0] != -1){ //found a splitting side! + if(splitA == -1){ + splitA = i; + spA = coords; + } + else if(splitB == -1){ + splitB = i; + spB = coords; + break; //found two splits - this will do + } + } + } + if(spB == null) + return null; + + //now generate the two polygons + int[][][] split = new int[2][][]; + split[0] = new int[splitB-splitA+3][2]; + split[1] = new int[build.length - (splitB-splitA) + 2][2]; + //make the first poly (anti clockwise) + int ind = 0; + for(int i = splitA+1; i <= splitB; i++){ + split[0][ind][0] = build[i][0]; + split[0][ind++][1] = build[i][1]; + } + split[0][split[0].length-3] = spB; + split[0][split[0].length-2] = spA; + split[0][split[0].length-1][0] = split[0][0][0]; + split[0][split[0].length-1][1] = split[0][0][1]; + //now make 2nd poly (anti clockwise) + for(ind = 0; ind <= splitA; ind++){ + split[1][ind][0] = build[ind][0]; + split[1][ind][1] = build[ind][1]; + } + split[1][ind][0] = spA[0]; + split[1][ind++][1] = spA[1]; + split[1][ind][0] = spB[0]; + split[1][ind++][1] = spB[1]; + for(int i = splitB+1; i < build.length; i++){ + split[1][ind][0] = build[i][0]; + split[1][ind++][1] = build[i][1]; + } + return split; + } + + /** + * Finds if an intersection of a line with a polygon exists. + * @param x1 x coordinate of the first point on the line. + * @param y1 y coordinate of the first point on the line. + * @param x2 x coordinate of the second point on the line. + * @param y2 y coordinate of the second point on the line. + * @param api The apexes of the polygon to check. + * @return True if an intersection exists, otherwise false. + **/ + public static boolean intersects(int x1, int y1, int x2, int y2, int[][] api){ + for(int i = 0; i < api.length-1; i++){ + int x3 = api[i][0]; + int x4 = api[i+1][0]; + int y3 = api[i][1]; + int y4 = api[i+1][1]; + //get intersection + if(intersection(x1,y1,x2,y2,x3,y3,x4,y4)[0] != -1) + return true; + } + return false; + } + + /** + * Whether two line segments intersect. + * @param x1 x coordinate of the first point on the first line. + * @param y1 y coordinate of the first point on the first line. + * @param x2 x coordinate of the second point on the first line. + * @param y2 y coordinate of the second point on the first line. + * @param x3 x coordinate of the first point on the second line. + * @param y3 y coordinate of the first point on the second line. + * @param x4 x coordinate of the second point on the second line. + * @param y4 y coordinate of the second point on the second line. + **/ + private static int[] intersection(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4){ + long d = ((long)(y4-y3))*(x2-x1) - ((long)(x4-x3))*(y2-y1); + if(d == 0) + return new int[]{-1,-1}; //parallel lines + double a = (((long)(x4-x3))*(y1-y3) - ((long)(y4-y3))*(x1-x3))*1.0/d; + int x = x1+(int)(a*(x2-x1)); + int y = y1+(int)(a*(y2-y1)); + //check they are within the bounds of the lines + if(x >= Math.min(x1,x2) && x <= Math.max(x1,x2) + && x <= Math.max(x3,x4) && x >= Math.min(x3,x4) + && y >= Math.min(y1,y2) && y <= Math.max(y1,y2) + && y <= Math.max(y3,y4) && y >= Math.min(y3,y4)) //they cross!! + return new int[]{x,y}; + return new int[]{-1,-1}; + } + + /** + * Whether an infinite line and a line segment intersect. + * @param x1 x coordinate of the first point on the infinite line. + * @param y1 y coordinate of the first point on the infinite line. + * @param x2 x coordinate of the second point on the infinite line. + * @param y2 y coordinate of the second point on the infinite line. + * @param x3 x coordinate of the first point on the line segment. + * @param y3 y coordinate of the first point on the line segment. + * @param x4 x coordinate of the second point on the line segment. + * @param y4 y coordinate of the second point on the line segment. + **/ + private static int[] intersectionI(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4){ + long d = ((long)(y4-y3))*(x2-x1) - ((long)(x4-x3))*(y2-y1); + if(d == 0) + return new int[]{-1,-1}; //parallel lines + double a = (((long)(x4-x3))*(y1-y3) - ((long)(y4-y3))*(x1-x3))*1.0/d; + int x = x1+(int)(a*(x2-x1)); + int y = y1+(int)(a*(y2-y1)); + //check they are within the bounds of the finite line + if(x <= Math.max(x3,x4) && x >= Math.min(x3,x4) + && y <= Math.max(y3,y4) && y >= Math.min(y3,y4)) //they cross!! + return new int[]{x,y}; + return new int[]{-1,-1}; + } +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/RoadSetFactory.java b/modules/oldsims/rescuecore/tools/mapgenerator/RoadSetFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..5c860a7734b024541c48d966af2d2d56f158bab9 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/RoadSetFactory.java @@ -0,0 +1,81 @@ +/* + * Last change: $Date: 2004/07/11 22:51:54 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.Random; + +/** + * A class that provides a factory method for creating and setting up a RescueMap. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public class RoadSetFactory{ + /** Minimum road length for grid initial condition. **/ + private static final int MIN_START_DISTANCE = 30000; //30m road + /** Maximum road length for grid initial condition. **/ + private static final int MAX_START_DISTANCE = 80000; //80m road + + /** + * Creates a RescueMap, then initialises its nodes and roads. + * @param scheme The name of the initialisation scheme to use. + * @param width Approximate width to make the map in millimeters. + * @param height Approximate height to make the map in millimeters. + * @param density How dense to make the roads of map - from 0..100. + * @param rand The random number generator. + * @return A new road map. Null if an invalid scheme is used. + **/ + public static RescueMap createRoadSet(String scheme, int width, int height, int density, Random rand){ + //try to find a scheme we know + if(scheme.equals(" ")){ + return null; + } + else + return grid(width,height,density); + } + + /** + * Constructs a new RescueMap uniformly distributed over the given rectangle. + * The nodes are connected in a grid. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param density How dense to make the roads of map - from 0..100. + * @return A new RescueMap laid out in a grid. + **/ + private static RescueMap grid(int width, int height, int density){ + int spacing = (100-density)*(MAX_START_DISTANCE-MIN_START_DISTANCE)/100 + MIN_START_DISTANCE; + int xCount = width/spacing; + int yCount = height/spacing; + System.out.println("Creating a "+xCount+"x"+yCount+" grid = "+xCount*yCount+" intersections."); + RescueMap rm = new RescueMap(xCount*yCount,width,height); + for(int i = 0; i < yCount; i++) + for(int j = 0; j < xCount; j++){ + int index = i*xCount+j; + rm.setX(index,j*spacing); + rm.setY(index, i*spacing); + if(i > 0){ + rm.setRoad(index,index-xCount,1); + rm.setRoad(index-xCount,index,1); + } + if(j > 0){ + rm.setRoad(index,index-1,1); + rm.setRoad(index-1,index,1); + } + } + return rm; + } + + + } diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/RoadWeighter.java b/modules/oldsims/rescuecore/tools/mapgenerator/RoadWeighter.java new file mode 100644 index 0000000000000000000000000000000000000000..f2f782a2a4879403fab7854043c746b8ec0cecf3 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/RoadWeighter.java @@ -0,0 +1,36 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import java.util.Random; + +/** + * Interface to classes that set the number of lanes of roads + * in a RescueMap. + * @author Jonathan Teutenberg + * @version 1.0 Aug 2003 + **/ +public interface RoadWeighter{ + + /** + * Alters the number of lanes on roads in the map. + * @param rm The RescueMap to alter + * @param nooneway Whether different number of lanes in each direction is permitted + * @param rand The random number generator + **/ + public void connect(RescueMap rm, int uniformity, boolean nooneway, Random rand); + +} diff --git a/modules/oldsims/rescuecore/tools/mapgenerator/ScenarioMaker.java b/modules/oldsims/rescuecore/tools/mapgenerator/ScenarioMaker.java new file mode 100644 index 0000000000000000000000000000000000000000..4b61dd077fe7e4e325108ec946a3190984e6385e --- /dev/null +++ b/modules/oldsims/rescuecore/tools/mapgenerator/ScenarioMaker.java @@ -0,0 +1,1135 @@ +/* + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.mapgenerator; + +import rescuecore.*; +import rescuecore.view.*; +import rescuecore.objects.*; +import rescuecore.tools.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import javax.swing.*; +import java.io.*; +import java.util.Collection; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import java.util.HashMap; + +public class ScenarioMaker { + private enum Tool { + PLACE_ROADS("Place blocked roads"), + CLEAR_ROADS("Remove blocked roads"), + PLACE_FIRES("Place fires"), + CLEAR_FIRES("Remove fires"), + PLACE_REFUGES("Place refuges"), + CLEAR_REFUGES("Remove refuges"), + PLACE_FIRE_BRIGADES("Place fire brigades"), + CLEAR_FIRE_BRIGADES("Remove fire brigades"), + PLACE_FIRE_STATIONS("Place fire stations"), + CLEAR_FIRE_STATIONS("Remove fire stations"), + PLACE_POLICE_FORCES("Place Police forces"), + CLEAR_POLICE_FORCES("Remove police forces"), + PLACE_POLICE_OFFICES("Place police offices"), + CLEAR_POLICE_OFFICES("Remove police offices"), + PLACE_AMBULANCE_TEAMS("Place ambulance teams"), + CLEAR_AMBULANCE_TEAMS("Remove ambulance teams"), + PLACE_AMBULANCE_CENTRES("Place ambulance centres"), + CLEAR_AMBULANCE_CENTRES("Remove ambulance centres"), + PLACE_CIVILIANS("Place civilians"), + CLEAR_CIVILIANS("Remove civilians"), + INCREASE_IMPORTANCE("Increase building importance"), + DECREASE_IMPORTANCE("Decrease building importance"), + INSPECTOR("Object inspector"); + + private String name; + + Tool(String s) { + name = s; + } + + public String getName() { + return name; + } + }; + + private Road[] allRoads; + private Node[] allNodes; + private Building[] allBuildings; + private Memory memory; + private Map map; + private Point pressPoint; + private Point dragPoint; + private Layer overlay; + private Layer roadLayer; + private Layer buildingLayer; + private Layer humanoidLayer; + private Tool tool; + private JLabel currentTool; + private ObjectInspector inspector; + private ObjectSelector selector; + private int nextID = 1000000; + private Summary summary; + + public ScenarioMaker(Road[] roads, Node[] nodes, Building[] buildings, final boolean oldGIS) { + allRoads = roads; + allNodes = nodes; + allBuildings = buildings; + memory = new HashMemory(); + for (int i=0;i<roads.length;++i) { + if (memory.lookup(roads[i].getID())!=null) System.err.println("WARNING: Duplicate road ID: "+roads[i].getID()+", this is is already used by "+memory.lookup(roads[i].getID())); + memory.add(roads[i],0,this); + } + for (int i=0;i<nodes.length;++i) { + if (memory.lookup(nodes[i].getID())!=null) System.err.println("WARNING: Duplicate node ID: "+nodes[i].getID()+", this is is already used by "+memory.lookup(nodes[i].getID())); + memory.add(nodes[i],0,this); + } + for (int i=0;i<buildings.length;++i) { + if (memory.lookup(buildings[i].getID())!=null) System.err.println("WARNING: Duplicate building ID: "+buildings[i].getID()+", this is is already used by "+memory.lookup(buildings[i].getID())); + memory.add(buildings[i],0,this); + } + Arrays.sort(allRoads,new RoadSorter(memory)); + map = new Map(memory); + roadLayer = Layer.createRoadLayer(memory); + roadLayer.addRenderer(Road.class,new BigRoadRenderer()); + buildingLayer = Layer.createBuildingLayer(memory); + buildingLayer.addRenderer(Building.class,new ImportantBuildingRenderer(BuildingRenderer.ordinaryBuildingRenderer())); + humanoidLayer = Layer.createHumanoidLayer(memory); + humanoidLayer.addRenderer(Humanoid.class,new HumanoidCountRenderer(HumanoidRenderer.ordinaryHumanoidRenderer())); + map.addLayer(roadLayer); + map.addLayer(buildingLayer); + map.addLayer(humanoidLayer); + map.addLayer(Layer.createNodeLayer(memory)); + overlay = Layer.createOverlayLayer("Overlay"); + map.addLayer(overlay); + map.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + handleClick(e); + } + public void mousePressed(MouseEvent e) { + handlePress(e); + } + public void mouseReleased(MouseEvent e) { + handleRelease(e); + } + }); + map.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent e) { + handleDrag(e); + } + }); + final Action saveAction = new AbstractAction("Save") { + public void actionPerformed(ActionEvent e) { + save(oldGIS); + } + }; + final Action randomiseAction = new AbstractAction("Randomise") { + public void actionPerformed(ActionEvent e) { + randomise(); + } + }; + JMenuBar menubar = new JMenuBar(); + JMenu file = new JMenu("File"); + JMenu tools = new JMenu("Tools"); + JMenu agentTools = new JMenu("Agents"); + JMenu fb = new JMenu("Fire brigades"); + JMenu fs = new JMenu("Fire stations"); + JMenu pf = new JMenu("Police forces"); + JMenu po = new JMenu("Police offices"); + JMenu at = new JMenu("Ambulance teams"); + JMenu ac = new JMenu("Ambulance centres"); + JMenu civ = new JMenu("Civilians"); + file.add(saveAction); + tools.add(new ToolAction(Tool.PLACE_ROADS)); + tools.add(new ToolAction(Tool.CLEAR_ROADS)); + tools.addSeparator(); + tools.add(new ToolAction(Tool.PLACE_FIRES)); + tools.add(new ToolAction(Tool.CLEAR_FIRES)); + tools.add(new ToolAction(Tool.PLACE_REFUGES)); + tools.add(new ToolAction(Tool.CLEAR_REFUGES)); + tools.addSeparator(); + agentTools.add(fb); + agentTools.add(fs); + agentTools.add(pf); + agentTools.add(po); + agentTools.add(at); + agentTools.add(ac); + agentTools.add(civ); + fb.add(new ToolAction(Tool.PLACE_FIRE_BRIGADES,"Place")); + fb.add(new ToolAction(Tool.CLEAR_FIRE_BRIGADES,"Remove")); + fs.add(new ToolAction(Tool.PLACE_FIRE_STATIONS,"Place")); + fs.add(new ToolAction(Tool.CLEAR_FIRE_STATIONS,"Remove")); + pf.add(new ToolAction(Tool.PLACE_POLICE_FORCES,"Place")); + pf.add(new ToolAction(Tool.CLEAR_POLICE_FORCES,"Remove")); + po.add(new ToolAction(Tool.PLACE_POLICE_OFFICES,"Place")); + po.add(new ToolAction(Tool.CLEAR_POLICE_OFFICES,"Remove")); + at.add(new ToolAction(Tool.PLACE_AMBULANCE_TEAMS,"Place")); + at.add(new ToolAction(Tool.CLEAR_AMBULANCE_TEAMS,"Remove")); + ac.add(new ToolAction(Tool.PLACE_AMBULANCE_CENTRES,"Place")); + ac.add(new ToolAction(Tool.CLEAR_AMBULANCE_CENTRES,"Remove")); + civ.add(new ToolAction(Tool.PLACE_CIVILIANS,"Place")); + civ.add(new ToolAction(Tool.CLEAR_CIVILIANS,"Remove")); + tools.add(agentTools); + tools.addSeparator(); + tools.add(new ToolAction(Tool.INCREASE_IMPORTANCE)); + tools.add(new ToolAction(Tool.DECREASE_IMPORTANCE)); + tools.addSeparator(); + tools.add(randomiseAction); + tools.addSeparator(); + tools.add(new ToolAction(Tool.INSPECTOR)); + menubar.add(file); + menubar.add(tools); + summary = new Summary(); + JFrame frame = new JFrame("Scenario Maker"); + JPanel top = new JPanel(new BorderLayout()); + top.add(menubar,BorderLayout.CENTER); + top.add(summary,BorderLayout.EAST); + JPanel main = new JPanel(new BorderLayout()); + main.add(map,BorderLayout.CENTER); + JPanel side = new JPanel(new BorderLayout()); + currentTool = new JLabel("Current tool: Object inspector"); + inspector = new ObjectInspector(true); + selector = new ObjectSelector(map); + side.add(currentTool,BorderLayout.NORTH); + side.add(new JScrollPane(inspector),BorderLayout.CENTER); + main.add(side,BorderLayout.EAST); + main.add(top,BorderLayout.NORTH); + frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) { + System.exit(0); + }}); + frame.getContentPane().add(main); + frame.pack(); + changeTool(Tool.INSPECTOR); + frame.setVisible(true); + } + + private int generateID() { + int result; + do { + result = nextID++; + } while (memory.lookup(result)!=null); + return result; + } + + private void changeTool(Tool t) { + if (tool==t) return; + tool = t; + currentTool.setText("Current tool: "+t.getName()); + if (tool==Tool.INSPECTOR) selector.addObjectSelectionListener(inspector); + else selector.removeObjectSelectionListener(inspector); + } + + private void randomise() { + RandomiseDialog r = new RandomiseDialog(null); + r.pack(); + r.setVisible(true); + if (r.wasOK()) { + FireStation[] fireStations = new FireStation[r.getFireStations()]; + PoliceOffice[] policeOffices = new PoliceOffice[r.getPoliceOffices()]; + AmbulanceCenter[] ambulanceCentres = new AmbulanceCenter[r.getAmbulanceCentres()]; + Refuge[] refuges = new Refuge[r.getRefuges()]; + FireBrigade[] fireBrigades = new FireBrigade[r.getFireBrigades()]; + PoliceForce[] policeForces = new PoliceForce[r.getPoliceForces()]; + AmbulanceTeam[] ambulanceTeams = new AmbulanceTeam[r.getAmbulanceTeams()]; + Civilian[] civs = new Civilian[r.getCivs()]; + + Building[] ordinary = RandomConfig.placeMotionlessObjects(fireStations,policeOffices,ambulanceCentres,refuges,allBuildings); + // RandomConfig.placeMovingObjects(fireBrigades,policeForces,ambulanceTeams,civs,allBuildings,allRoads,allNodes,false,true); + RandomConfig.placeMovingObjects(fireBrigades,policeForces,ambulanceTeams,civs,allBuildings,new Road[0],allNodes,false,true); + Building[] fires = RandomConfig.placeNormalFires(r.getFires(),ordinary); + + // Remove all humanoids, convert all buildings back to normal and remove all fieryness + Collection<RescueObject> all = memory.getAllObjects(); + for (RescueObject next : all) { + if (next instanceof Building) { + Building b = (Building)next; + b.setFieryness(0,0,this); + convertBuilding(b,RescueConstants.TYPE_BUILDING); + } + if (next instanceof Humanoid) { + memory.remove(next); + } + } + + // Add all these objects to the memory + for (int i=0;i<fireStations.length;++i) memory.add(fireStations[i],0); + for (int i=0;i<policeOffices.length;++i) memory.add(policeOffices[i],0); + for (int i=0;i<ambulanceCentres.length;++i) memory.add(ambulanceCentres[i],0); + for (int i=0;i<refuges.length;++i) memory.add(refuges[i],0); + // We need to generate IDs for new humanoids + for (int i=0;i<fireBrigades.length;++i) { + fireBrigades[i].setID(generateID()); + memory.add(fireBrigades[i],0); + } + for (int i=0;i<policeForces.length;++i) { + policeForces[i].setID(generateID()); + memory.add(policeForces[i],0); + } + for (int i=0;i<ambulanceTeams.length;++i) { + ambulanceTeams[i].setID(generateID()); + memory.add(ambulanceTeams[i],0); + } + for (int i=0;i<civs.length;++i) { + civs[i].setID(generateID()); + memory.add(civs[i],0); + } + // And add the fires + for (int i=0;i<fires.length;++i) fires[i].setFieryness(1,0,null); + + map.setMemory(memory); + map.repaint(); + summary.update(); + } + } + + private void handleClick(MouseEvent e) { + if (dragPoint==null) { + } + } + + private void handlePress(MouseEvent e) { + pressPoint = e.getPoint(); + } + + private void handleRelease(MouseEvent e) { + if (dragPoint==null) { + Object[] all = map.getObjectsAtPoint(pressPoint); + update(all,e); + } + else { + int x1 = Math.min(pressPoint.x,dragPoint.x); + int y1 = Math.min(pressPoint.y,dragPoint.y); + int x2 = Math.max(pressPoint.x,dragPoint.x); + int y2 = Math.max(pressPoint.y,dragPoint.y); + Rectangle2D box = new Rectangle2D.Double(x1,y1,x2-x1,y2-y1); + Object[] objects = map.getObjectsInArea(box); + update(objects,e); + pressPoint = null; + dragPoint = null; + overlay.removeAllObjects(); + map.repaint(); + } + } + + private void handleDrag(MouseEvent e) { + if (pressPoint!=null) { + dragPoint = e.getPoint(); + int dx = pressPoint.x - dragPoint.x; + int dy = pressPoint.y - dragPoint.y; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + if (dx > 5 || dy > 5) { + // Draw a rectangle on the view + Rectangle r = new Rectangle(Math.min(pressPoint.x,dragPoint.x),Math.min(pressPoint.y,dragPoint.y),dx,dy); + overlay.setObject(r); + map.repaint(); + } + } + } + + private void update(Object[] os, MouseEvent e) { + boolean change = false; + switch (e.getButton()) { + case MouseEvent.BUTTON1: + change = leftClick(os); + break; + } + if (change) { + map.setMemory(memory); + map.repaint(); + summary.update(); + } + } + + private boolean leftClick(Object[] os) { + switch (tool) { + case PLACE_ROADS: + return increaseBlock(os); + case CLEAR_ROADS: + return decreaseBlock(os); + case PLACE_FIRES: + return placeFires(os); + case CLEAR_FIRES: + return clearFires(os); + case PLACE_REFUGES: + return placeRefuges(os); + case CLEAR_REFUGES: + return clearRefuges(os); + case PLACE_FIRE_BRIGADES: + return addAgent(os,RescueConstants.TYPE_FIRE_BRIGADE); + case CLEAR_FIRE_BRIGADES: + return removeAgent(os,RescueConstants.TYPE_FIRE_BRIGADE); + case PLACE_POLICE_FORCES: + return addAgent(os,RescueConstants.TYPE_POLICE_FORCE); + case CLEAR_POLICE_FORCES: + return removeAgent(os,RescueConstants.TYPE_POLICE_FORCE); + case PLACE_AMBULANCE_TEAMS: + return addAgent(os,RescueConstants.TYPE_AMBULANCE_TEAM); + case CLEAR_AMBULANCE_TEAMS: + return removeAgent(os,RescueConstants.TYPE_AMBULANCE_TEAM); + case PLACE_CIVILIANS: + return addAgent(os,RescueConstants.TYPE_CIVILIAN); + case CLEAR_CIVILIANS: + return removeAgent(os,RescueConstants.TYPE_CIVILIAN); + case PLACE_FIRE_STATIONS: + return addCentre(os,RescueConstants.TYPE_FIRE_STATION); + case CLEAR_FIRE_STATIONS: + return removeCentre(os,RescueConstants.TYPE_FIRE_STATION); + case PLACE_POLICE_OFFICES: + return addCentre(os,RescueConstants.TYPE_POLICE_OFFICE); + case CLEAR_POLICE_OFFICES: + return removeCentre(os,RescueConstants.TYPE_POLICE_OFFICE); + case PLACE_AMBULANCE_CENTRES: + return addCentre(os,RescueConstants.TYPE_AMBULANCE_CENTER); + case CLEAR_AMBULANCE_CENTRES: + return removeCentre(os,RescueConstants.TYPE_AMBULANCE_CENTER); + case INCREASE_IMPORTANCE: + return increaseImportance(os); + case DECREASE_IMPORTANCE: + return decreaseImportance(os); + } + return false; + } + + /* + private boolean rightClick(Object[] os) { + switch (tool) { + case ROADS: + return decreaseBlock(os); + case IMPORTANCE: + return decreaseImportance(os); + } + return false; + } + */ + + private boolean increaseBlock(Object[] roads) { + boolean changed = false; + for (int i=0;i<roads.length;++i) { + if (roads[i] instanceof Road) { + changed = increaseBlock((Road)roads[i]) | changed; + } + } + return changed; + } + + private boolean decreaseBlock(Object[] roads) { + boolean changed = false; + for (int i=0;i<roads.length;++i) { + if (roads[i] instanceof Road) { + changed = decreaseBlock((Road)roads[i]) | changed; + } + } + return changed; + } + + private boolean increaseBlock(Road road) { + int lanes = road.getLinesToHead(); + int blocked = lanes - freeLanes(road); + if (blocked==lanes) return false; + setBlockedLanes(road,blocked+1); + return true; + } + + private boolean decreaseBlock(Road road) { + int lanes = road.getLinesToHead(); + int blocked = lanes - freeLanes(road); + if (blocked==0) return false; + setBlockedLanes(road,blocked-1); + return true; + } + + private void setBlockedLanes(Road road, int blocked) { + int width = road.getWidth(); + int laneWidth = width/(road.getLinesToHead()+road.getLinesToTail()); + int blockNeeded = blocked * laneWidth * 2; + if (blockNeeded > width) { + System.out.println("Trying to set block to "+blockNeeded+" but width is only "+width); + blockNeeded = width; + } + road.setBlock(blockNeeded,0,this); + // Check that we have blocked the right number of lanes + int newBlock = road.getLinesToHead()-freeLanes(road); + if (newBlock != blocked) System.out.println("We have "+newBlock+" blocked lanes instead of "+blocked+"!"); + } + + private boolean placeFires(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) changed = placeFire((Building)os[i]) | changed; + } + return changed; + } + + private boolean clearFires(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) changed = clearFire((Building)os[i]) | changed; + } + return changed; + } + + private boolean placeFire(Building b) { + if (b.getFieryness()==0) { + b.setFieryness(1,0,this); + return true; + } + return false; + } + + private boolean clearFire(Building b) { + if (b.getFieryness()!=0) { + b.setFieryness(0,0,this); + return true; + } + return false; + } + + private boolean placeRefuges(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) changed = setRefuge((Building)os[i]) | changed; + } + return changed; + } + + private boolean clearRefuges(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) changed = clearRefuge((Building)os[i]) | changed; + } + return changed; + } + + private boolean increaseImportance(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) { + Building b = (Building)os[i]; + b.setImportance(b.getImportance()+1,0,null); + changed = true; + } + } + return changed; + } + + private boolean decreaseImportance(Object[] os) { + boolean changed = false; + for (int i=0;i<os.length;++i) { + if (os[i] instanceof Building) { + Building b = (Building)os[i]; + if (b.getImportance()>1) { + b.setImportance(b.getImportance()-1,0,null); + changed = true; + } + } + } + return changed; + } + + private boolean setRefuge(Building b) { + if (b.isRefuge()) return false; + convertBuilding(b,RescueConstants.TYPE_REFUGE); + return true; + } + + private boolean clearRefuge(Building b) { + if (!b.isRefuge()) return false; + convertBuilding(b,RescueConstants.TYPE_BUILDING); + return true; + } + + private boolean addAgent(Object[] os, int type) { + // Find the first object that we can place an agent on + int position = 0; + Road road = null; + for (Object next : os) { + if (next instanceof Building || next instanceof Road || next instanceof Node) { + position = ((RescueObject)next).getID(); + if (next instanceof Road) road = (Road)next; + } + } + if (position==0) return false; + // Generate the agent + Humanoid result; + switch (type) { + case RescueConstants.TYPE_FIRE_BRIGADE: + result = new FireBrigade(); + break; + case RescueConstants.TYPE_POLICE_FORCE: + result = new PoliceForce(); + break; + case RescueConstants.TYPE_AMBULANCE_TEAM: + result = new AmbulanceTeam(); + break; + case RescueConstants.TYPE_CIVILIAN: + result = new Civilian(); + break; + default: + return false; + } + result.setID(generateID()); + result.setPosition(position,0,this); + if (road!=null) result.setPositionExtra(road.getLength()/2,0,this); + memory.add(result,0,this); + return true; + } + + private boolean removeAgent(Object[] os, int type) { + // Remove the first object that is the right type + for (Object next : os) { + if (next instanceof RescueObject) { + RescueObject r = (RescueObject)next; + if (r.getType()==type) { + memory.remove(r); + return true; + } + } + } + return false; + } + + private boolean addCentre(Object[] os, int type) { + // Turn the first ordinary building into the right type + for (Object next : os) { + if (next instanceof Building) { + Building b = (Building)next; + if (b.isOrdinaryBuilding()) { + convertBuilding(b,type); + return true; + } + } + } + return false; + } + + private boolean removeCentre(Object[] os, int type) { + for (Object next : os) { + if (next instanceof Building) { + Building b = (Building)next; + if (b.getType()==type) { + convertBuilding(b,RescueConstants.TYPE_BUILDING); + return true; + } + } + } + return false; + } + + private void convertBuilding(Building b, int type) { + if (b.getType()==type) return; + Building replace; + switch (type) { + case RescueConstants.TYPE_BUILDING: + replace = new Building(b); + break; + case RescueConstants.TYPE_REFUGE: + replace = new Refuge(b); + break; + case RescueConstants.TYPE_FIRE_STATION: + replace = new FireStation(b); + break; + case RescueConstants.TYPE_POLICE_OFFICE: + replace = new PoliceOffice(b); + break; + case RescueConstants.TYPE_AMBULANCE_CENTER: + replace = new AmbulanceCenter(b); + break; + default: + System.err.println("Can't convert to type "+type); + return; + } + replace.setID(b.getID()); + memory.remove(b); + memory.add(replace,0,this); + } + + private void save(boolean oldGIS) { + try { + // Write out the blocked roads + PrintWriter out = new PrintWriter(new FileWriter(new File("blockades.lst"))); + for (int i=0;i<allRoads.length;++i) { + out.println(allRoads[i].getBlock()); + } + out.flush(); + out.close(); + out = new PrintWriter(new FileWriter(new File("gisini.txt"))); + // Write out all civilians, agents, refuges and fires + if (oldGIS) { + writeOldGIS(out,getFireStations(),getPoliceOffices(),getAmbulanceCenters(),getFireBrigades(),getPoliceForces(),getAmbulanceTeams(),getCivilians(),getRefuges(),getFires()); + } + else { + MapFiles.writeGISMotionlessObjects(out,getFireStations(),getPoliceOffices(),getAmbulanceCenters(),getRefuges()); + MapFiles.writeGISMovingObjects(out,getFireBrigades(),getPoliceForces(),getAmbulanceTeams(),getCivilians(),memory); + MapFiles.writeGISFires(out,getFires()); + MapFiles.writeGISImportantBuildings(out,getImportantBuildings()); + } + out.flush(); + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private void writeOldGIS(PrintWriter out, FireStation[] fs, PoliceOffice[] po, AmbulanceCenter[] ac, FireBrigade[] f, PoliceForce[] p, AmbulanceTeam[] a, Civilian[] c, Refuge[] r, Building[] fires) { + out.println("[MotionLessObject]"); + out.println("FireStationNum="+fs.length); + out.println("PoliceOfficeNum="+po.length); + out.println("AmbulanceCenterNum="+ac.length); + out.println("RefugeNum="+r.length); + for (int i=0;i<fs.length;++i) out.println("FireStation"+i+"=0,0,"+fs[i].getID()+",0"); + for (int i=0;i<po.length;++i) out.println("PoliceOffice"+i+"=0,0,"+po[i].getID()+",0"); + for (int i=0;i<ac.length;++i) out.println("AmbulanceCenter"+i+"=0,0,"+ac[i].getID()+",0"); + for (int i=0;i<r.length;++i) out.println("Refuge"+i+"=0,0,"+r[i].getID()+",0"); + out.println("[MovingObject]"); + out.println("FireBrigadeNum="+f.length); + out.println("PoliceForceNum="+p.length); + out.println("AmbulanceTeamNum="+a.length); + out.println("CivilianNum="+c.length); + for (int i=0;i<f.length;++i) out.println("FireBrigade"+i+"=0,0,"+f[i].getPosition()+",0,0,"+f[i].getPositionExtra()+",0"); + for (int i=0;i<p.length;++i) out.println("PoliceForce"+i+"=0,0,"+p[i].getPosition()+",0,0,"+p[i].getPositionExtra()+",0"); + for (int i=0;i<a.length;++i) out.println("AmbulanceTeam"+i+"=0,0,"+a[i].getPosition()+",0,0,"+a[i].getPositionExtra()+",0"); + for (int i=0;i<c.length;++i) out.println("Civilian"+i+"=0,0,"+c[i].getPosition()+",0,0,"+c[i].getPositionExtra()+",0"); + out.println("[Fires]"); + out.println("FirePointNum="+fires.length); + for (int i=0;i<fires.length;++i) out.println("FirePoint"+i+"=0,0,"+fires[i].getID()+",0"); + } + + private FireStation[] getFireStations() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_FIRE_STATION); + FireStation[] result = new FireStation[objects.size()]; + objects.toArray(result); + return result; + } + + private PoliceOffice[] getPoliceOffices() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_POLICE_OFFICE); + PoliceOffice[] result = new PoliceOffice[objects.size()]; + objects.toArray(result); + return result; + } + + private AmbulanceCenter[] getAmbulanceCenters() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_AMBULANCE_CENTER); + AmbulanceCenter[] result = new AmbulanceCenter[objects.size()]; + objects.toArray(result); + return result; + } + + private Refuge[] getRefuges() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_REFUGE); + Refuge[] result = new Refuge[objects.size()]; + objects.toArray(result); + return result; + } + + private FireBrigade[] getFireBrigades() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_FIRE_BRIGADE); + FireBrigade[] result = new FireBrigade[objects.size()]; + objects.toArray(result); + return result; + } + + private PoliceForce[] getPoliceForces() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_POLICE_FORCE); + PoliceForce[] result = new PoliceForce[objects.size()]; + objects.toArray(result); + return result; + } + + private AmbulanceTeam[] getAmbulanceTeams() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_AMBULANCE_TEAM); + AmbulanceTeam[] result = new AmbulanceTeam[objects.size()]; + objects.toArray(result); + return result; + } + + private Civilian[] getCivilians() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_CIVILIAN); + Civilian[] result = new Civilian[objects.size()]; + objects.toArray(result); + return result; + } + + private Building[] getFires() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_BUILDING,RescueConstants.TYPE_REFUGE,RescueConstants.TYPE_FIRE_STATION,RescueConstants.TYPE_AMBULANCE_CENTER,RescueConstants.TYPE_POLICE_OFFICE); + Set<Building> fires = new HashSet<Building>(); + for (RescueObject next : objects) { + Building b = (Building)next; + if (b.getFieryness()>0) { + fires.add(b); + } + } + return (Building[])fires.toArray(new Building[0]); + } + + private Building[] getImportantBuildings() { + Collection<RescueObject> objects = memory.getObjectsOfType(RescueConstants.TYPE_BUILDING,RescueConstants.TYPE_REFUGE,RescueConstants.TYPE_FIRE_STATION,RescueConstants.TYPE_AMBULANCE_CENTER,RescueConstants.TYPE_POLICE_OFFICE); + Set<Building> important = new HashSet<Building>(); + for (RescueObject next : objects) { + Building b = (Building)next; + if (b.getImportance()>1) { + important.add(b); + } + } + return (Building[])important.toArray(new Building[0]); + } + + private static int freeLanes(Road road) { + double blockWidth = road.getBlock()/2d; + double lineWidth = ((double)road.getWidth())/((double)road.getLinesToHead()+road.getLinesToTail()); + double linesBlockedRate = blockWidth / lineWidth; + return road.getLinesToHead()-(int) Math.floor(linesBlockedRate + 0.5d); + } + + public static void main(String[] args) { + boolean oldGIS = false; + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase("--oldgis")) oldGIS = true; + else { + printUsage(); + return; + } + } + try { + Road[] r = MapFiles.loadRoads("road.bin"); + Node[] n = MapFiles.loadNodes("node.bin"); + Building[] b = MapFiles.loadBuildings("building.bin"); + new ScenarioMaker(r,n,b,oldGIS); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + private static void printUsage() { + System.out.println("Usage: ScenarioMaker [--oldgis]"); + } + + private class Summary extends JLabel { + public Summary() { + super("FB: 0, FS: 0, PF: 0, PO: 0, AT: 0, AC: 0, Civ: 0"); + } + + public void update() { + int fb = memory.getObjectsOfType(RescueConstants.TYPE_FIRE_BRIGADE).size(); + int fs = memory.getObjectsOfType(RescueConstants.TYPE_FIRE_STATION).size(); + int pf = memory.getObjectsOfType(RescueConstants.TYPE_POLICE_FORCE).size(); + int po = memory.getObjectsOfType(RescueConstants.TYPE_POLICE_OFFICE).size(); + int at = memory.getObjectsOfType(RescueConstants.TYPE_AMBULANCE_TEAM).size(); + int ac = memory.getObjectsOfType(RescueConstants.TYPE_AMBULANCE_CENTER).size(); + int civ = memory.getObjectsOfType(RescueConstants.TYPE_CIVILIAN).size(); + setText("FB: "+fb+", FS: "+fs+", PF: "+pf+", PO: "+po+", AT: "+at+", AC: "+ac+", Civ: "+civ); + } + } + + private static class BigRoadRenderer implements MapRenderer { + public boolean canRender(Object o) { + return o instanceof Road; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Road road = (Road)o; + Node roadHead = (Node)memory.lookup(road.getHead()); + Node roadTail = (Node)memory.lookup(road.getTail()); + int headX = transform.toScreenX(roadHead.getX()); + int headY = transform.toScreenY(roadHead.getY()); + int tailX = transform.toScreenX(roadTail.getX()); + int tailY = transform.toScreenY(roadTail.getY()); + Shape shape = new java.awt.geom.Line2D.Double(headX,headY,tailX,tailY); + shape = new BasicStroke(10).createStrokedShape(shape); + Color c = Color.BLACK; + int lanes = road.getLinesToHead(); + int free = freeLanes(road); + c = Color.ORANGE; + if (free==0) c = Color.BLACK; + if (free==lanes) c = Color.WHITE; + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,c); + ((Graphics2D)g).draw(shape); + return shape; + } + } + + private static class ImportantBuildingRenderer implements MapRenderer { + private BuildingRenderer downstream; + + public ImportantBuildingRenderer(BuildingRenderer downstream) { + this.downstream = downstream; + } + + public boolean canRender(Object o) { + return o instanceof Building && downstream.canRender(o); + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Shape shape = downstream.render(o,memory,g,transform); + int importance = ((Building)o).getImportance(); + if (importance > 1) { + // Draw the importance on top of the shape + g.setColor(Color.black); + Rectangle bounds = shape.getBounds(); + FontMetrics metrics = g.getFontMetrics(); + String s = ""+importance; + int width = metrics.stringWidth(s); + int centerX = bounds.x + (bounds.width/2); + g.drawString(s,centerX-(width/2),bounds.y+(bounds.height/2)+metrics.getDescent()); + } + return shape; + } + } + + private static class HumanoidCountRenderer implements MapRenderer { + private MapRenderer downstream; + + public HumanoidCountRenderer(MapRenderer downstream) { + this.downstream = downstream; + } + + public boolean canRender(Object o) { + return o instanceof Humanoid && downstream.canRender(o); + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Shape shape = downstream.render(o,memory,g,transform); + int position = ((Humanoid)o).getPosition(); + // Find all humanoids at this position + int count = 0; + for (RescueObject next : memory.getObjectsOfType(RescueConstants.TYPE_CIVILIAN,RescueConstants.TYPE_AMBULANCE_TEAM,RescueConstants.TYPE_FIRE_BRIGADE,RescueConstants.TYPE_POLICE_FORCE)) { + Humanoid h = (Humanoid)next; + if (h.getPosition()==position) ++count; + } + if (count > 1) { + // Draw the count on top of the shape + g.setColor(Color.black); + Rectangle bounds = shape.getBounds(); + FontMetrics metrics = g.getFontMetrics(); + String s = ""+count; + int width = metrics.stringWidth(s); + int centerX = bounds.x + (bounds.width/2); + g.drawString(s,centerX-(width/2),bounds.y+(bounds.height/2)+metrics.getDescent()); + } + return shape; + } + } + + private class RoadSorter implements Comparator { + private Memory m; + + public RoadSorter(Memory m) { + this.m = m; + } + + public int compare(Object o1, Object o2) { + Road r1 = (Road)o1; + Road r2 = (Road)o2; + Node h1 = (Node)m.lookup(r1.getHead()); + Node t1 = (Node)m.lookup(r1.getTail()); + Node h2 = (Node)m.lookup(r2.getHead()); + Node t2 = (Node)m.lookup(r2.getTail()); + int x1 = (h1.getX()+t1.getX())/2; + int y1 = (h1.getY()+t1.getY())/2; + int x2 = (h2.getX()+t2.getX())/2; + int y2 = (h2.getY()+t2.getY())/2; + if (x1 < x2) return -1; + if (x1 > x2) return 1; + if (y1 < y2) return -1; + if (y2 > y1) return 1; + return 0; + } + } + + private class RandomiseDialog extends JDialog { + private JSpinner civsField; + private JSpinner fireField; + private JSpinner policeField; + private JSpinner ambulanceField; + private JSpinner fireStationField; + private JSpinner policeOfficeField; + private JSpinner ambulanceCenterField; + private JSpinner refugeField; + private JSpinner firesField; + private JButton ok; + private JButton cancel; + private boolean wasOK; + + public RandomiseDialog(Frame owner) { + super(owner,"Randomise config",true); + civsField = new JSpinner(new SpinnerNumberModel(70,50,80,1)); + fireField = new JSpinner(new SpinnerNumberModel(10,5,15,1)); + policeField = new JSpinner(new SpinnerNumberModel(10,5,15,1)); + ambulanceField = new JSpinner(new SpinnerNumberModel(8,5,10,1)); + fireStationField = new JSpinner(new SpinnerNumberModel(1,1,1,1)); + policeOfficeField = new JSpinner(new SpinnerNumberModel(1,1,1,1)); + ambulanceCenterField = new JSpinner(new SpinnerNumberModel(1,1,11,1)); + refugeField = new JSpinner(new SpinnerNumberModel(4,1,5,1)); + firesField = new JSpinner(new SpinnerNumberModel(4,2,8,1)); + ok = new JButton("OK"); + cancel = new JButton("Cancel"); + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + JPanel main = new JPanel(layout); + c.gridx = 0; + c.gridy = 0; + c.weightx = 1; + c.weighty = 1; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + JLabel l = new JLabel("Fire brigades"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Police forces"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Ambulance teams"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Civilians"); + layout.setConstraints(l,c); + main.add(l); + c.gridy = 0; + c.gridx = 2; + l = new JLabel("Fire stations"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Police offices"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Ambulance centres"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Refuges"); + layout.setConstraints(l,c); + main.add(l); + ++c.gridy; + l = new JLabel("Fires"); + layout.setConstraints(l,c); + main.add(l); + c.gridy = 0; + c.gridx = 1; + layout.setConstraints(fireField,c); + main.add(fireField); + ++c.gridy; + layout.setConstraints(policeField,c); + main.add(policeField); + ++c.gridy; + layout.setConstraints(ambulanceField,c); + main.add(ambulanceField); + ++c.gridy; + layout.setConstraints(civsField,c); + main.add(civsField); + c.gridy = 0; + c.gridx = 3; + layout.setConstraints(fireStationField,c); + main.add(fireStationField); + ++c.gridy; + layout.setConstraints(policeOfficeField,c); + main.add(policeOfficeField); + ++c.gridy; + layout.setConstraints(ambulanceCenterField,c); + main.add(ambulanceCenterField); + ++c.gridy; + layout.setConstraints(refugeField,c); + main.add(refugeField); + ++c.gridy; + layout.setConstraints(firesField,c); + main.add(firesField); + ++c.gridy; + c.gridx = 0; + c.gridwidth = 2; + c.anchor = GridBagConstraints.CENTER; + layout.setConstraints(ok,c); + main.add(ok); + c.gridx = 2; + layout.setConstraints(cancel,c); + main.add(cancel); + setContentPane(main); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + wasOK = true; + setVisible(false); + }}); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + wasOK = false; + setVisible(false); + }}); + } + + public boolean wasOK() { + return wasOK; + } + + public int getFireBrigades() { + return ((Number)fireField.getValue()).intValue(); + } + + public int getFireStations() { + return ((Number)fireStationField.getValue()).intValue(); + } + + public int getPoliceForces() { + return ((Number)policeField.getValue()).intValue(); + } + + public int getPoliceOffices() { + return ((Number)policeOfficeField.getValue()).intValue(); + } + + public int getAmbulanceTeams() { + return ((Number)ambulanceField.getValue()).intValue(); + } + + public int getAmbulanceCentres() { + return ((Number)ambulanceCenterField.getValue()).intValue(); + } + + public int getCivs() { + return ((Number)civsField.getValue()).intValue(); + } + + public int getRefuges() { + return ((Number)refugeField.getValue()).intValue(); + } + + public int getFires() { + return ((Number)firesField.getValue()).intValue(); + } + } + + private class ToolAction extends AbstractAction { + private Tool tool; + + public ToolAction(Tool t) { + this(t,t.getName()); + } + + public ToolAction(Tool t, String name) { + super(name); + tool = t; + } + + public void actionPerformed(ActionEvent e) { + changeTool(tool); + } + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/ConfigFile.java b/modules/oldsims/rescuecore/tools/simulationrunner/ConfigFile.java new file mode 100644 index 0000000000000000000000000000000000000000..0dbda7903b02ea4b6774de3e7214bae64da31c68 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/ConfigFile.java @@ -0,0 +1,89 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.util.*; +import java.io.*; + +public class ConfigFile { + private Map variables; + private List processes; + + public ConfigFile(String fileName, String logPrefix) throws IOException { + variables = new HashMap(); + processes = new ArrayList(); + BufferedReader in = new BufferedReader(new FileReader(new File(fileName))); + String next = null; + do { + next = in.readLine(); + if (next!=null) { + next = next.trim(); + if (next.startsWith("#") || next.equals("")) continue; + next = substituteVariables(next); + if (next.indexOf("=")==-1) { + addProcess(next,logPrefix); + } + else addVariable(next); + } + } while (next!=null); + } + + public List getProcesses() { + return processes; + } + + private void addProcess(String line, String logPrefix) { + int index1 = line.indexOf("#"); + int index2 = line.indexOf("#",index1+1); + String name = line.substring(0,index1); + String command = line.substring(index1+1,index2); + String rexp = line.substring(index2+1); + RescueProcess p = new RescueProcess(name,command,rexp,logPrefix); + // System.out.println("Adding process: "+p); + processes.add(p); + } + + private void addVariable(String line) { + int index = line.indexOf("="); + String name = line.substring(0,index); + String value = line.substring(index+1); + variables.put(name,value); + // System.out.println("Adding variable: "+name+"="+value); + } + + private String substituteVariables(String original) { + // System.out.println("Substituting variables: "+original); + int start = 0; + int end = 0; + StringBuffer result = new StringBuffer(original); + do { + String current = result.toString(); + start = current.indexOf("${"); + end = current.indexOf("}",start); + // Make the substitution + if (start > 0 && end > 0) { + String name = current.substring(start+2,end); + String value = (String)variables.get(name); + if (value==null) value = System.getProperty(name); + if (value==null) value = ""; + //System.out.println("Substituting ${"+name+"} with "+value); + result.replace(start,end+1,value); + } + } while (start > 0 && end > 0); + // System.out.println("Substituted variables: "+result.toString()); + return result.toString(); + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/LogReader.java b/modules/oldsims/rescuecore/tools/simulationrunner/LogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..963dcfb55115b061197e2f885c2fea9c2bddae27 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/LogReader.java @@ -0,0 +1,155 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.io.*; +import java.util.*; + +public class LogReader implements Runnable { + private boolean running; + private boolean alive; + private InputStream in; + private java.util.List out; + private final Object waitLock = new Object(); + private String waitingFor; + private StringBuffer waitBuffer; + + /** + Generate a new LogReader that reads from the given InputStream and writes to the given OutputStream + @param in The InputStream to read + @param log An OutputStream to write to + */ + public LogReader(InputStream in, OutputStream log) { + this.in = in; + out = new ArrayList(); + if (log!=null) + out.add(log); + } + + /** + Generate a new LogReader that reads from the given InputStream + @param in The InputStream to read + */ + public LogReader(InputStream in) { + this.in = in; + out = new ArrayList(); + } + + /** + Add a new destination for the data + @param stream The new destination for data. + */ + public void addOutputStream(OutputStream stream) { + out.add(stream); + } + + /** + Shut down this LogReader + */ + public synchronized void kill() { + running = false; + while (alive) { + try {wait(1000);} catch (InterruptedException e) {break;} + } + } + + /** + Start this LogReader + */ + public void start() { + start(null,null); + } + + /** + Start this LogReader and wait for a particular String to appear in the output. This method will block until that message appears + @param waitFor The message to wait for + @param process The RescueProcess that controls this LogReader + */ + public void start(String waitFor, RescueProcess process) { + synchronized(waitLock) { + new Thread(this).start(); + waitingFor = waitFor; + if (waitFor==null || waitFor.equals("")) return; + waitBuffer = new StringBuffer(); + while (waitingFor!=null) { + if (!process.isRunning()) break; + try {waitLock.wait(1000);} catch (InterruptedException e) {break;} + } + } + } + + public void run() { + running = true; + alive = true; + byte[] data = new byte[1024]; + try { + while (running) { + // Be nice to other processes + Thread.yield(); + // Read some data + // But only read available data to avoid blocking + int available = Math.min(data.length,in.available()); + if (available==0) { + try {Thread.sleep(1000);} catch (InterruptedException e) {} + continue; + } + // System.out.println(available+" bytes available"); + int count = in.read(data,0,available); + // And write it out + if (count > 0) { + // System.out.println("Read: "+new String(data)); + checkWait(data); + for (Iterator it = out.iterator();it.hasNext();) { + OutputStream next = (OutputStream)it.next(); + next.write(data,0,count); + next.flush(); + } + } + } + } + catch (IOException e) { + // e.printStackTrace(); + } + for (Iterator it = out.iterator();it.hasNext();) { + OutputStream next = (OutputStream)it.next(); + try { + next.flush(); + next.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + synchronized(this) { + alive = false; + notifyAll(); + } + } + + private void checkWait(byte[] data) { + synchronized(waitLock) { + if (waitingFor==null || waitBuffer==null) return; + waitBuffer.append(new String(data)); + String s = waitBuffer.toString(); + // System.out.println("Checking "+s+" for "+waitingFor); + if (s.indexOf(waitingFor)!=-1) { + waitingFor = null; + waitBuffer = null; + waitLock.notifyAll(); + } + } + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/ProcessHandler.java b/modules/oldsims/rescuecore/tools/simulationrunner/ProcessHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..3f944fe4d5bb827df7c84f9259c557de644759fe --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/ProcessHandler.java @@ -0,0 +1,108 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +public class ProcessHandler { + private RescueProcess[] processes; + private ProcessViewer viewer; + private volatile boolean running; + private volatile Thread startThread; + + /** + Generate a new ProcessHandler + @param processes The processes to start + @param viewer The viewer used to track the processes + */ + public ProcessHandler(RescueProcess[] processes, ProcessViewer viewer) { + this.processes = processes; + this.viewer = viewer; + } + + /** + Start all the processes and wait for them to finish + */ + public void runAll() { + // System.out.println("Starting processes"); + synchronized(this) { + running = true; + startThread = Thread.currentThread(); + } + for (int i=0;i<processes.length;++i) { + RescueProcess next = processes[i]; + // System.out.print("Starting "+next+"..."); + if (viewer!=null) viewer.processStarting(i); + if (!running || !next.start()) { + // System.out.println("failed"); + break; + } + if (viewer!=null) viewer.processStarted(i); + // System.out.println("success"); + } + // Wait for one of them to finish + boolean finished = false; + synchronized(this) { + startThread = null; + while (running && !finished) { + // System.out.println("Waiting for a process to finish..."); + for (int i=0;i<processes.length;++i) { + RescueProcess next = processes[i]; + if (!next.isRunning()) { + // System.out.println(next+" has finished"); + if (viewer!=null) viewer.processStopped(i); + finished = true; + break; + } + } + try {wait(5000);} catch (InterruptedException e) {} + } + } + stopAllProcesses(); + } + + /** + Stop all the processes + */ + public synchronized void stopAll() { + running = false; + if (startThread!=null) startThread.interrupt(); + notifyAll(); + while (somethingRunning()) try {wait(1000);} catch (InterruptedException e) {break;} + } + + /** + Stop all the processes + */ + private synchronized void stopAllProcesses() { + // System.out.println("Stopping all processes"); + for (int i=0;i<processes.length;++i) { + RescueProcess next = processes[i]; + if (next.isRunning()) { + System.out.print("Stopping process "+(i+1)+" of "+processes.length+"..."); + next.stop(); + if (viewer!=null) viewer.processStopped(i); + System.out.println("stopped"); + } + } + } + + private synchronized boolean somethingRunning() { + for (int i=0;i<processes.length;++i) { + RescueProcess next = processes[i]; + if (next.isRunning()) return true; + } + return false; + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/ProcessViewer.java b/modules/oldsims/rescuecore/tools/simulationrunner/ProcessViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..8d90da151214533b9439112a5ef20f38ef783bc3 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/ProcessViewer.java @@ -0,0 +1,135 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.awt.*; +import javax.swing.*; +import javax.swing.table.*; + +public class ProcessViewer extends JPanel { + private JTabbedPane tabs; + private JTable summary; + private ProcessTableModel model; + + private final static int PROCESS_COLUMN_NAME = 0; + private final static int PROCESS_COLUMN_COMMAND = 1; + private final static int PROCESS_COLUMN_STATUS = 2; + private final static int PROCESS_COLUMN_COUNT = 3; + private final static String[] PROCESS_COLUMN_NAMES = {"Name","Command line","Status"}; + + /** + Generate a ProcessViewer that will view the given processes + */ + public ProcessViewer(RescueProcess[] processes) { + super(new BorderLayout()); + model = new ProcessTableModel(processes); + summary = new JTable(model); + tabs = new JTabbedPane(); + add(new JScrollPane(summary),BorderLayout.CENTER); + add(tabs,BorderLayout.EAST); + // for (int i=0;i<processes.length;++i) { + // RescueProcess next = processes[i]; + /* + Class clazz = filters.get(next.name); + if (clazz==null) clazz = DefaultProcessFilter.class; + try { + ProcessFilter filter = (ProcessFilter)clazz.newInstance(); + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in = new PipedInputStream(out); + filter.setInputStream(in); + + } + catch (Exception e) { + e.printStackTrace(); + } + */ + // } + } + + /** + Notification that a process is starting + @param index The index of the process that is starting + */ + public void processStarting(int index) { + model.setStatus(index,"Starting"); + } + + /** + Notification that a process has started + @param index The index of the process that has started + */ + public void processStarted(int index) { + model.setStatus(index,"Running"); + } + + /** + Notification that a process has stopped + @param index The index of the process that has stopped + */ + public void processStopped(int index) { + model.setStatus(index,"Stopped"); + } + + private class ProcessTableModel extends AbstractTableModel { + private String[] names, commands, statuses; + + ProcessTableModel(RescueProcess[] processes) { + names = new String[processes.length]; + commands = new String[processes.length]; + statuses = new String[processes.length]; + for (int i=0;i<processes.length;++i) { + names[i] = processes[i].getName(); + commands[i] = processes[i].getCommandLine(); + statuses[i] = "Not started"; + } + } + + void setStatus(int index, String status) { + statuses[index] = status; + fireTableDataChanged(); + } + + public int getRowCount() { + return names.length; + } + + public int getColumnCount() { + return PROCESS_COLUMN_COUNT; + } + + public Object getValueAt(int row, int col) { + switch (col) { + case PROCESS_COLUMN_NAME: + return names[row]; + case PROCESS_COLUMN_COMMAND: + return commands[row]; + case PROCESS_COLUMN_STATUS: + return statuses[row]; + default: + throw new RuntimeException("Unknown column: "+col); + } + } + + public String getColumnName(int col) { + return PROCESS_COLUMN_NAMES[col]; + } + + public Class getColumnClass(int col) { + return String.class; + } + } +} + diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/RescueProcess.java b/modules/oldsims/rescuecore/tools/simulationrunner/RescueProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..c089a600b904602c4e368b229502f38527bf3051 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/RescueProcess.java @@ -0,0 +1,141 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.io.*; + +public class RescueProcess { + private String name, commandline, done, logPrefix; + private boolean useOut; + private Process process; + private LogReader out, error; + + /** Create a new RescueProcess + @param name The name of this process + @param commandline The command line used to start the process + @param started When this string is encountered in the standard output stream then this process is deemed to have initialised successfully. If this parameter is null, then the process is assumed to initialise instantaneously. + */ + public RescueProcess(String name, String commandline, String started, String logPrefix) { + this.name = name; + this.commandline = commandline; + this.done = started; + this.logPrefix = logPrefix; + useOut = true; + if (done.startsWith("stderr:")) { + done = done.substring(7); + useOut = false; + } + } + + public String toString() { + return name+": "+commandline+" -> "+done; + } + + /** + Get the name of this process + @return The name of this process + */ + public String getName() { + return name; + } + + /** + Get the command line used to start this process + @return The command line used to start this process + */ + public String getCommandLine() { + return commandline; + } + + /** + Get the string that delimits successful initialisation of this process + @return The string that delimits successful initialisation + */ + public String getStartedFlag() { + return done; + } + + /** + Start this process and wait for the started flag to appear in the output. Standard output and standard error are automatically redirected to files called <name>.log and <name>.error, where <name> is the value returned by the @{link #getName()} method. + @return true iff the process starts successfully + */ + public synchronized boolean start() { + try { + System.out.println("Executing "+commandline); + process = Runtime.getRuntime().exec(commandline); + InputStream output = process.getInputStream(); + InputStream errors = process.getErrorStream(); + // Create log files + out = new LogReader(output,logPrefix==null?null:new BufferedOutputStream(new FileOutputStream(new File(logPrefix+name+".log")))); + error = new LogReader(errors,logPrefix==null?null:new BufferedOutputStream(new FileOutputStream(new File(logPrefix+name+".error")))); + } + catch (IOException e) { + System.err.println("Could not start process: "+commandline); + e.printStackTrace(); + if (process!=null) process.destroy(); + return false; + } + // Start sucking the output into log files + if (useOut) { + out.start(done,this); + error.start(); + } + else { + out.start(); + error.start(done,this); + } + return true; + } + + /** + Add a new destination for the standard output stream from this process. Output data will be multiplexed to all destinations. + @param stream An OutputStream to write standard output to + */ + public void addOutputDestination(OutputStream stream) { + out.addOutputStream(stream); + } + + /** + Add a new destination for the standard error stream from this process. Output data will be multiplexed to all destinations. + @param stream An OutputStream to write standard error to + */ + public void addErrorDestination(OutputStream stream) { + error.addOutputStream(stream); + } + + /** + Find out whether this process is running or not + @return true iff the process is running + */ + public synchronized boolean isRunning() { + if (process==null) return false; + try {process.exitValue();} + catch (IllegalThreadStateException e) {return true;} + return false; + } + + /** + Stop the process + */ + public synchronized void stop() { + if (process==null) return; + out.kill(); + error.kill(); + process.destroy(); + process = null; + } +} + diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/RunSession.java b/modules/oldsims/rescuecore/tools/simulationrunner/RunSession.java new file mode 100644 index 0000000000000000000000000000000000000000..17a820f35ccf332975a601de1450ed130dcf5364 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/RunSession.java @@ -0,0 +1,335 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +// FIXME: Clients need to make a callback to let the server know when they are finished + +import java.io.*; +import java.net.*; +import java.util.*; + +public class RunSession implements SimulationConstants { + private final static String DEFAULT_SESSION_FILE = "session.xml"; + private final static String DEFAULT_TEAM_DIRECTORY = "teams"; + private final static String DEFAULT_CLIENTS_FILE = "clients.txt"; + + public static void main(String[] args) { + String sessionFile = DEFAULT_SESSION_FILE; + String teamDir = DEFAULT_TEAM_DIRECTORY; + String clientsFile = DEFAULT_CLIENTS_FILE; + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase("-s") || args[i].equalsIgnoreCase("--session")) { + sessionFile = args[++i]; + } + else if (args[i].equalsIgnoreCase("-t") || args[i].equalsIgnoreCase("--teams")) { + teamDir = args[++i]; + } + else if (args[i].equalsIgnoreCase("-c") || args[i].equalsIgnoreCase("--clients")) { + clientsFile = args[++i]; + } + else if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) { + printHelp(); + return; + } + else { + System.err.println("Unrecognised option: "+args[i]); + printHelp(); + return; + } + } + try { + // Load the client info + ClientInfo[] clients = loadClients(clientsFile); + if (clients==null) return; + // Load the teams + TeamInfo[] teams = loadTeams(teamDir); + if (teams==null) return; + // Load the session + Session session = new Session(sessionFile); + + // Say what's going on + System.out.println("Clients"); + System.out.println("*******"); + for (int i=0;i<clients.length;++i) { + System.out.println(clients[i]); + } + System.out.println(); + System.out.println("Teams"); + System.out.println("*****"); + for (int i=0;i<teams.length;++i) { + System.out.println("Team "+(i+1)); + System.out.println("\tCommands"); + System.out.println("\t========"); + for (int j=0;j<teams[i].commands.length;++j) { + System.out.println("\t"+teams[i].commands[j]); + } + System.out.println(); + System.out.println("\tFiles"); + System.out.println("\t========"); + for (int j=0;j<teams[i].files.length;++j) { + System.out.println("\t"+teams[i].files[j]); + } + System.out.println(); + } + System.out.println("Session"); + System.out.println("*******"); + System.out.println("Name: "+session.getName()); + System.out.println("Config: "+session.getConfigFile()); + System.out.println("Gis: "+session.getGisFile()); + System.out.println("Roads: "+session.getRoadFile()); + System.out.println("Nodes: "+session.getNodeFile()); + System.out.println("Buildings: "+session.getBuildingFile()); + System.out.println("Galpolydata: "+session.getGalFile()); + System.out.println("Shindopolydata: "+session.getShindoFile()); + RescueProcess[] allProcesses = session.getProcesses(); + for (int i=0;i<allProcesses.length;++i) { + System.out.println("Process "+i+": "+allProcesses[i]); + } + // Run everything + run(clients,teams,session); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private static void run(ClientInfo[] clients, TeamInfo[] teams, Session session) throws IOException { + sendSimulatorData(clients,session); + for (int i=0;i<teams.length;++i) { + run(clients,teams[i],session); + } + } + + private static boolean sendSimulatorData(ClientInfo[] clients, Session session) throws IOException { + // Send all simulator data to simulation clients + for (int i=0;i<clients.length;++i) { + if (clients[i].isSimulation) { + // Send all simulator data to simulation clients + Socket s = null; + DataOutputStream out = null; + DataInputStream in = null; + try { + s = new Socket(clients[i].address,clients[i].port); + out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); + in = new DataInputStream(new BufferedInputStream(s.getInputStream())); + if (!sendFile("config.txt",session.getConfigFile(),out,in)) return false; + if (!sendFile("road.bin",session.getRoadFile(),out,in)) return false; + if (!sendFile("node.bin",session.getNodeFile(),out,in)) return false; + if (!sendFile("building.bin",session.getBuildingFile(),out,in)) return false; + if (!sendFile("galpolydata.dat",session.getGalFile(),out,in)) return false; + if (!sendFile("shindopolydata.dat",session.getShindoFile(),out,in)) return false; + if (!sendFile("gisini.txt",session.getGisFile(),out,in)) return false; + } + finally { + if (in!=null) try {in.close();} catch (IOException e) {e.printStackTrace();} + if (out!=null) try {out.writeInt(COMMAND_END);out.flush();out.close();} catch (IOException e) {e.printStackTrace();} + if (s!=null) try {s.close();} catch (IOException e) {e.printStackTrace();} + } + } + } + return true; + } + + private static boolean run(ClientInfo[] clients, TeamInfo team, Session session) throws IOException { + try { + int numClients = 0; + for (int i=0;i<clients.length;++i) { + if (!clients[i].isSimulation) { + ++numClients; + // Send all agent data to agent clients + Socket s = null; + DataOutputStream out = null; + DataInputStream in = null; + try { + s = new Socket(clients[i].address,clients[i].port); + out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); + in = new DataInputStream(new BufferedInputStream(s.getInputStream())); + for (int j=0;j<team.files.length;++j) { + File file = new File(team.files[j]); + if (!sendFile(file.getName(),file,out,in)) return false; + } + } + finally { + if (in!=null) try {in.close();} catch (IOException e) {e.printStackTrace();} + if (out!=null) try {out.writeInt(COMMAND_END);out.flush();out.close();} catch (IOException e) {e.printStackTrace();} + if (s!=null) try {s.close();} catch (IOException e) {e.printStackTrace();} + } + } + } + // Now allocate processes to clients + int processesPerClient = (int)Math.ceil(((double)team.commands.length)/(double)numClients); + String[][] processes = new String[numClients][processesPerClient]; + int nextClient = 0; + int nextIndex = 0; + for (int i=0;i<team.commands.length;++i) { + processes[nextClient++][nextIndex] = team.commands[i]; + if (nextClient >= numClients) { + nextClient = 0; + ++nextIndex; + } + } + // Send the jobs to the clients + nextClient = 0; + for (int i=0;i<clients.length;++i) { + if (clients[i].isSimulation) continue; + sendProcesses(clients[i],processes[nextClient++]); + } + // Wait for a client to return + } + finally { + // Stop all the clients + } + return true; + } + + private static boolean sendFile(String name, String filename, DataOutputStream out, DataInputStream in) throws IOException { + return sendFile(name,new File(filename),out,in); + } + + private static boolean sendFile(String name, File file, DataOutputStream out, DataInputStream in) throws IOException { + long size = file.length(); + DataInputStream data = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); + return sendFile(name,data,size,out,in); + } + + private static boolean sendFile(String name, DataInputStream file, long size, DataOutputStream out, DataInputStream in) throws IOException { + out.writeInt(COMMAND_FILE); + out.writeUTF(name); + out.writeLong(size); + byte[] buffer = new byte[1024]; + long sent = 0; + while (sent < size) { + int n = file.read(buffer); + if (n > 0) { + out.write(buffer,0,n); + sent += n; + } + } + out.flush(); + return in.readInt()==COMMAND_SUCCESSFUL; + } + + private static boolean sendProcesses(ClientInfo client, String[] processes) throws IOException { + return true; + } + + private static void printHelp() { + System.err.println("Usage: RunSession [-s | --session <session file>] [-t | --teams <teams directory>] [-c | --clients <clients file>] [-h | --help]"); + System.err.println("-s\t--session\tLoad the session from <session file>. Default \""+DEFAULT_SESSION_FILE+"\""); + System.err.println("-t\t--teams\tLoad the teams from <teams directory>. Each subdirectory in <teams directory> should contain a file called \"commandlines.txt\" that contains all command lines used to launch the agents, as well as the binaries for those agents and any supporting files. Default \""+DEFAULT_TEAM_DIRECTORY+"\""); + System.err.println("-c\t--clients\tGet the list of clients from <clients file>. Default \""+DEFAULT_CLIENTS_FILE+"\""); + System.err.println("-h\t--help\tPrint this help message"); + } + + private static ClientInfo[] loadClients(String clientFile) throws IOException { + BufferedReader in = new BufferedReader(new FileReader(clientFile)); + String line = null; + Collection result = new ArrayList(); + boolean isSim = true; + do { + line = in.readLine(); + if (line!=null) { + line = line.trim(); + if (line.startsWith("#") || line.equals("")) continue; + if (line.equalsIgnoreCase("[simulation]")) isSim = true; + else if (line.equalsIgnoreCase("[agents]")) isSim = false; + else { + int index = line.indexOf(":"); + String addressString = null; + int port = DEFAULT_CLIENT_PORT; + try { + if (index==-1) addressString = line; + else { + addressString = line.substring(0,index); + port = Integer.parseInt(line.substring(index+1)); + } + InetAddress address = InetAddress.getByName(addressString); + result.add(new ClientInfo(isSim,address,port)); + } + catch (UnknownHostException e) { + System.err.println("Cannot find address \""+addressString+"\""); + } + catch (NumberFormatException e) { + System.err.println("Bad port number in "+line); + } + } + } + } while (line!=null); + return (ClientInfo[])result.toArray(new ClientInfo[result.size()]); + } + + private static TeamInfo[] loadTeams(String teamsDirName) throws IOException { + File teamsDir = new File(teamsDirName); + if (!teamsDir.isDirectory()) { + System.err.println("Error loading teams: "+teamsDirName+" is not a directory"); + return null; + } + Collection result = new ArrayList(); + File[] subdirs = teamsDir.listFiles(new FileFilter() {public boolean accept(File f) {return f.isDirectory();}}); + for (int i=0;i<subdirs.length;++i) { + // Make sure commandlines.txt exists + File commandsFile = new File(subdirs[i].getAbsolutePath()+File.separator+"commandlines.txt"); + if (!commandsFile.exists()) { + System.err.println("WARNING: Directory "+subdirs[i].getAbsolutePath()+" does not contain \"commandlines.txt\""); + continue; + } + File[] allFiles = subdirs[i].listFiles(new FileFilter() {public boolean accept(File f) {return !f.getName().equals("commandlines.txt");}}); + Collection allCommands = new ArrayList(); + BufferedReader in = new BufferedReader(new FileReader(commandsFile)); + String line = null; + do { + line = in.readLine(); + if (line!=null) { + line = line.trim(); + if (line.startsWith("#")) continue; + allCommands.add(line); + } + } while (line!=null); + in.close(); + String[] commands = (String[])allCommands.toArray(new String[allCommands.size()]); + String[] files = new String[allFiles.length]; + for (int j=0;j<files.length;++j) files[j] = allFiles[j].getAbsolutePath(); + result.add(new TeamInfo(commands,files)); + } + return (TeamInfo[])result.toArray(new TeamInfo[result.size()]); + } + + private static class ClientInfo { + boolean isSimulation; + InetAddress address; + int port; + + public ClientInfo(boolean sim, InetAddress a, int p) { + isSimulation = sim; + address = a; + port = p; + } + + public String toString() { + return address.getHostName()+":"+port+" "+(isSimulation?"running simulation":"running agents"); + } + } + + private static class TeamInfo { + String[] commands; + String[] files; + + public TeamInfo(String[] c, String[] f) { + commands = c; + files = f; + } + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/Session.java b/modules/oldsims/rescuecore/tools/simulationrunner/Session.java new file mode 100644 index 0000000000000000000000000000000000000000..bc099ed1bb9f4620b220ee2a912f92c8ba373256 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/Session.java @@ -0,0 +1,92 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.io.*; +import java.util.*; + +public class Session { + private String name; + private String configFile, roadFile, nodeFile, buildingFile, gisFile, shindoFile, galFile; + private RescueProcess[] simulatorComponents; + + public Session(String xmlFile) throws IOException, XMLDecodingException { + XMLTag tag = XMLDocumentParser.parse(new BufferedReader(new FileReader(xmlFile))); + XMLTag nameTag = tag.getChild("name",false); + XMLTag configTag = tag.getChild("config"); + XMLTag roadTag = tag.getChild("road"); + XMLTag nodeTag = tag.getChild("node"); + XMLTag buildingTag = tag.getChild("building"); + XMLTag gisTag = tag.getChild("gis"); + XMLTag shindoTag = tag.getChild("shindo"); + XMLTag galTag = tag.getChild("gal"); + name = nameTag.getText(); + configFile = configTag==null?"config.txt":configTag.getText(); + roadFile = roadTag==null?"road.bin":roadTag.getText(); + nodeFile = nodeTag==null?"node.bin":nodeTag.getText(); + buildingFile = buildingTag==null?"building.bin":buildingTag.getText(); + gisFile = gisTag==null?"gis.ini":gisTag.getText(); + shindoFile = shindoTag==null?"shindopolydata.dat":shindoTag.getText(); + galFile = galTag==null?"galpolydata.dat":galTag.getText(); + XMLTag simulatorsTag = tag.getChild("simulators",false); + List simulators = simulatorsTag.getChildren("simulator"); + simulatorComponents = new RescueProcess[simulators.size()]; + int i=0; + for (Iterator it = simulators.iterator();it.hasNext();) { + XMLTag next = (XMLTag)it.next(); + XMLTag nextName = next.getChild("name",false); + XMLTag commandLine = next.getChild("commandline",false); + XMLTag trigger = next.getChild("trigger"); + simulatorComponents[i++] = new RescueProcess(nextName.getText(),commandLine.getText(),trigger==null?null:trigger.getText(),null); + } + } + + public String getName() { + return name; + } + + public String getConfigFile() { + return configFile; + } + + public String getRoadFile() { + return roadFile; + } + + public String getNodeFile() { + return nodeFile; + } + + public String getBuildingFile() { + return buildingFile; + } + + public String getGalFile() { + return galFile; + } + + public String getShindoFile() { + return shindoFile; + } + + public String getGisFile() { + return gisFile; + } + + public RescueProcess[] getProcesses() { + return simulatorComponents; + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/SimulationClient.java b/modules/oldsims/rescuecore/tools/simulationrunner/SimulationClient.java new file mode 100644 index 0000000000000000000000000000000000000000..4ca441bcb549e0eb55ecbdbffc71d231c4a01122 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/SimulationClient.java @@ -0,0 +1,205 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.io.*; +import java.net.*; + +public class SimulationClient implements SimulationConstants { + private ProcessHandler processHandler; + private InputHandler input; + + public static void main(String[] args) { + int port = DEFAULT_CLIENT_PORT; + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase("--help")) { + printHelp(); + return; + } + else if (args[i].equalsIgnoreCase("-p") || args[i].equalsIgnoreCase("--port")) { + port = Integer.parseInt(args[++i]); + } + } + try {new SimulationClient(port);} + catch (IOException e) { + e.printStackTrace(); + } + } + + private static void printHelp() { + System.out.println("Usage: SimulationClient [-p | --port <port number>]"); + System.out.println("-p\t--port\tListen on <port number>. Default "+DEFAULT_CLIENT_PORT); + } + + public SimulationClient(int port) throws IOException { + input = new InputHandler(port); + new Thread(input).start(); + } + + private void kill() { + input.kill(); + } + + private synchronized boolean runProcesses(RescueProcess[] processes) { + if (processHandler!=null) return false; + processHandler = new ProcessHandler(processes,null); + new Thread() {public void run() {processHandler.runAll();stopProcesses();}}.start(); + return true; + } + + private synchronized void stopProcesses() { + if (processHandler==null) return; + processHandler.stopAll(); + processHandler = null; + } + + private class InputHandler implements Runnable { + private volatile boolean alive; + private volatile boolean running; + private ServerSocket server; + + public InputHandler(int port) throws IOException { + server = new ServerSocket(port); + server.setSoTimeout(1000); + alive = true; + running = true; + } + + public synchronized void kill() { + running = false; + while (alive) try {wait(1000);} catch (InterruptedException e) {break;} + } + + public void run() { + try { + while (running) { + // Listen for input + Socket s = server.accept(); + handle(s); + s.close(); + } + } + catch (IOException e) { + e.printStackTrace(); + } + try { + server.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + synchronized(this) { + alive = false; + notifyAll(); + } + } + + private void handle(Socket s) throws IOException { + DataInputStream in = null; + DataOutputStream out = null; + try { + out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream())); + in = new DataInputStream(new BufferedInputStream(s.getInputStream())); + int command; + do { + command = in.readInt(); + switch (command) { + case COMMAND_STOP: + stopProcesses(); + out.writeInt(COMMAND_SUCCESSFUL); + break; + case COMMAND_START: + if (handleStart(in)) out.writeInt(COMMAND_SUCCESSFUL); + else out.writeInt(COMMAND_FAILED); + break; + case COMMAND_FILE: + if (handleFile(in,out)) out.writeInt(COMMAND_SUCCESSFUL); + else out.writeInt(COMMAND_FAILED); + break; + case COMMAND_END: + out.writeInt(COMMAND_SUCCESSFUL); + break; + default: + System.err.println("Unrecognised command: "+command); + out.writeInt(UNKNOWN_COMMAND); + } + out.flush(); + } + while (command != COMMAND_END); + } + catch (IOException e) { + out.writeInt(COMMAND_FAILED); + throw e; + } + finally { + if (in!=null) in.close(); + if (out!=null) { + try { + out.flush(); + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private boolean handleStart(DataInputStream in) throws IOException { + int num = in.readInt(); + RescueProcess[] processes = new RescueProcess[num]; + for (int i=0;i<num;++i) { + processes[i] = new RescueProcess(in.readUTF(),in.readUTF(),in.readUTF(),null); + } + return runProcesses(processes); + } + + private boolean handleFile(DataInputStream in, DataOutputStream out) throws IOException { + String name = in.readUTF(); + long size = in.readLong(); + OutputStream file = null; + try { + try { + file = new BufferedOutputStream(new FileOutputStream(name)); + } + catch (IOException e) { + return false; + } + byte[] buffer = new byte[1024]; + long count = 0; + while (count < size) { + int num = in.read(buffer); + if (num > 0) try { + file.write(buffer,0,num); + count += num; + } + catch (IOException e) { + return false; + } + } + } + finally { + if (file!=null) try { + file.flush(); + file.close(); + } + catch (IOException e) { + } + } + return true; + } + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/SimulationConstants.java b/modules/oldsims/rescuecore/tools/simulationrunner/SimulationConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..11b1d1c7c2f1948e5ff77dc39a85b2864ea554c2 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/SimulationConstants.java @@ -0,0 +1,29 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +public interface SimulationConstants { + public final static int DEFAULT_CLIENT_PORT = 12536; + + public final static int COMMAND_STOP = 0; + public final static int COMMAND_START = 1; + public final static int COMMAND_FILE = 2; + public final static int COMMAND_END = 3; + + public final static int COMMAND_FAILED = -3; + public final static int COMMAND_SUCCESSFUL = -2; + public final static int UNKNOWN_COMMAND = -1; +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/StartSimulation.java b/modules/oldsims/rescuecore/tools/simulationrunner/StartSimulation.java new file mode 100644 index 0000000000000000000000000000000000000000..2821259e7c0d1df39fc9c0036a2d14d8d496545f --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/StartSimulation.java @@ -0,0 +1,140 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.util.*; +import java.io.*; +import javax.swing.*; +import java.awt.event.*; + +public class StartSimulation { + private final static String DEFAULT_CONFIG_FILE = "simulation.config"; + private final static String DEFAULT_LOG_PREFIX = ""; + + private final static String[] FILTER_NAMES = {"Kernel"}; + private final static Class[] FILTER_CLASSES = {KernelProcessFilter.class}; + + private final static Map filters; + private ProcessHandler handler; + + private final Thread HALT = new Thread() {public void run() {handler.stopAll();}}; + + static { + filters = new HashMap(); + for (int i=0;i<FILTER_NAMES.length;++i) + filters.put(FILTER_NAMES[i],FILTER_CLASSES[i]); + } + + public static void main(String[] args) { + new StartSimulation(args); + } + + private StartSimulation(String[] args) { + String config = DEFAULT_CONFIG_FILE; + String logPrefix = DEFAULT_LOG_PREFIX; + boolean gui = true; + for (int i=0;i<args.length;++i) { + if (args[i].equalsIgnoreCase("-c") || args[i].equalsIgnoreCase("--config")) { + config = args[++i]; + } + else if (args[i].equalsIgnoreCase("-l") || args[i].equalsIgnoreCase("--log-prefix")) { + logPrefix = args[++i]; + } + else if (args[i].equalsIgnoreCase("-g") || args[i].equalsIgnoreCase("--nogui")) { + gui = false; + } + else { + printUsage(); + return; + } + } + try { + // System.out.println("Reading config file: "+config); + ConfigFile configFile = new ConfigFile(config,logPrefix); + // System.out.println("Starting "+configFile.processes.size()+" processes"); + // int i=1; + // for (Iterator it = configFile.processes.iterator();it.hasNext();++i) { + // RescueProcess next = (RescueProcess)it.next(); + // System.out.println(i+":\t"+next.name+" ("+next.commandline+")"); + // } + // Start all the processes + ProcessViewer viewer = new ProcessViewer((RescueProcess[])configFile.getProcesses().toArray(new RescueProcess[0])); + handler = new ProcessHandler((RescueProcess[])configFile.getProcesses().toArray(new RescueProcess[0]),viewer); + if (gui) { + JFrame frame = new JFrame("Robocup Rescue Simulation"); + frame.setContentPane(viewer); + frame.pack(); + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) { + SwingUtilities.invokeLater(HALT); + }}); + } + Runtime.getRuntime().addShutdownHook(HALT); + handler.runAll(); + } + catch (IOException e) { + e.printStackTrace(); + } + System.exit(0); + } + + private void printUsage() { + System.out.println("Usage: StartSimulation [(-c | --config) <config file>] [(-l | --log-prefix) <log prefix>] [(-g | --nogui)]"); + System.out.println("-c\t--config\tUse <config file> to configure the simulator. Default \"simulation.config\""); + System.out.println("-l\t--log-prefix\tPrepend all log files with <log prefix>. Default is no prefix"); + System.out.println("-g\t--nogui\tDon't show the gui"); + } + + private abstract class ProcessFilter implements Runnable { + private boolean running, alive; + private BufferedReader reader; + + public ProcessFilter() { + + } + + public void kill() { + running = false; + synchronized(this) { + while(alive) try {wait(1000);} catch (InterruptedException e) {break;} + } + } + + public void run() { + running = alive = true; + while (running) { + try { + String line = reader.readLine(); + } + catch (IOException e) { + e.printStackTrace(); + break; + } + } + synchronized(this) { + alive = false; + notifyAll(); + } + } + + public JComponent getViewComponent() { + return null; + } + } + + private class KernelProcessFilter extends ProcessFilter { + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/Team.java b/modules/oldsims/rescuecore/tools/simulationrunner/Team.java new file mode 100644 index 0000000000000000000000000000000000000000..5a0ad548a59128aee713acbd98508a6e516b4fa2 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/Team.java @@ -0,0 +1,11 @@ +package rescuecore.tools.simulationrunner; + +public class Team { + private String name; + private String[] commandLines; + + public Team(String name, String[] commandLines) { + this.name = name; + this.commandLines = commandLines; + } +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/XMLDecodingException.java b/modules/oldsims/rescuecore/tools/simulationrunner/XMLDecodingException.java new file mode 100644 index 0000000000000000000000000000000000000000..7a8ef402390ebbc0a7cc21a12cc123e588ad70a6 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/XMLDecodingException.java @@ -0,0 +1,25 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +public class XMLDecodingException extends Exception { + public XMLDecodingException() {super();} + public XMLDecodingException(String s) {super(s);} + public XMLDecodingException(XMLTag tag) {super(tag.getName());} + public XMLDecodingException(Throwable t) {super(t);} + public XMLDecodingException(String s, Throwable t) {super(s,t);} + public XMLDecodingException(XMLTag tag, Throwable t) {super(tag.getName(),t);} +} diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/XMLDocumentParser.java b/modules/oldsims/rescuecore/tools/simulationrunner/XMLDocumentParser.java new file mode 100644 index 0000000000000000000000000000000000000000..c468c99d4db2c9e804edb897247ab4422d0f3ba8 --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/XMLDocumentParser.java @@ -0,0 +1,121 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The + * University of Auckland + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or + * The University of Auckland nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package rescuecore.tools.simulationrunner; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Stack; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +public class XMLDocumentParser { + + public static XMLTag parse(InputStream in) + throws XMLDecodingException, IOException { + try { + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + Handler handler = new Handler(); + parser.parse(new InputSource(in), handler); + return handler.root; + } catch (ParserConfigurationException e) { + throw new XMLDecodingException(e); + } catch (SAXException e) { + throw new XMLDecodingException(e); + } + } + + + public static XMLTag parse(Reader in) + throws XMLDecodingException, IOException { + try { + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + Handler handler = new Handler(); + parser.parse(new InputSource(in), handler); + return handler.root; + } catch (ParserConfigurationException e) { + throw new XMLDecodingException(e); + } catch (SAXException e) { + throw new XMLDecodingException(e); + } + } + + private static class Handler extends DefaultHandler { + + XMLTag root; + XMLTag current; + Stack stack; + + public void startDocument() { + root = null; + current = null; + stack = new Stack(); + } + + + public void startElement(String uri, String localname, String qName, + Attributes attributes) { + if (current != null) { + stack.push(current); + XMLTag next = new XMLTag(qName); + current.addChild(next); + current = next; + } else { + current = new XMLTag(qName); + root = current; + } + for (int i = 0; i < attributes.getLength(); ++i) + current.setAttribute(attributes.getQName(i), attributes.getValue(i)); + } + + + public void endElement(String uri, String localname, String qName) { + if (stack.size() == 0) + current = null; + else + current = (XMLTag) stack.pop(); + } + + + public void characters(char[] data, int start, int length) { + String s = new String(data, start, length); + if (s.trim().equals("")) + return; + if (current != null) + current.appendText(s); + } + } +} \ No newline at end of file diff --git a/modules/oldsims/rescuecore/tools/simulationrunner/XMLTag.java b/modules/oldsims/rescuecore/tools/simulationrunner/XMLTag.java new file mode 100644 index 0000000000000000000000000000000000000000..5a0ee991eaae41cf62bb74b7807effb08be1dc6f --- /dev/null +++ b/modules/oldsims/rescuecore/tools/simulationrunner/XMLTag.java @@ -0,0 +1,205 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.tools.simulationrunner; + +import java.util.*; +import java.io.*; + +public class XMLTag { + private XMLTag parent; + private List children; + private String name; + private Map attributes; + private StringBuffer textBuffer; + private String text; + + public XMLTag(String name) { + this.name = name; + attributes = new HashMap(); + textBuffer = new StringBuffer(); + children = new ArrayList(); + } + + public XMLTag(String name, String text) { + this.name = name; + attributes = new HashMap(); + textBuffer = new StringBuffer(); + children = new ArrayList(); + appendText(text); + } + + public String getName() { + return name; + } + + public void setAttribute(String name, String value) { + attributes.put(name,value); + } + + public String getAttribute(String name) { + return (String)attributes.get(name); + } + + public void appendText(String s) { + textBuffer.append(s); + text = null; + } + + public String getText() { + if (text==null) text = textBuffer.toString(); + return text; + } + + public void setParent(XMLTag parent) { + this.parent = parent; + } + + public XMLTag getParent() { + return parent; + } + + public void addChild(XMLTag child) { + children.add(child); + child.parent = this; + } + + public List getChildren() { + return children; + } + + public List getChildren(String name) { + List result = new ArrayList(); + for (Iterator it = children.iterator();it.hasNext();) { + XMLTag next = (XMLTag)it.next(); + if (next.name.equals(name)) result.add(next); + } + return result; + } + + public XMLTag getChild(String name) throws XMLDecodingException { + return getChild(name,true); + } + + public XMLTag getChild(String name, boolean canBeNull) throws XMLDecodingException { + List allChildren = getChildren(name); + if (allChildren.size()>1) throw new XMLDecodingException(this+" should have no more than one child called "+name); + if (allChildren.size()==0) { + if (canBeNull) return null; + throw new XMLDecodingException(this+" should have exactly one child called "+name); + } + return (XMLTag)allChildren.get(0); + } + + public void prettyPrint(PrintWriter out, String prefix) { + prettyPrint(this,out,prefix); + } + + public void print(PrintWriter out) { + print(this,out); + } + + public String toString() { + StringWriter string = new StringWriter(); + PrintWriter out = new PrintWriter(string); + print(out); + return string.toString(); + } + + public static void print(XMLTag tag, PrintWriter out) { + // If this is an empty tag then use the <tagname/> form + if (tag.children.size()==0 && tag.attributes.size()==0 && tag.getText().equals("")) { + out.print("<"); + out.print(tag.name); + out.print("/>"); + return; + } + // Start tag + out.print("<"); + out.print(tag.name); + // Attributes + for (Iterator it = tag.attributes.keySet().iterator();it.hasNext();) { + out.print(" "); + String name = (String)it.next(); + String value = (String)tag.attributes.get(name); + char quoteChar = '"'; + if (value.indexOf("\"")!=-1) quoteChar='\''; + out.print(name); + out.print("="); + out.print(quoteChar); + out.print(value); + out.print(quoteChar); + } + out.print(">"); + out.print(tag.getText()); + // Children + for (Iterator it = tag.children.iterator();it.hasNext();) { + print((XMLTag)it.next(),out); + } + // End tag + out.print("</"); + out.print(tag.name); + out.print(">"); + } + + public static void prettyPrint(XMLTag tag, PrintWriter out) { + prettyPrint(tag,out,""); + } + + public static void prettyPrint(XMLTag tag, PrintWriter out, String prefix) { + // If this is an empty tag then use the <tagname/> form + if (tag.children.size()==0 && tag.attributes.size()==0 && tag.getText().equals("")) { + out.print(prefix); + out.print("<"); + out.print(tag.name); + out.println("/>"); + return; + } + // Start tag + out.print(prefix); + out.print("<"); + out.print(tag.name); + // Attributes + for (Iterator it = tag.attributes.keySet().iterator();it.hasNext();) { + out.print(" "); + String name = (String)it.next(); + String value = (String)tag.attributes.get(name); + char quoteChar = '"'; + if (value.indexOf("\"")!=-1) quoteChar='\''; + out.print(name); + out.print("="); + out.print(quoteChar); + out.print(value); + out.print(quoteChar); + } + out.print(">"); + // Text + String text = tag.getText(); + if (text==null || text.trim().equals("")) out.println(); + else out.print(text); + // Children + if (tag.children.size()>0) { + String childPrefix = prefix+" "; + for (Iterator it = tag.children.iterator();it.hasNext();) { + prettyPrint((XMLTag)it.next(),out,childPrefix); + } + // End tag + out.print(prefix); + } + out.print("</"); + out.print(tag.name); + out.println(">"); + } +} diff --git a/modules/oldsims/rescuecore/view/BuildingRenderer.java b/modules/oldsims/rescuecore/view/BuildingRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..61d5c21a3602b32e7efa1d464d366dc6b49999e4 --- /dev/null +++ b/modules/oldsims/rescuecore/view/BuildingRenderer.java @@ -0,0 +1,132 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import rescuecore.objects.*; +import java.awt.*; + +public abstract class BuildingRenderer implements MapRenderer { + private final static BuildingRenderer ORDINARY = new OrdinaryBuildingRenderer(); + + public static BuildingRenderer ordinaryBuildingRenderer() { + return ORDINARY; + } + + public static BuildingRenderer filledBuildingRenderer(int fillMode, Color colour) { + return new FilledBuildingRenderer(fillMode,colour); + } + + public static BuildingRenderer outlinedBuildingRenderer(int outlineMode, Color colour) { + return new OutlinedBuildingRenderer(outlineMode,colour); + } + + public boolean canRender(Object o) {return (o instanceof Building);} + + private static class OrdinaryBuildingRenderer extends BuildingRenderer { + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Building b = (Building)o; + int[] apexes = b.getApexes(); + int[] xs = new int[apexes.length/2]; + int[] ys = new int[apexes.length/2]; + for (int i=0;i<xs.length;++i) { + xs[i] = transform.toScreenX(apexes[i*2]); + ys[i] = transform.toScreenY(apexes[i*2+1]); + } + int fieryness = b.getFieryness(); + Color colour = ViewConstants.BUILDING_COLOUR; + if (b.isFireStation()) colour = ViewConstants.FIRE_STATION_COLOUR; + if (b.isAmbulanceCenter()) colour = ViewConstants.AMBULANCE_CENTER_COLOUR; + if (b.isPoliceOffice()) colour = ViewConstants.POLICE_OFFICE_COLOUR; + if (b.isRefuge()) colour = ViewConstants.REFUGE_COLOUR; + switch (fieryness) { + case RescueConstants.FIERYNESS_HEATING: + colour = ViewConstants.HEATING_COLOUR; + break; + case RescueConstants.FIERYNESS_BURNING: + colour = ViewConstants.FIRE_COLOUR; + break; + case RescueConstants.FIERYNESS_INFERNO: + colour = ViewConstants.INFERNO_COLOUR; + break; + case RescueConstants.FIERYNESS_WATER_DAMAGE: + colour = ViewConstants.WATER_DAMAGE_COLOUR; + break; + case RescueConstants.FIERYNESS_SLIGHTLY_BURNT: + case RescueConstants.FIERYNESS_MODERATELY_BURNT: + case RescueConstants.FIERYNESS_VERY_BURNT: + colour = ViewConstants.EXTINGUISHED_COLOUR; + break; + case RescueConstants.FIERYNESS_BURNT_OUT: + colour = ViewConstants.BURNT_OUT_COLOUR; + break; + } + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,colour); + g.fillPolygon(xs,ys,xs.length); + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,Color.black); + g.drawPolygon(xs,ys,xs.length); + return new Polygon(xs,ys,xs.length); + } + } + + private static class FilledBuildingRenderer extends BuildingRenderer { + private int mode; + private Color colour; + + FilledBuildingRenderer(int mode, Color colour) { + this.mode = mode; + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Building b = (Building)o; + int[] apexes = b.getApexes(); + int[] xs = new int[apexes.length/2]; + int[] ys = new int[apexes.length/2]; + for (int i=0;i<xs.length;++i) { + xs[i] = transform.toScreenX(apexes[i*2]); + ys[i] = transform.toScreenY(apexes[i*2+1]); + } + RenderTools.setFillMode(g,mode,colour); + g.fillPolygon(xs,ys,xs.length); + return new Polygon(xs,ys,xs.length); + } + } + + private static class OutlinedBuildingRenderer extends BuildingRenderer { + private int mode; + private Color colour; + + OutlinedBuildingRenderer(int mode, Color colour) { + this.mode = mode; + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Building b = (Building)o; + int[] apexes = b.getApexes(); + int[] xs = new int[apexes.length/2]; + int[] ys = new int[apexes.length/2]; + for (int i=0;i<xs.length;++i) { + xs[i] = transform.toScreenX(apexes[i*2]); + ys[i] = transform.toScreenY(apexes[i*2+1]); + } + RenderTools.setLineMode(g,mode,colour); + g.drawPolygon(xs,ys,xs.length); + return new Polygon(xs,ys,xs.length); + } + } +} diff --git a/modules/oldsims/rescuecore/view/ConvexHull.java b/modules/oldsims/rescuecore/view/ConvexHull.java new file mode 100644 index 0000000000000000000000000000000000000000..59137026226f428fdf5823958d016dd10d0e2423 --- /dev/null +++ b/modules/oldsims/rescuecore/view/ConvexHull.java @@ -0,0 +1,143 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.util.*; +import rescuecore.*; + +public class ConvexHull { + private int[] xs; + private int[] ys; + private int numPoints; + private HullPoint pivot; + + public ConvexHull(RescueObject[] os, Memory memory) throws CannotFindLocationException { + if(os.length > 1){ + double[] tempXs = new double[os.length]; + double[] tempYs = new double[os.length]; + for (int i=0;i<os.length;++i) { + int[] xy = memory.getXY(os[i]); + tempXs[i] = xy[0]; + tempYs[i] = xy[1]; + } + int[] hull = convexHull(tempXs,tempYs); + xs = new int[hull.length]; + ys = new int[hull.length]; + for (int i=0;i<hull.length;++i) { + xs[i] = (int)(tempXs[hull[i]]); + ys[i] = (int)(tempYs[hull[i]]); + } + numPoints = hull.length; + } + else{ + numPoints = os.length; + xs = new int[numPoints]; + ys = new int[numPoints]; + for(int i = 0; i < numPoints; i++){ //zero or one iterations + int[] xy = memory.getXY(os[i]); + xs[i] = xy[0]; + ys[i] = xy[1]; + } + } + } + + public int[] getXs() { + return xs; + } + + public int[] getYs() { + return ys; + } + + public int countPoints() { + return numPoints; + } + + /** Return the indices of the points that lie on a convex hull surrounding all points in the set given. The hull is returned starting with the point with highest y-coordinate and moving clockwise */ + private int[] convexHull(double[] xs, double[] ys) { + // Find the highest y; + int highest = 0; + // System.out.println("Finding convex hull of "+xs.length+" points"); + HullPoint[] points = new HullPoint[xs.length]; + for (int i=0;i<ys.length;++i) { + // System.out.println("Point "+i+" ("+xs[i]+","+ys[i]+")"); + if (ys[i] > ys[highest]) highest = i; + points[i] = new HullPoint(i,xs[i],ys[i]); + } + // System.out.println("Highest point: "+highest+" ("+xs[highest]+","+ys[highest]+")"); + // Swap point zero with highest + HullPoint temp = points[0]; + points[0] = points[highest]; + points[highest] = temp; + pivot = points[0]; + Arrays.sort(points,new Comparator() { + public int compare(Object o1, Object o2) { + if (o1==pivot) return -1; + if (o2==pivot) return 1; + double angle = ccw((HullPoint)o1,(HullPoint)o2); + if (angle>0) return -1; + return 1; + } + }); + HullPoint[] hull = new HullPoint[points.length]; + hull[0] = points[0]; + hull[1] = points[1]; + int hullSize = 2; + for (int i=2;i<points.length;++i) { + while (rightTurn(hull[hullSize-2],hull[hullSize-1],points[i])) { + --hullSize; + } + hull[hullSize++] = points[i]; + } + int[] result = new int[hullSize]; + for (int i=0;i<hullSize;++i) { + result[i] = hull[i].index; + } + return result; + } + + private boolean rightTurn(HullPoint p1, HullPoint p2) { + return rightTurn(p1,pivot,p2); + } + + private boolean rightTurn(HullPoint p1, HullPoint p2, HullPoint p3) { + // if (p1==p2 || p1==p3 || p2==p3) System.err.println("WARNING: Checking for right turn with identical points: "+p1+", "+p2+", "+p3); + return (p1.x - p2.x) * (p3.y - p2.y) - (p1.y - p2.y) * (p3.x - p2.x) > 0; + } + + private double ccw(HullPoint p1, HullPoint p2) { + return ccw(p1,pivot,p2); + } + + private double ccw(HullPoint p1, HullPoint p2, HullPoint p3) { + return (p1.x - p2.x) * (p3.y - p2.y) - (p1.y - p2.y) * (p3.x - p2.x); + } + + private class HullPoint { + int index; + double x, y; + + HullPoint(int index, double x, double y) { + this.index = index; + this.x = x; + this.y = y; + } + + public String toString() { + return "Point "+index+" ("+x+","+y+")"; + } + } +} diff --git a/modules/oldsims/rescuecore/view/ConvexHullRenderer.java b/modules/oldsims/rescuecore/view/ConvexHullRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..24547ac00b55f9c766c6c865d10cf7d0fa20dd2f --- /dev/null +++ b/modules/oldsims/rescuecore/view/ConvexHullRenderer.java @@ -0,0 +1,79 @@ +/* + * Last change: $Date: 2004/08/09 23:56:57 $ + * $Revision: 1.7 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.*; +import rescuecore.Memory; + +public class ConvexHullRenderer implements MapRenderer { + public static final ConvexHullRenderer RED = new ConvexHullRenderer(Color.RED); + public static final ConvexHullRenderer GREEN = new ConvexHullRenderer(Color.GREEN); + public static final ConvexHullRenderer BLUE = new ConvexHullRenderer(Color.BLUE); + public static final ConvexHullRenderer ORANGE = new ConvexHullRenderer(Color.ORANGE); + public static final ConvexHullRenderer YELLOW = new ConvexHullRenderer(Color.YELLOW); + public static final ConvexHullRenderer WHITE = new ConvexHullRenderer(Color.WHITE); + + private Color outline; + private Color fill; + + private ConvexHullRenderer(Color outline){ + this.outline = outline; + this.fill = new Color(outline.getRed(),outline.getGreen(),outline.getBlue(),96); + } + + public boolean canRender(Object o) { + return o instanceof ConvexHull; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + ConvexHull hull = (ConvexHull)o; + // Draw the points + int[] hullXs = hull.getXs(); + int[] hullYs = hull.getYs(); + int[] xs = new int[hullXs.length]; + int[] ys = new int[hullYs.length]; + System.arraycopy(hullXs,0,xs,0,xs.length); + System.arraycopy(hullYs,0,ys,0,ys.length); + int num = hull.countPoints(); + g.setColor(outline); + for (int i=0;i<num;++i) { + g.drawRect(transform.toScreenX(xs[i])-1,transform.toScreenY(ys[i])-1,3,3); + } + // Draw segments + /*int lastX = transform.toScreenX(xs[0]); + int lastY = transform.toScreenY(ys[0]); + int firstX = lastX; + int firstY = lastY; + for (int i=1;i<num;++i) { + int x = transform.toScreenX(xs[i]); + int y = transform.toScreenY(ys[i]); + g.drawLine(lastX,lastY,x,y); + lastX = x; + lastY = y; + } + g.drawLine(lastX,lastY,firstX,firstY);*/ + for (int i=0;i<num;++i) { + xs[i] = transform.toScreenX(xs[i]); + ys[i] = transform.toScreenY(ys[i]); + } + Polygon p = new Polygon(xs,ys,xs.length); + g.setColor(fill); + g.fillPolygon(p); + g.setColor(outline); + g.drawPolygon(p); + return p; + } +} diff --git a/modules/oldsims/rescuecore/view/Display.java b/modules/oldsims/rescuecore/view/Display.java new file mode 100644 index 0000000000000000000000000000000000000000..165b9239cceb3d3e0aa115f82ba701c435d965d7 --- /dev/null +++ b/modules/oldsims/rescuecore/view/Display.java @@ -0,0 +1,87 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.*; +import javax.swing.*; +import java.text.NumberFormat; +import rescuecore.Memory; + +public class Display extends JPanel { + private Map map; + private ObjectInspector inspector; + private JLabel timestep; + private JLabel score; + + private final static NumberFormat FORMAT = NumberFormat.getInstance(); + + static { + FORMAT.setMaximumFractionDigits(4); + FORMAT.setMinimumFractionDigits(0); + FORMAT.setMaximumIntegerDigits(100); + FORMAT.setMinimumIntegerDigits(1); + } + + public static Display showDisplay(Memory m) { + return showDisplay(Map.defaultMap(m)); + } + + public static Display showDisplay(Map map) { + final JFrame frame = new JFrame(); + Display result = new Display(map); + frame.setContentPane(result); + frame.pack(); + SwingUtilities.invokeLater(new Runnable() {public void run() { + frame.setVisible(true); + }}); + return result; + } + + public Display(Memory m) { + this(Map.defaultMap(m)); + } + + public Display(Map map) { + super(new BorderLayout()); + this.map = map; + inspector = new ObjectInspector(); + timestep = new JLabel("300"); + score = new JLabel("100.0000"); + JPanel top = new JPanel(new FlowLayout()); + top.add(new JLabel("Time: ")); + top.add(timestep); + top.add(new JLabel("Score: ")); + top.add(score); + add(top,BorderLayout.NORTH); + add(inspector,BorderLayout.EAST); + add(map,BorderLayout.CENTER); + ObjectSelector selector = new ObjectSelector(); + map.addMouseListener(selector); + selector.addObjectSelectionListener(inspector); + } + + public Map getMap() { + return map; + } + + public void setTimestep(int t) { + timestep.setText(""+t); + } + + public void setScore(double d) { + score.setText(FORMAT.format(d)); + } +} diff --git a/modules/oldsims/rescuecore/view/HumanoidRenderer.java b/modules/oldsims/rescuecore/view/HumanoidRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..9ba4cbf781d2747b76b50ec83666428a6e43d5c2 --- /dev/null +++ b/modules/oldsims/rescuecore/view/HumanoidRenderer.java @@ -0,0 +1,102 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import rescuecore.objects.*; +import java.awt.*; +import java.awt.geom.*; + +public abstract class HumanoidRenderer implements MapRenderer { + private final static HumanoidRenderer ORDINARY = new OrdinaryHumanoidRenderer(); + + private final static int RADIUS = 5; + private final static int DIAMETER = RADIUS*2+1; + + public static HumanoidRenderer ordinaryHumanoidRenderer() { + return ORDINARY; + } + + public static HumanoidRenderer outlinedHumanoidRenderer(int outlineMode, Color outlineColour) { + return new OutlinedHumanoidRenderer(outlineMode,outlineColour); + } + + public static HumanoidRenderer coveredHumanoidRenderer(Color colour) { + return new CoveredHumanoidRenderer(colour); + } + + public boolean canRender(Object o) {return (o instanceof Humanoid);} + + private static class OrdinaryHumanoidRenderer extends HumanoidRenderer { + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Humanoid h = (Humanoid)o; + int[] xy = memory.getXY(h); + if (xy==null) return null; + int x = transform.toScreenX(xy[0]); + int y = transform.toScreenY(xy[1]); + Color colour = ViewConstants.CIVILIAN_COLOUR; + if (h.getType()==RescueConstants.TYPE_FIRE_BRIGADE) colour = ViewConstants.FIRE_BRIGADE_COLOUR; + if (h.getType()==RescueConstants.TYPE_AMBULANCE_TEAM) colour = ViewConstants.AMBULANCE_TEAM_COLOUR; + if (h.getType()==RescueConstants.TYPE_POLICE_FORCE) colour = ViewConstants.POLICE_FORCE_COLOUR; + if (h.getType()==RescueConstants.TYPE_CAR) colour = ViewConstants.CAR_COLOUR; + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,colour); + g.fillOval(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,Color.black); + g.drawOval(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + return new Ellipse2D.Double(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + } + } + + private static class OutlinedHumanoidRenderer extends HumanoidRenderer { + private int mode; + private Color colour; + + OutlinedHumanoidRenderer(int mode, Color colour) { + this.mode = mode; + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Humanoid h = (Humanoid)o; + int[] xy = memory.getXY(h); + if (xy==null) return null; + int x = transform.toScreenX(xy[0]); + int y = transform.toScreenY(xy[1]); + RenderTools.setLineMode(g,mode,colour); + g.drawOval(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + return new Ellipse2D.Double(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + } + } + + private static class CoveredHumanoidRenderer extends HumanoidRenderer { + private Color colour; + + CoveredHumanoidRenderer(Color colour) { + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException { + Humanoid h = (Humanoid)o; + int[] xy = memory.getXY(h); + if (xy==null) return null; + int x = transform.toScreenX(xy[0]); + int y = transform.toScreenY(xy[1]); + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,colour); + g.fillOval(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + return new Ellipse2D.Double(x-RADIUS,y-RADIUS,DIAMETER,DIAMETER); + } + } +} diff --git a/modules/oldsims/rescuecore/view/Layer.java b/modules/oldsims/rescuecore/view/Layer.java new file mode 100644 index 0000000000000000000000000000000000000000..a5f5c9a22ab35ce716cc51e28ebcfec5c67b9f69 --- /dev/null +++ b/modules/oldsims/rescuecore/view/Layer.java @@ -0,0 +1,390 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ $Revision: 1.9 $ Copyright (c) + * 2004, The Black Sheep, Department of Computer Science, The University of + * Auckland All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that the + * following conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. Neither the name of + * The Black Sheep, The Department of Computer Science or The University of + * Auckland nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package rescuecore.view; + +import rescuecore.objects.*; +import rescuecore.*; +import rescuecore.event.*; +import java.awt.image.BufferedImage; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Point; +import java.awt.geom.Rectangle2D; +import java.util.*; + +/** + * This class represents a layer of information to be displayed on a map of the + * world. Each layer is responsible for drawing some subset of the available + * objects in the world. Layers are responsible for requesting repaints as + * necessary. + */ +public class Layer { + + // private boolean dirty; + protected Collection<Object> objects; + private java.util.Map shapes; + // private BufferedImage image; + protected java.util.Map renderers; + protected String name; + protected boolean enabled; + // private double transparancy; + + + public static Layer createRoadLayer( Memory m ) { + final MemoryLayer result = new MemoryLayer( "Roads" ); + result.addType( RescueConstants.TYPE_ROAD ); + result.addRenderer( Road.class, RoadRenderer.ordinaryRoadRenderer() ); + result.memoryChanged( m ); + return result; + } + + + public static Layer createNodeLayer( Memory m ) { + final MemoryLayer result = new MemoryLayer( "Nodes" ); + result.addType( RescueConstants.TYPE_NODE ); + result.addRenderer( Node.class, NodeRenderer.ordinaryNodeRenderer() ); + result.memoryChanged( m ); + return result; + } + + + public static Layer createBuildingLayer( Memory m ) { + final MemoryLayer result = new MemoryLayer( "Buildings" ); + result.addType( RescueConstants.TYPE_BUILDING ); + result.addType( RescueConstants.TYPE_REFUGE ); + result.addType( RescueConstants.TYPE_FIRE_STATION ); + result.addType( RescueConstants.TYPE_POLICE_OFFICE ); + result.addType( RescueConstants.TYPE_AMBULANCE_CENTER ); + result.addRenderer( Building.class, + BuildingRenderer.ordinaryBuildingRenderer() ); + result.memoryChanged( m ); + return result; + } + + + public static Layer createHumanoidLayer( Memory m ) { + final MemoryLayer result = new MemoryLayer( "Humanoids" ); + result.addType( RescueConstants.TYPE_CIVILIAN ); + result.addType( RescueConstants.TYPE_FIRE_BRIGADE ); + result.addType( RescueConstants.TYPE_POLICE_FORCE ); + result.addType( RescueConstants.TYPE_AMBULANCE_TEAM ); + result.addRenderer( Humanoid.class, + HumanoidRenderer.ordinaryHumanoidRenderer() ); + result.memoryChanged( m ); + return result; + } + + + @Deprecated + public static Layer createLayer( Memory m, Object object, MapRenderer r, + String name ) { + return createLayer( object, r, name ); + } + + + public static Layer createLayer( Object object, MapRenderer r, String name ) { + final Layer result = new Layer( name ); + result.addRenderer( object.getClass(), r ); + result.setObject( object ); + return result; + } + + + @Deprecated + public static Layer createLayer( Memory m, final Object[] objects, + MapRenderer r, String name ) { + return createLayer( objects, r, name ); + } + + + public static Layer createLayer( final Object[] objects, MapRenderer r, + String name ) { + final Layer result = new Layer( name ); + for ( int i = 0; i < objects.length; ++i ) { + result.addRenderer( objects[i].getClass(), r ); + result.addObject( objects[i] ); + } + return result; + } + + + public static Layer createOverlayLayer( String name ) { + Layer result = new Layer( name ); + result.addRenderer( Shape.class, new ShapeRenderer() ); + return result; + } + + + @Deprecated + public static Layer createEmptyLayer( Memory m, String name ) { + return createEmptyLayer( name ); + } + + + @Deprecated + public static Layer createEmptyLayer( String name ) { + Layer result = new Layer( name ); + result.renderers = new HashMap(); + return result; + } + + + @Deprecated + public Layer( Memory m, String name ) { + this( name ); + } + + + public Layer( String name ) { + this.name = name; + // dirty = true; + // image = null; + objects = new HashSet<Object>(); + shapes = new HashMap(); + // memory = m; + renderers = new HashMap(); + // registerDefaultRenderers(); + enabled = true; + // transparancy = 0; + } + + + public String getName() { + return name; + } + + + public void setEnabled( boolean b ) { + enabled = b; + } + + + public boolean isEnabled() { + return enabled; + } + + /* + * public void setTransparancy(double d) { transparancy = d; } public double + * getTransparancy() { return transparancy; } + */ + + + public void memoryChanged( Memory m ) { + } + + + public void addObject( Object o ) { + objects.add( o ); + // dirty(); + } + + + public void addObjects( Object[] os ) { + for ( int i = 0; i < os.length; ++i ) + objects.add( os[i] ); + // dirty(); + } + + + public void addObjects( Collection os ) { + objects.addAll( os ); + // dirty(); + } + + + public void setObject( Object o ) { + removeAllObjects(); + addObject( o ); + } + + + public void setObjects( Object[] os ) { + removeAllObjects(); + addObjects( os ); + } + + + public void setObjects( Collection os ) { + removeAllObjects(); + addObjects( os ); + } + + + public void removeObject( Object o ) { + objects.remove( o ); + // dirty(); + } + + + public void removeObjects( Object[] os ) { + for ( int i = 0; i < os.length; ++i ) + objects.remove( os[i] ); + } + + + public void removeObjects( Collection os ) { + objects.removeAll( os ); + } + + + public void removeAllObjects() { + objects.clear(); + // dirty(); + } + + + public void addRenderer( Class clazz, MapRenderer renderer ) { + renderers.put( clazz, renderer ); + } + + + public void removeRenderer( Class clazz ) { + renderers.remove( clazz ); + } + + + public void paint( Graphics g, int width, int height, + ScreenTransform transform, Memory m ) { + // if (dirty || image==null) { + // image = null; + // if (image==null || image.getWidth()!=width || image.getHeight()!=height) + // image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); + + // paint(image.createGraphics(),transform); + // dirty = false; + // } + // g.drawImage(image,0,0,null); + paint( (Graphics2D) g, transform, m ); + } + + // public void dirty() { + // dirty = true; + // } + + + public Object[] getObjectsAtPoint( Point p ) { + Collection result = new ArrayList(); + // System.out.println("Getting objects at point "+p); + for ( Iterator it = objects.iterator(); it.hasNext(); ) { + Object next = it.next(); + Shape s = (Shape) shapes.get( next ); + // System.out.println("Next object: "+next+", shape: "+s); + if ( s != null && s.contains( p.x, p.y ) ) { + result.add( next ); + // System.out.println("Added"); + } + } + return result.toArray(); + } + + + public Object[] getObjectsInArea( Rectangle2D r ) { + Collection result = new ArrayList(); + for ( Iterator it = objects.iterator(); it.hasNext(); ) { + Object next = it.next(); + Shape s = (Shape) shapes.get( next ); + if ( s != null && s.intersects( r ) ) { + result.add( next ); + } + } + return result.toArray(); + } + + + private void paint( Graphics2D graphics, ScreenTransform transform, + Memory memory ) { + RenderTools.setLineMode( graphics, ViewConstants.LINE_MODE_SOLID, + Color.black, 1 ); + RenderTools.setFillMode( graphics, ViewConstants.FILL_MODE_SOLID, + Color.black ); + for ( Iterator it = objects.iterator(); it.hasNext(); ) { + Object next = it.next(); + if ( next == null ) continue; + Class clazz = next.getClass(); + MapRenderer renderer = getRenderer( clazz ); + // Copy the graphics context to protect it from irreversible changes + Graphics g = graphics.create(); + if ( renderer != null && renderer.canRender( next ) ) { + try { + shapes.put( next, renderer.render( next, memory, g, transform ) ); + } catch ( CannotFindLocationException e ) { + System.out.println( e ); + } + } + } + // System.out.println("Layer "+name+" finished painting"); + } + + + private MapRenderer getRenderer( Class clazz ) { + if ( clazz == null ) return null; + MapRenderer result = (MapRenderer) renderers.get( clazz ); + if ( result == null ) { + Class[] interfaces = clazz.getInterfaces(); + for ( int i = 0; i < interfaces.length; ++i ) { + result = getRenderer( interfaces[i] ); + if ( result != null ) return result; + } + return getRenderer( clazz.getSuperclass() ); + } + return result; + } + + + public void registerDefaultRenderers() { + renderers.put( Road.class, RoadRenderer.ordinaryRoadRenderer() ); + renderers.put( Node.class, NodeRenderer.ordinaryNodeRenderer() ); + renderers.put( Building.class, + BuildingRenderer.ordinaryBuildingRenderer() ); + renderers.put( Humanoid.class, + HumanoidRenderer.ordinaryHumanoidRenderer() ); + renderers.put( ConvexHull.class, ConvexHullRenderer.RED ); + renderers.put( Text.class, new TextRenderer() ); + } + + + private static class ShapeRenderer implements MapRenderer { + + public boolean canRender( Object o ) { + return o instanceof Shape; + } + + + public Shape render( Object o, Memory memory, Graphics g, + ScreenTransform transform ) throws CannotFindLocationException { + RenderTools.setLineMode( g, ViewConstants.LINE_MODE_DASH, Color.CYAN, 1 ); + RenderTools.setFillMode( g, ViewConstants.FILL_MODE_SOLID, + new Color( Color.CYAN.getRed(), Color.CYAN.getGreen(), + Color.CYAN.getBlue(), 128 ) ); + Shape s = (Shape) o; + ( (Graphics2D) g ).fill( s ); + return s; + } + } +} diff --git a/modules/oldsims/rescuecore/view/Line.java b/modules/oldsims/rescuecore/view/Line.java new file mode 100644 index 0000000000000000000000000000000000000000..b562ff133825c4cec25f4da163f0cb581c3e5b3b --- /dev/null +++ b/modules/oldsims/rescuecore/view/Line.java @@ -0,0 +1,40 @@ +/* + * Last change: $Date: 2004/06/15 04:36:46 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Created on 20/05/2004 + */ +package rescuecore.view; + +/** + * @author Cam + * + */ +public class Line { + private int x1, x2; + private int y1, y2; + + public Line(int x1, int y1, int x2, int y2){ + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + public int getX1(){return x1;} + public int getY1(){return y1;} + public int getX2(){return x2;} + public int getY2(){return y2;} +} diff --git a/modules/oldsims/rescuecore/view/LineRenderer.java b/modules/oldsims/rescuecore/view/LineRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..971bac2196273bc538f3dd0869ad467a88c1bb26 --- /dev/null +++ b/modules/oldsims/rescuecore/view/LineRenderer.java @@ -0,0 +1,65 @@ +/* + * Last change: $Date: 2004/06/15 04:36:46 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Created on 20/05/2004 + * + */ +/* + * Last change: $Date: 2004/06/15 04:36:46 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import java.awt.*; +import java.awt.geom.Line2D; + +/** + * @author Cam + * + */ + +public class LineRenderer implements MapRenderer { + private Color colour; + + public LineRenderer(Color colour) { + this.colour = colour; + } + + public boolean canRender(Object o) {return (o instanceof Line);} + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Line l = (Line)o; + int x1 = transform.toScreenX(l.getX1()); + int y1 = transform.toScreenY(l.getY1()); + int x2 = transform.toScreenX(l.getX2()); + int y2 = transform.toScreenY(l.getY2()); + g.setColor(colour); + g.drawLine(x1,y1,x2,y2); + return new Line2D.Double(x1,y1,x2,y2); + } +} diff --git a/modules/oldsims/rescuecore/view/Map.java b/modules/oldsims/rescuecore/view/Map.java new file mode 100644 index 0000000000000000000000000000000000000000..d0cd0f307394d12304003b224a231bdac592c397 --- /dev/null +++ b/modules/oldsims/rescuecore/view/Map.java @@ -0,0 +1,208 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.awt.geom.Rectangle2D; +import rescuecore.*; +import rescuecore.objects.*; + +/** + This class represents a view of the world. It is possible to draw outlines of objects in the world, convex hulls around groups of objects and to register custom renderers. + */ +public class Map extends JComponent { + private Memory memory; + private Dimension mapSize; + private int minX, maxX, minY, maxY, xRange, yRange; + private java.util.List layers; + // private Collection listeners; + // private MousePressInfo mouseInfo; + private JPopupMenu menu; + + public static Map defaultMap(Memory m) { + Map result = new Map(m); + result.addLayer(Layer.createRoadLayer(m)); + result.addLayer(Layer.createNodeLayer(m)); + result.addLayer(Layer.createBuildingLayer(m)); + result.addLayer(Layer.createHumanoidLayer(m)); + return result; + } + + public Map(Memory m) { + layers = new ArrayList(); + setDoubleBuffered(true); + setMemory(m); + // listeners = new HashSet(); + // mouseInfo = new MousePressInfo(); + menu = new JPopupMenu("Layers"); + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + Point p = e.getPoint(); + if (e.getButton()==MouseEvent.BUTTON3) { + menu.pack(); + menu.show(Map.this,p.x,p.y); + } + // else handleMousePress(p); + } + // public void mouseClicked(MouseEvent e) { + // handleMouseClick(e); + // } + }); + } + + /* + public void addObjectSelectionListener(ObjectSelectionListener l) { + synchronized(listeners) { + listeners.add(l); + } + } + + public void removeObjectSelectionListener(ObjectSelectionListener l) { + synchronized(listeners) { + listeners.remove(l); + } + } + */ + + public void addLayer(final Layer l) { + layers.add(l); + final AbstractAction action = new AbstractAction(l.isEnabled()?"Hide "+l.getName():"Show "+l.getName()) { + public void actionPerformed(ActionEvent e) { + l.setEnabled(!l.isEnabled()); + putValue(Action.SMALL_ICON,l.isEnabled()?javax.swing.plaf.metal.MetalIconFactory.getMenuItemCheckIcon():null); + putValue(Action.NAME,l.isEnabled()?"Hide "+l.getName():"Show "+l.getName()); + repaint(); + } + }; + menu.add(action); + repaint(); + } + + public void removeLayer(Layer l) { + int index = layers.indexOf(l); + layers.remove(l); + menu.remove(index); + repaint(); + } + + public void setMemory(Memory m) { + // System.out.println("Setting memory"); + memory = m; + minX = 0; + minY = 0; + maxX = 1; + maxY = 1; + boolean first = true; + int x = 0; + int y = 0; + Collection<RescueObject> all = memory.getAllObjects(); + for (RescueObject next : all) { + switch (next.getType()) { + case RescueConstants.TYPE_REFUGE: + case RescueConstants.TYPE_FIRE_STATION: + case RescueConstants.TYPE_AMBULANCE_CENTER: + case RescueConstants.TYPE_POLICE_OFFICE: + case RescueConstants.TYPE_BUILDING: + x = ((Building)next).getX(); + y = ((Building)next).getY(); + break; + case RescueConstants.TYPE_NODE: + x = ((Node)next).getX(); + y = ((Node)next).getY(); + break; + case RescueConstants.TYPE_RIVER_NODE: + x = ((RiverNode)next).getX(); + y = ((RiverNode)next).getY(); + break; + default: + continue; + } + if (first) { + minX = maxX = x; + minY = maxY = y; + first = false; + } + else { + minX = Math.min(minX,x); + maxX = Math.max(maxX,x); + minY = Math.min(minY,y); + maxY = Math.max(maxY,y); + } + } + maxX += 6000; + maxY += 6000; + minX -= 6000; + minY -= 6000; + xRange = maxX-minX; + yRange = maxY-minY; + // mapSize = new Dimension(xRange,yRange); + // Map size should be between 200 and 600 + mapSize = new Dimension(Math.max(200,Math.min(xRange,600)),Math.max(200,Math.min(yRange,600))); + for (Iterator it = layers.iterator();it.hasNext();) { + Layer next = (Layer)it.next(); + // System.out.println("Updating layer "+next.getName()); + next.memoryChanged(m); + } + } + + public Dimension getPreferredSize() { + return mapSize; + } + + public void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + int width = getSize().width; + int height = getSize().height; + RenderTools.setFillMode(graphics,ViewConstants.FILL_MODE_SOLID,ViewConstants.BACKGROUND_COLOUR); + graphics.fillRect(0,0,width,height); + Graphics2D g = (Graphics2D)graphics; + ScreenTransform transform = new ScreenTransform(minX,minY,xRange,yRange,width,height); + for (Iterator it = layers.iterator();it.hasNext();) { + Layer next = (Layer)it.next(); + if (next.isEnabled()) + next.paint(g,width,height,transform,memory); + } + } + + public Object[] getObjectsAtPoint(Point p) { + Collection all = new ArrayList(); + Layer[] allLayers = (Layer[])layers.toArray(new Layer[0]); + for (int i=allLayers.length-1;i>=0;--i) { + Layer next = allLayers[i]; + if (next.isEnabled()) { + Object[] o = next.getObjectsAtPoint(p); + for (int j=0;j<o.length;++j) all.add(o[j]); + } + } + return all.toArray(); + } + + public Object[] getObjectsInArea(Rectangle2D area) { + Collection all = new ArrayList(); + Layer[] allLayers = (Layer[])layers.toArray(new Layer[0]); + for (int i=allLayers.length-1;i>=0;--i) { + Layer next = allLayers[i]; + if (next.isEnabled()) { + Object[] o = next.getObjectsInArea(area); + for (int j=0;j<o.length;++j) all.add(o[j]); + } + } + return all.toArray(); + } +} diff --git a/modules/oldsims/rescuecore/view/MapRenderer.java b/modules/oldsims/rescuecore/view/MapRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..9795ec02a10b6133e8467c39c9d33843bb65420c --- /dev/null +++ b/modules/oldsims/rescuecore/view/MapRenderer.java @@ -0,0 +1,41 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.Graphics; +import java.awt.Shape; +import rescuecore.Memory; +import rescuecore.CannotFindLocationException; + +public interface MapRenderer { + /** + Return true if and only if this MapRenderer can render a particular object + @param o The Object we want to render + @return true iff this MapRenderer can render the given object + */ + public boolean canRender(Object o); + + /** + Render an object + @param o The object to render + @param memory The Memory containing our view of the world + @param g A Graphics object to render on + @param transform The ScreenTransform that should be used to transform world coordinates into screen coordinates + @return The bounding Shape of the rendered object, in screen coordinates + @throws CannotFindLocationException if the location of a RescueObject cannot be determined + */ + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) throws CannotFindLocationException; +} diff --git a/modules/oldsims/rescuecore/view/MemoryLayer.java b/modules/oldsims/rescuecore/view/MemoryLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..697c464fea103e3258ac3c67ce96eb08dbe51a29 --- /dev/null +++ b/modules/oldsims/rescuecore/view/MemoryLayer.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.objects.*; +import rescuecore.*; +import java.util.*; + +public class MemoryLayer extends Layer { + private Set<Integer> types; + + public MemoryLayer(String name) { + super(name); + types = new HashSet<Integer>(); + } + + public void addType(int type) { + types.add(type); + } + + public void memoryChanged(Memory m) { + super.memoryChanged(m); + objects.clear(); + for (Iterator<Integer> it = types.iterator();it.hasNext();) { + int next = it.next(); + Collection<RescueObject> os = m.getObjectsOfType(next); + objects.addAll(os); + } + } +} + diff --git a/modules/oldsims/rescuecore/view/NodeRenderer.java b/modules/oldsims/rescuecore/view/NodeRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..c8c4c31e1894a8980d981ea0bb42763f2ebedd9c --- /dev/null +++ b/modules/oldsims/rescuecore/view/NodeRenderer.java @@ -0,0 +1,62 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import rescuecore.objects.*; +import java.awt.*; + +public class NodeRenderer implements MapRenderer { + private final static NodeRenderer ORDINARY = new NodeRenderer(); + + public static NodeRenderer ordinaryNodeRenderer() { + return ORDINARY; + } + + public static NodeRenderer outlinedNodeRenderer(int mode, Color colour) { + return new OutlinedNodeRenderer(mode,colour); + } + + protected NodeRenderer() {} + + public boolean canRender(Object o) {return (o instanceof Node);} + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + int x = transform.toScreenX(((Node)o).getX()); + int y = transform.toScreenY(((Node)o).getY()); + RenderTools.setFillMode(g,ViewConstants.FILL_MODE_SOLID,Color.black); + g.fillRect(x-1,y-1,3,3); + return new Rectangle(x-1,y-1,3,3); + } + + private static class OutlinedNodeRenderer extends NodeRenderer { + private int mode; + private Color colour; + + public OutlinedNodeRenderer(int mode, Color colour) { + this.mode = mode; + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + int x = transform.toScreenX(((Node)o).getX()); + int y = transform.toScreenY(((Node)o).getY()); + RenderTools.setLineMode(g,mode,colour); + g.drawRect(x-1,y-1,3,3); + return new Rectangle(x-1,y-1,3,3); + } + } +} diff --git a/modules/oldsims/rescuecore/view/ObjectInspector.java b/modules/oldsims/rescuecore/view/ObjectInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..038f48c85b75acf6704a3c808666e6e82eeb327c --- /dev/null +++ b/modules/oldsims/rescuecore/view/ObjectInspector.java @@ -0,0 +1,128 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.*; +import javax.swing.*; +import javax.swing.table.*; +import rescuecore.*; +import rescuecore.event.*; + +public class ObjectInspector extends JPanel implements ObjectSelectionListener, PropertyListener { + private PropertiesTableModel model; + private RescueObject view; + private int[] properties; + private boolean showUpdateData; + + private final static int COLUMN_PROPERTY = 0; + private final static int COLUMN_VALUE = 1; + private final static int COLUMN_LAST_UPDATE = 2; + private final static int COLUMN_UPDATE_SOURCE = 3; + private final static int NUM_COLUMNS = 4; + private final static int NUM_BASIC_COLUMNS = 2; + + public ObjectInspector() { + this(true); + } + + public ObjectInspector(boolean showUpdates) { + super(new BorderLayout()); + model = new PropertiesTableModel(); + add(new JScrollPane(new JTable(model)),BorderLayout.CENTER); + showUpdateData = showUpdates; + } + + public void objectSelected(ObjectSelectionEvent e) { + if (e.getSelectedObject()==null) return; + if (e.getSelectedObject() instanceof RescueObject) showObject((RescueObject)e.getSelectedObject()); + } + + public void propertyChanged(PropertyChangedEvent event) { + model.refresh(); + } + + public void showObject(RescueObject o) { + if (view!=null) view.removePropertyListener(this); + view = o; + properties = view==null?null:view.getKnownPropertyTypes(); + model.refresh(); + if (view!=null) view.addPropertyListener(this); + } + + private class PropertiesTableModel extends AbstractTableModel { + public void refresh() { + fireTableDataChanged(); + } + + public int getRowCount() { + return view==null?0:properties.length+2; + } + + public int getColumnCount() { + return showUpdateData?NUM_COLUMNS:NUM_BASIC_COLUMNS; + } + + public String getColumnName(int col) { + switch(col) { + case COLUMN_PROPERTY: + return "Property"; + case COLUMN_VALUE: + return "Value"; + case COLUMN_LAST_UPDATE: + return "Last update"; + case COLUMN_UPDATE_SOURCE: + return "Update source"; + default: + throw new IllegalArgumentException("Unknown column: "+col); + } + } + + public Class getColumnClass(int col) { + return String.class; + } + + public Object getValueAt(int row, int col) { + Property p; + switch (col) { + case COLUMN_PROPERTY: + if (row==0) return "ID"; + if (row==1) return "Type"; + return Handy.getPropertyName(properties[row-2]); + case COLUMN_VALUE: + if (row==0) return ""+view.getID(); + if (row==1) return Handy.getTypeName(view.getType()); + p = view.getProperty(properties[row-2]); + if (p==null) return "<unknown>"; + return p.getStringValue(); + case COLUMN_LAST_UPDATE: + if (row==0) return ""; + if (row==1) return ""; + p = view.getProperty(properties[row-2]); + if (p==null) return "<unknown>"; + return ""+p.getLastUpdate(); + case COLUMN_UPDATE_SOURCE: + if (row==0) return ""; + if (row==1) return ""; + p = view.getProperty(properties[row-2]); + if (p==null) return "<unknown>"; + Object result = p.getLastUpdateSource(); + return result==null?"null":result.toString(); + default: + throw new IllegalArgumentException("Unknown column: "+col); + } + } + } +} diff --git a/modules/oldsims/rescuecore/view/ObjectSelectionEvent.java b/modules/oldsims/rescuecore/view/ObjectSelectionEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..aefaed5a7d1544d428a9b0f5583ae8bdb9f5ba37 --- /dev/null +++ b/modules/oldsims/rescuecore/view/ObjectSelectionEvent.java @@ -0,0 +1,29 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +public class ObjectSelectionEvent extends java.util.EventObject { + private Object object; + + public ObjectSelectionEvent(Object source, Object object) { + super(source); + this.object = object; + } + + public Object getSelectedObject() { + return object; + } +} diff --git a/modules/oldsims/rescuecore/view/ObjectSelectionListener.java b/modules/oldsims/rescuecore/view/ObjectSelectionListener.java new file mode 100644 index 0000000000000000000000000000000000000000..2850d45978e6aa417cdfa824618e28c5932aeb31 --- /dev/null +++ b/modules/oldsims/rescuecore/view/ObjectSelectionListener.java @@ -0,0 +1,20 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +public interface ObjectSelectionListener extends java.util.EventListener { + public void objectSelected(ObjectSelectionEvent e); +} diff --git a/modules/oldsims/rescuecore/view/ObjectSelector.java b/modules/oldsims/rescuecore/view/ObjectSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..fd27b81b7d2e4015fce5a0d8464d30004df19302 --- /dev/null +++ b/modules/oldsims/rescuecore/view/ObjectSelector.java @@ -0,0 +1,144 @@ +/* + * Last change: $Date: 2004/07/11 22:26:28 $ + * $Revision: 1.6 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.Point; +import java.awt.event.*; +import java.util.*; + +public class ObjectSelector implements MouseListener { + private Collection<ObjectSelectionListener> listeners; + private MousePressInfo mouseInfo; + private Map map; + + public ObjectSelector() { + listeners = new ArrayList<ObjectSelectionListener>(); + mouseInfo = new MousePressInfo(); + } + + public ObjectSelector(Map map) { + this(); + setMap(map); + } + + public void setMap(Map newMap) { + if (map!=null) map.removeMouseListener(this); + if (newMap!=null && newMap!=map) newMap.addMouseListener(this); + map = newMap; + } + + public void addObjectSelectionListener(ObjectSelectionListener l) { + synchronized(listeners) { + listeners.add(l); + } + } + + public void removeObjectSelectionListener(ObjectSelectionListener l) { + synchronized(listeners) { + listeners.remove(l); + } + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mouseClicked(MouseEvent e) { + /* + if (e.getClickCount()>=2) { + Point p = e.getPoint(); + Object[] objects = map.toArray(); + // Choose which object to view + JPopupMenu objectMenu = new JPopupMenu("Choose an object"); + for (int i=0;i<objects.length;++i) { + if (objects[i] instanceof RescueObject) { + final RescueObject ro = (RescueObject)objects[i]; + final AbstractAction action = new AbstractAction(ro.toString()) { + public void actionPerformed(ActionEvent e) { + JFrame frame = new JFrame(); + ObjectInspector content = new ObjectInspector(); + content.showObject(ro); + frame.setContentPane(content); + frame.pack(); + frame.setVisible(true); + } + }; + objectMenu.add(action); + } + } + objectMenu.pack(); + objectMenu.show(map,p.x,p.y); + } + */ + } + + public void mousePressed(MouseEvent e) { + if (map==null) return; + Point p = e.getPoint(); + // Is it close to the last mouse press? + if (mouseInfo.closeTo(p) && mouseInfo.objects!=null && mouseInfo.objects.length>0) { + fireObjectSelected(mouseInfo.advancePointer()); + } + else { + Object[] objects = map.getObjectsAtPoint(p); + mouseInfo.newPoint(p,objects); + if (objects.length>0) fireObjectSelected(objects[0]); + else fireObjectSelected(null); + } + } + + private void fireObjectSelected(Object o) { + ObjectSelectionEvent e = new ObjectSelectionEvent(map,o); + ObjectSelectionListener[] l; + synchronized(listeners) { + l = (ObjectSelectionListener[])listeners.toArray(new ObjectSelectionListener[0]); + } + for (int i=0;i<l.length;++i) { + l[i].objectSelected(e); + } + } + + private class MousePressInfo { + Point p; + Object[] objects; + int index; + + public boolean closeTo(Point point) { + if (p==null || point==null) return false; + if (Math.abs(p.x-point.x)<2 && Math.abs(p.y-point.y)<2) return true; + return false; + } + + public Object advancePointer() { + ++index; + if (index>=objects.length) index = 0; + System.out.println("Selected object "+index+" of "+objects.length); + return objects[index]; + } + + public Object newPoint(Point p, Object[] objects) { + this.p = p; + this.objects = objects; + index = 0; + return objects.length>0?objects[0]:null; + } + } +} diff --git a/modules/oldsims/rescuecore/view/RenderTools.java b/modules/oldsims/rescuecore/view/RenderTools.java new file mode 100644 index 0000000000000000000000000000000000000000..802abf2a40b0ccf510398a1390832c287d6a193d --- /dev/null +++ b/modules/oldsims/rescuecore/view/RenderTools.java @@ -0,0 +1,117 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.4 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.*; +import java.awt.image.*; + +public class RenderTools { + private final static int HATCH_SIZE = 9; + + private final static Stroke SOLID_STROKE = new BasicStroke(1); + private final static float[] DOT_PATTERN = {0.1f,1f}; + private final static float[] DASH_PATTERN = {1f,1f}; + private final static float[] DOT_DASH_PATTERN = {0.1f,1f,1f,1f}; + private final static float[][] DASH_PATTERNS = {null,DOT_PATTERN,DASH_PATTERN,DOT_DASH_PATTERN}; + + private final static BufferedImage HORIZONTAL = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static BufferedImage VERTICAL = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static BufferedImage DIAGONAL = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static BufferedImage REVERSE_DIAGONAL = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static BufferedImage HATCH = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static BufferedImage CROSS_HATCH = new BufferedImage(HATCH_SIZE,HATCH_SIZE,BufferedImage.TYPE_INT_ARGB); + private final static Rectangle ANCHOR = new Rectangle(0,0,HATCH_SIZE,HATCH_SIZE); + + private final static int TRANSPARENT_BLACK = new Color(0,0,0,0).getRGB(); + + static { + for (int x=0;x<HATCH_SIZE;++x) { + for (int y=0;y<HATCH_SIZE;++y) { + HORIZONTAL.setRGB(x,y,TRANSPARENT_BLACK); + VERTICAL.setRGB(x,y,TRANSPARENT_BLACK); + DIAGONAL.setRGB(x,y,TRANSPARENT_BLACK); + REVERSE_DIAGONAL.setRGB(x,y,TRANSPARENT_BLACK); + HATCH.setRGB(x,y,TRANSPARENT_BLACK); + CROSS_HATCH.setRGB(x,y,TRANSPARENT_BLACK); + } + } + } + + private RenderTools() {} + + public static void setLineMode(Graphics g, int mode, Color colour, int width) { + Stroke stroke; + if (mode==ViewConstants.LINE_MODE_SOLID) { + if (width==1) stroke = SOLID_STROKE; + else stroke = new BasicStroke(width); + } + else { + stroke = new BasicStroke(width,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,1f,DASH_PATTERNS[mode],0); + } + ((Graphics2D)g).setStroke(stroke); + if (colour!=null) + g.setColor(colour); + } + + public static void setLineMode(Graphics g, int mode, Color colour) { + setLineMode(g,mode,colour,1); + } + + public static void setLineMode(Graphics g, int mode) { + setLineMode(g,mode,null,1); + } + + public static void setLineMode(Graphics g, int mode, int width) { + setLineMode(g,mode,null,width); + } + + public static void setFillMode(Graphics g, int mode, Color colour) { + int c = colour.getRGB(); + for (int x=0;x<HATCH_SIZE;++x) { + HORIZONTAL.setRGB(x,HATCH_SIZE/2,c); + VERTICAL.setRGB(HATCH_SIZE/2,x,c); + DIAGONAL.setRGB(x,x,c); + REVERSE_DIAGONAL.setRGB(HATCH_SIZE-x-1,x,c); + HATCH.setRGB(x,HATCH_SIZE/2,c); + HATCH.setRGB(HATCH_SIZE/2,x,c); + CROSS_HATCH.setRGB(x,x,c); + CROSS_HATCH.setRGB(HATCH_SIZE-x-1,x,c); + } + switch (mode) { + case ViewConstants.FILL_MODE_SOLID: + ((Graphics2D)g).setPaint(colour); + break; + case ViewConstants.FILL_MODE_HORIZONTAL_LINES: + ((Graphics2D)g).setPaint(new TexturePaint(HORIZONTAL,ANCHOR)); + break; + case ViewConstants.FILL_MODE_VERTICAL_LINES: + ((Graphics2D)g).setPaint(new TexturePaint(VERTICAL,ANCHOR)); + break; + case ViewConstants.FILL_MODE_DIAGONAL_LINES: + ((Graphics2D)g).setPaint(new TexturePaint(DIAGONAL,ANCHOR)); + break; + case ViewConstants.FILL_MODE_REVERSE_DIAGONAL_LINES: + ((Graphics2D)g).setPaint(new TexturePaint(REVERSE_DIAGONAL,ANCHOR)); + break; + case ViewConstants.FILL_MODE_HATCH: + ((Graphics2D)g).setPaint(new TexturePaint(HATCH,ANCHOR)); + break; + case ViewConstants.FILL_MODE_CROSS_HATCH: + ((Graphics2D)g).setPaint(new TexturePaint(CROSS_HATCH,ANCHOR)); + break; + } + } +} diff --git a/modules/oldsims/rescuecore/view/RoadRenderer.java b/modules/oldsims/rescuecore/view/RoadRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..ab5adc10b543dda7b278f790bc75711a97a2685b --- /dev/null +++ b/modules/oldsims/rescuecore/view/RoadRenderer.java @@ -0,0 +1,95 @@ +/* + * Last change: $Date: 2005/02/18 03:34:34 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import rescuecore.objects.*; +import java.awt.*; +import java.awt.geom.*; + +public class RoadRenderer implements MapRenderer { + private final static RoadRenderer ORDINARY = new RoadRenderer(); + + public static RoadRenderer ordinaryRoadRenderer() { + return ORDINARY; + } + + public static RoadRenderer outlinedRoadRenderer(int mode, Color colour) { + return new OutlinedRoadRenderer(mode,colour); + } + + protected RoadRenderer() {} + + public boolean canRender(Object o) {return (o instanceof Road);} + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Road road = (Road)o; + Node roadHead = (Node)memory.lookup(road.getHead()); + Node roadTail = (Node)memory.lookup(road.getTail()); + int headX = transform.toScreenX(roadHead.getX()); + int headY = transform.toScreenY(roadHead.getY()); + int tailX = transform.toScreenX(roadTail.getX()); + int tailY = transform.toScreenY(roadTail.getY()); + int blockedLines = 0; //road.getBlockedLines(); FIXME + int total = road.getLinesToHead()+road.getLinesToTail(); + int free = total-blockedLines; + boolean isBlocked = road.getBlock()>0; + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,Color.black,(road.getLinesToHead()+road.getLinesToTail())*3); + g.drawLine(headX,headY,tailX,tailY); + if (isBlocked) { + Color blockColour = Color.white; + if (blockedLines>0) blockColour = Color.gray; + if (free==0) blockColour = Color.red; + RenderTools.setLineMode(g,ViewConstants.LINE_MODE_SOLID,blockColour,2); + // Draw a cross in the middle of the road + int centerX = (headX+tailX)/2; + int centerY = (headY+tailY)/2; + g.drawLine(centerX-3,centerY-3,centerX+3,centerY+3); + g.drawLine(centerX-3,centerY+3,centerX+3,centerY-3); + } + Shape shape = new java.awt.geom.Line2D.Double(headX,headY,tailX,tailY); + shape = new BasicStroke((road.getLinesToTail()+road.getLinesToHead())*3).createStrokedShape(shape); + return shape; + } + + private static class OutlinedRoadRenderer extends RoadRenderer { + private int mode; + private Color colour; + + public OutlinedRoadRenderer(int mode, Color colour) { + this.mode = mode; + this.colour = colour; + } + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + Road road = (Road)o; + Node roadHead = (Node)memory.lookup(road.getHead()); + Node roadTail = (Node)memory.lookup(road.getTail()); + int headX = transform.toScreenX(roadHead.getX()); + int headY = transform.toScreenY(roadHead.getY()); + int tailX = transform.toScreenX(roadTail.getX()); + int tailY = transform.toScreenY(roadTail.getY()); + int blocked = 0; //road.getBlockedLines(); FIXME + int total = road.getLinesToHead()+road.getLinesToTail(); + int free = total-blocked; + Shape shape = new java.awt.geom.Line2D.Double(headX,headY,tailX,tailY); + shape = new BasicStroke(road.getLinesToTail()+road.getLinesToHead()).createStrokedShape(shape); + RenderTools.setLineMode(g,mode,colour); + ((Graphics2D)g).draw(shape); + return shape; + } + } +} diff --git a/modules/oldsims/rescuecore/view/ScreenTransform.java b/modules/oldsims/rescuecore/view/ScreenTransform.java new file mode 100644 index 0000000000000000000000000000000000000000..97fb56195a77d0c52b9bed9e1114c34dc167e9ac --- /dev/null +++ b/modules/oldsims/rescuecore/view/ScreenTransform.java @@ -0,0 +1,42 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.3 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +public class ScreenTransform { + private int minX, xRange; + private int minY, yRange; + private int width, height; + + public ScreenTransform(int minX, int minY, int xRange, int yRange, int width, int height) { + this.minX = minX; + this.minY = minY; + this.xRange = xRange; + this.yRange = yRange; + this.width = width; + this.height = height; + } + + public int toScreenX(int rescueX) { + double xPos = ((double)(rescueX-minX))/((double)xRange); + return (int)(xPos*width); + } + + public int toScreenY(int rescueY) { + double yPos = ((double)(rescueY-minY))/((double)yRange); + double result = yPos*height; + return (int)(height - result); + } +} diff --git a/modules/oldsims/rescuecore/view/TestMap.java b/modules/oldsims/rescuecore/view/TestMap.java new file mode 100644 index 0000000000000000000000000000000000000000..36348f73f83e1f9d2c8f7d16b75797dfb773bd45 --- /dev/null +++ b/modules/oldsims/rescuecore/view/TestMap.java @@ -0,0 +1,45 @@ +/* + * Last change: $Date: 2004/05/04 03:09:39 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import rescuecore.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.io.*; + +public class TestMap { + public static void main(String[] args) { + try { + ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(args[0]))); + Memory m = (Memory)in.readObject(); + in.close(); + Map map = new Map(m); + + JFrame frame = new JFrame("Map"); + frame.getContentPane().add(new Display(map)); + frame.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) { + System.exit(0); + }}); + frame.pack(); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + frame.setVisible(true); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/modules/oldsims/rescuecore/view/Text.java b/modules/oldsims/rescuecore/view/Text.java new file mode 100644 index 0000000000000000000000000000000000000000..28dc3575985bdac87f7e0fa1b080d1f4a104db0a --- /dev/null +++ b/modules/oldsims/rescuecore/view/Text.java @@ -0,0 +1,39 @@ +/* + * Last change: $Date: 2004/05/31 01:53:48 $ + * $Revision: 1.1 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Created on 20/05/2004 + */ +package rescuecore.view; + +/** + * @author Jono + * + */ +public class Text { + private String string; + private int x; + private int y; + + public Text(String str, int x, int y){ + this.x = x; + this.y = y; + string = str; + } + + public String getString(){return string;} + public int getX(){return x;} + public int getY(){return y;} +} diff --git a/modules/oldsims/rescuecore/view/TextRenderer.java b/modules/oldsims/rescuecore/view/TextRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..e151fcf9d80e70c9ec9a1d91038518ba92033471 --- /dev/null +++ b/modules/oldsims/rescuecore/view/TextRenderer.java @@ -0,0 +1,52 @@ +/* + * Last change: $Date: 2004/06/10 01:17:51 $ + * $Revision: 1.2 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Created on 20/05/2004 + * + */ +package rescuecore.view; + +import rescuecore.*; +import java.awt.*; +/** + * @author Jono + * + */ + +public class TextRenderer implements MapRenderer { + private Font f = new Font("Times",Font.BOLD,12); + + public void setFont(Font f){ + this.f = f; + } + + public boolean canRender(Object o) {return (o instanceof Text);} + + public Shape render(Object o, Memory memory, Graphics g, ScreenTransform transform) { + FontMetrics fm = g.getFontMetrics(f); + Text t = (Text)o; + String s = t.getString(); + int x = transform.toScreenX(t.getX()); + int y = transform.toScreenY(t.getY()); + int width = fm.stringWidth(s); + int height = fm.getHeight(); + x -= width/2; + y += height/2; + g.setColor(Color.WHITE); + g.drawString(s,x,y); + return new Rectangle(x,y,width,height); + } +} diff --git a/modules/oldsims/rescuecore/view/ViewConstants.java b/modules/oldsims/rescuecore/view/ViewConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..0332eacae371b27e224d04bb96c0dd9adacac03a --- /dev/null +++ b/modules/oldsims/rescuecore/view/ViewConstants.java @@ -0,0 +1,69 @@ +/* + * Last change: $Date: 2004/06/07 22:08:08 $ + * $Revision: 1.5 $ + * + * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package rescuecore.view; + +import java.awt.Color; + +public final class ViewConstants { + private ViewConstants() {} + + public final static int LINE_MODE_SOLID = 0; + public final static int LINE_MODE_DOT = 1; + public final static int LINE_MODE_DASH = 2; + public final static int LINE_MODE_DOT_DASH = 3; + + public final static int FILL_MODE_SOLID = 0; + public final static int FILL_MODE_HORIZONTAL_LINES = 1; + public final static int FILL_MODE_VERTICAL_LINES = 2; + public final static int FILL_MODE_DIAGONAL_LINES = 3; + public final static int FILL_MODE_REVERSE_DIAGONAL_LINES = 4; + public final static int FILL_MODE_HATCH = 5; + public final static int FILL_MODE_CROSS_HATCH = 6; + + public final static Color NO_COLOUR = new Color(0,0,0,255); + public final static Color BACKGROUND_COLOUR = new Color(96,96,96); + + public final static Color AGENT_COLOUR = Color.blue; + public final static Color TARGET_COLOUR = Color.red; + public final static Color PRIMARY_TARGETS_COLOUR = Color.white; + public final static Color SECONDARY_TARGETS_COLOUR = Color.yellow; + public final static Color TERTIARY_TARGETS_COLOUR = Color.orange; + public final static Color BAD_TARGETS_COLOUR = Color.pink; + public final static Color PATH_COLOUR = Color.white; + + public final static Color FIRE_STATION_COLOUR = Color.yellow; + public final static Color POLICE_OFFICE_COLOUR = Color.blue; + public final static Color AMBULANCE_CENTER_COLOUR = Color.white; + public final static Color REFUGE_COLOUR = Color.pink; + public final static Color BUILDING_COLOUR = new Color(128,128,128); + + public final static Color HEATING_COLOUR = new Color(255,128,0); + public final static Color FIRE_COLOUR = Color.red; + public final static Color INFERNO_COLOUR = Color.red.darker(); + public final static Color BURNT_OUT_COLOUR = Color.black; + public final static Color WATER_DAMAGE_COLOUR = new Color(48,48,216); + public final static Color EXTINGUISHED_COLOUR = new Color(96,96,216); + + public final static Color CIVILIAN_COLOUR = Color.green; + public final static Color FIRE_BRIGADE_COLOUR = Color.red; + public final static Color POLICE_FORCE_COLOUR = Color.blue; + public final static Color AMBULANCE_TEAM_COLOUR = Color.white; + public final static Color CAR_COLOUR = Color.pink; + + public final static Color UNBLOCKED_COLOUR = Color.black; + public final static Color PARTIALLY_BLOCKED_COLOUR = Color.lightGray; + public final static Color TOTALLY_BLOCKED_COLOUR = Color.white; +} diff --git a/modules/rescuecore2/resources/rescuecore2/view/cross.png b/modules/rescuecore2/resources/rescuecore2/view/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..1a7e0e819ae1854c5150073a06820ef4f92fff84 Binary files /dev/null and b/modules/rescuecore2/resources/rescuecore2/view/cross.png differ diff --git a/modules/rescuecore2/resources/rescuecore2/view/tick.png b/modules/rescuecore2/resources/rescuecore2/view/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..5f68bf2fd80c4d7d24939e1932681254bb26ebba Binary files /dev/null and b/modules/rescuecore2/resources/rescuecore2/view/tick.png differ diff --git a/modules/rescuecore2/src/rescuecore2/Constants.java b/modules/rescuecore2/src/rescuecore2/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..ebe9f9abb607f5b0d7bfad7df22420c67b366089 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/Constants.java @@ -0,0 +1,62 @@ +package rescuecore2; + +/** + * Some useful constants that are shared across all parts of the Robocup Rescue + * software. Note that this does NOT include constants for entity/message types. + */ +public final class Constants { + /** prefix for control message URN */ + public static final int CONTROL_MSG_URN_PREFIX = 0x0100; + public static final int CONTROL_MSG_COMPONENT_URN_PREFIX = 0x0200; + + /** Config key for message factories. */ + public static final String FACTORY_KEY = "factory"; + /** Config key for looking up jars for inspection by a LoadableTypeProcessor. */ + public static final String JAR_DIR_KEY = "loadabletypes.inspect.dir"; + /** + * Config key for specifying whether to do a deep inspection of jars for + * loadable types. + */ + public static final String DEEP_JAR_INSPECTION_KEY = "loadabletypes.inspect.deep"; + /** Default location for looking up jar files. */ + public static final String DEFAULT_JAR_DIR = "../jars"; + /** Default deep inspection. */ + public static final boolean DEFAULT_DEEP_JAR_INSPECTION = true; + /** + * Config key for specifying jar names to ignore when finding loadable types. + */ + public static final String IGNORE_JARS_KEY = "loadabletypes.ignore"; + /** Default list of jar names to ignore when finding loadable types. */ + public static final String DEFAULT_IGNORE_JARS = "rescuecore2.jar"; + + /** Config key for the kernel host name. */ + public static final String KERNEL_HOST_NAME_KEY = "kernel.host"; + /** Default kernel host name. */ + public static final String DEFAULT_KERNEL_HOST_NAME = "localhost"; + /** Config key for the kernel port number. */ + public static final String KERNEL_PORT_NUMBER_KEY = "kernel.port"; + /** Default kernel port number. */ + public static final int DEFAULT_KERNEL_PORT_NUMBER = 27931; + /** Config key for the gis port number. */ + public static final String GIS_PORT_NUMBER_KEY = "gis.port"; + /** Default gis port number. */ + public static final int DEFAULT_GIS_PORT_NUMBER = 27932; + + /** The random seed key. */ + public static final String RANDOM_SEED_KEY = "random.seed"; + /** The random implementation class key. */ + public static final String RANDOM_CLASS_KEY = "random.class"; + /** The default random implementation class. */ + public static final String RANDOM_CLASS_DEFAULT = "org.uncommons.maths.random.MersenneTwisterRNG"; + + /** The name of the communication model class. */ + public static final String COMMUNICATION_MODEL_KEY = "kernel.communication-model"; + /** The name of the perception class. */ + public static final String PERCEPTION_KEY = "kernel.perception"; + + /** Config key for the top-level score function. */ + public static final String SCORE_FUNCTION_KEY = "score.function"; + + private Constants() { + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/GUIComponent.java b/modules/rescuecore2/src/rescuecore2/GUIComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..e3dc79f876052078b7952c2cdc23d0a62e030807 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/GUIComponent.java @@ -0,0 +1,20 @@ +package rescuecore2; + +import javax.swing.JComponent; + +/** + Tagging interface for objects that have a GUI component. + */ +public interface GUIComponent { + /** + Get a JComponent that should be added to the GUI. + @return A JComponent. + */ + JComponent getGUIComponent(); + + /** + Get the name of this part of the GUI. This will be used in things like tabbed panes and borders around GUI components. + @return The name of this GUI component. + */ + String getGUIComponentName(); +} diff --git a/modules/rescuecore2/src/rescuecore2/LaunchComponents.java b/modules/rescuecore2/src/rescuecore2/LaunchComponents.java new file mode 100644 index 0000000000000000000000000000000000000000..94c93b7cab347a077fa9952785fb011687073beb --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/LaunchComponents.java @@ -0,0 +1,125 @@ +package rescuecore2; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.io.IOException; +import javax.swing.JFrame; +import java.util.List; +import java.util.ArrayList; + +import rescuecore2.components.Component; +import rescuecore2.components.ComponentLauncher; +import rescuecore2.components.TCPComponentLauncher; +import rescuecore2.components.ComponentConnectionException; +import rescuecore2.components.ComponentInitialisationException; +import rescuecore2.connection.ConnectionException; +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.registry.Registry; +import rescuecore2.log.Logger; + +/** + General launcher for components. + */ +public final class LaunchComponents { + private static final String NO_GUI_FLAG = "--nogui"; + + private LaunchComponents() {} + + /** + Launch 'em! + @param args The arguments should be thus: [-p <port>]? [-h <hostname>]? [-c <config file>]* (fully.qualified.classname[*multiplier])+ + */ + public static void main(String[] args) { + Logger.setLogContext("launcher"); + Config config = new Config(); + boolean gui = true; + try { + args = CommandLineOptions.processArgs(args, config); + List<String> toLaunch = new ArrayList<String>(); + for (String next : args) { + if (NO_GUI_FLAG.equals(next)) { + gui = false; + } + else { + toLaunch.add(next); + } + } + int port = config.getIntValue(Constants.KERNEL_PORT_NUMBER_KEY, Constants.DEFAULT_KERNEL_PORT_NUMBER); + String host = config.getValue(Constants.KERNEL_HOST_NAME_KEY, Constants.DEFAULT_KERNEL_HOST_NAME); + processJarFiles(config); + ComponentLauncher launcher = new TCPComponentLauncher(host, port, config); + for (String next : toLaunch) { + connect(launcher, next, gui); + } + } + catch (IOException e) { + Logger.error("Error connecting components", e); + } + catch (ConfigException e) { + Logger.error("Configuration error", e); + } + catch (ConnectionException e) { + Logger.error("Error connecting components", e); + } + catch (InterruptedException e) { + Logger.error("Error connecting components", e); + } + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.process(); + } + + private static void connect(ComponentLauncher launcher, String argLine, boolean gui) throws InterruptedException, ConnectionException { + // Check if this class name has a multiplier + int index = argLine.indexOf("*"); + int count = 1; + String className = argLine; + if (index != -1) { + String mult = argLine.substring(index + 1); + if ("n".equals(mult)) { + count = Integer.MAX_VALUE; + } + else { + count = Integer.parseInt(mult); + } + className = argLine.substring(0, index); + } + Logger.info("Launching " + (count == Integer.MAX_VALUE ? "many" : count) + " instances of component '" + className + "'..."); + for (int i = 0; i < count; ++i) { + Component c = instantiate(className, Component.class); + if (c == null) { + break; + } + Logger.info("Launching instance " + (i + 1) + "..."); + try { + c.initialise(); + launcher.connect(c); + if (gui && c instanceof GUIComponent) { + GUIComponent g = (GUIComponent)c; + JFrame frame = new JFrame(g.getGUIComponentName()); + frame.setContentPane(g.getGUIComponent()); + frame.pack(); + frame.setVisible(true); + } + Logger.info("success"); + } + catch (ComponentConnectionException e) { + Logger.info("failed: " + e.getMessage()); + break; + } + catch (ComponentInitialisationException e) { + Logger.info("failed: " + e); + } + catch (ConnectionException e) { + Logger.info("failed: " + e); + break; + } + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/Timestep.java b/modules/rescuecore2/src/rescuecore2/Timestep.java new file mode 100644 index 0000000000000000000000000000000000000000..d13e1185a403bef9e776d17591c97bb5dc3f0129 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/Timestep.java @@ -0,0 +1,148 @@ +package rescuecore2; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.ArrayList; + +import rescuecore2.messages.Command; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +/** + A record of everything that happened in a timestep. This includes agent perception, commands and world model updates. +*/ +public class Timestep { + private int time; + private Collection<Command> commands; + private ChangeSet changes; + private Map<EntityID, ChangeSet> agentPerception; + private Map<EntityID, Collection<Command>> agentHearing; + private double score; + + /** + Construct a timestep record. + @param time The timestep number. + */ + public Timestep(int time) { + this.time = time; + agentPerception = new HashMap<EntityID, ChangeSet>(); + agentHearing = new HashMap<EntityID, Collection<Command>>(); + commands = new ArrayList<Command>(); + } + + /** + Set the commands for this timestep. + @param c The commands. + */ + public void setCommands(Collection<Command> c) { + commands.clear(); + commands.addAll(c); + } + + /** + Set the simulator updates ChangeSet for this timestep. + @param c The ChangeSet. + */ + public void setChangeSet(ChangeSet c) { + this.changes = c; + } + + /** + Register agent perception. + @param id The agent ID. + @param perception The ChangeSet the entity can perceive. + @param hearing The messages the agent heard. + */ + public void registerPerception(EntityID id, ChangeSet perception, Collection<Command> hearing) { + agentPerception.put(id, perception); + agentHearing.put(id, hearing); + } + + /** + Set the score for this timestep. + @param s The score. + */ + public void setScore(double s) { + score = s; + } + + /** + Get the time. + @return The time. + */ + public int getTime() { + return time; + } + + /** + Get the commands sent by agents this timestep. + @return The commands. + */ + public Collection<Command> getCommands() { + return Collections.unmodifiableCollection(commands); + } + + /** + Get the commands sent by a particular agent this timestep. + @param agentID The ID of the agent. + @return All commands send by that agent. + */ + public Collection<Command> getCommands(EntityID agentID) { + Set<Command> result = new HashSet<Command>(); + for (Command next : commands) { + if (next.getAgentID().equals(agentID)) { + result.add(next); + } + } + return result; + } + + /** + Get the changes to entities during this timestep. + @return The changes. + */ + public ChangeSet getChangeSet() { + return changes; + } + + /** + Get the set of agent IDs for agents that received an update this timestep. + @return The IDs of all agents that received an update. + */ + public Set<EntityID> getAgentsWithUpdates() { + Set<EntityID> result = new HashSet<EntityID>(); + result.addAll(agentPerception.keySet()); + result.addAll(agentHearing.keySet()); + return result; + } + + /** + Get the changes to entities that an agent saw at the start of this timestep. + @param agentID The agent ID to look up. + @return The ChangeSet the agent saw, or null if the ID is not recognised. + */ + public ChangeSet getAgentPerception(EntityID agentID) { + return agentPerception.get(agentID); + } + + /** + Get the communication messages that an agent heard at the start of this timestep. + @param agentID The agent ID to look up. + @return The set of messages the agent heard, or null if the ID is not recognised. + */ + public Collection<Command> getAgentHearing(EntityID agentID) { + return agentHearing.get(agentID); + } + + /** + Get the score for this timestep. + @return The score. + */ + public double getScore() { + return score; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/URN.java b/modules/rescuecore2/src/rescuecore2/URN.java new file mode 100644 index 0000000000000000000000000000000000000000..c1cda23463e74ce4cea8dfa5ea643ede0ea86ae9 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/URN.java @@ -0,0 +1,29 @@ +package rescuecore2; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public interface URN { + public int getURNId(); + + public String getURNStr(); + + public String name(); + + static <T extends Enum<?> & URN> Map<Integer, T> generateMap(Class<T> urnEnum) { + Map<Integer, T> map = new HashMap<>(); + + for (T t : urnEnum.getEnumConstants()) + map.put(t.getURNId(), t); + return Collections.unmodifiableMap(map); + } + + static <T extends Enum<?> & URN> Map<String, T> generateMapStr(Class<T> urnEnum) { + Map<String, T> map = new HashMap<>(); + + for (T t : urnEnum.getEnumConstants()) + map.put(t.getURNStr(), t); + return Collections.unmodifiableMap(map); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/components/AbstractAgent.java b/modules/rescuecore2/src/rescuecore2/components/AbstractAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..0fa837794ac7467d34d50a3f0df9b9b59b785b21 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/AbstractAgent.java @@ -0,0 +1,176 @@ +package rescuecore2.components; + +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.connection.ConnectionException; +import rescuecore2.messages.Message; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KASense; +import rescuecore2.messages.control.AKConnect; +import rescuecore2.messages.control.AKAcknowledge; +import rescuecore2.messages.control.KAConnectOK; +import rescuecore2.messages.control.KAConnectError; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.config.Config; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +/** + Abstract base class for agent implementations. + @param <T> The subclass of WorldModel that this agent understands. + @param <E> The subclass of Entity that this agent wants to control. + */ +public abstract class AbstractAgent<T extends WorldModel<? extends Entity>, E extends Entity> extends AbstractComponent<T> implements Agent { + /** + The ID of the entity controlled by this agent. + */ + private EntityID entityID; + + /** + Create a new AbstractAgent. + */ + protected AbstractAgent() { + config = new Config(); + } + + @Override + public final void postConnect(Connection c, EntityID agentID, Collection<Entity> entities, Config kernelConfig) { + this.entityID = agentID; + super.postConnect(c, entities, kernelConfig); + } + + @Override + public EntityID getID() { + return entityID; + } + + @Override + public void connect(Connection connection, RequestIDGenerator generator, Config config) throws ConnectionException, ComponentConnectionException, InterruptedException { + this.config = config; + int requestID = generator.generateRequestID(); + AKConnect connect = new AKConnect(requestID, 2, getName(), getRequestedEntityURNs()); + CountDownLatch latch = new CountDownLatch(1); + AgentConnectionListener l = new AgentConnectionListener(requestID, latch); + connection.addConnectionListener(l); + connection.sendMessage(connect); + // Wait for a reply + latch.await(); + l.testSuccess(); + } + + @Override + protected void postConnect() { + super.postConnect(); + } + + @Override + protected String getPreferredNDC() { + if (me() != null) { + return me().toString(); + } + return null; + } + + /** + Notification that a timestep has started. + @param time The timestep. + @param changes The set of changes observed this timestep. + @param heard The set of communication messages this agent heard. + */ + protected abstract void think(int time, ChangeSet changes, Collection<Command> heard); + + /** + Process an incoming sense message. The default implementation updates the world model and calls {@link #think}. Subclasses should generally not override this method but instead implement the {@link #think} method. + @param sense The sense message. + */ + protected void processSense(KASense sense) { + model.merge(sense.getChangeSet()); + Collection<Command> heard = sense.getHearing(); + think(sense.getTime(), sense.getChangeSet(), heard); + } + + /** + Get the entity controlled by this agent. + @return The entity controlled by this agent. + */ + @SuppressWarnings("unchecked") + protected E me() { + if (entityID == null) { + return null; + } + if (model == null) { + return null; + } + return (E)model.getEntity(entityID); + } + + @Override + protected void processMessage(Message msg) { + if (msg instanceof KASense) { + KASense sense = (KASense)msg; + if (entityID.equals(sense.getAgentID())) { + processSense(sense); + } + } + else { + super.processMessage(msg); + } + } + + private class AgentConnectionListener implements ConnectionListener { + private int requestID; + private CountDownLatch latch; + private ComponentConnectionException failureReason; + + public AgentConnectionListener(int requestID, CountDownLatch latch) { + this.requestID = requestID; + this.latch = latch; + failureReason = null; + } + + @Override + public void messageReceived(Connection c, Message msg) { + if (msg instanceof KAConnectOK) { + handleConnectOK(c, (KAConnectOK)msg); + } + if (msg instanceof KAConnectError) { + handleConnectError(c, (KAConnectError)msg); + } + } + + private void handleConnectOK(Connection c, KAConnectOK ok) { + if (ok.getRequestID() == requestID) { + c.removeConnectionListener(this); + postConnect(c, ok.getAgentID(), ok.getEntities(), ok.getConfig()); + try { + c.sendMessage(new AKAcknowledge(requestID, ok.getAgentID())); + } + catch (ConnectionException e) { + failureReason = new ComponentConnectionException(e); + } + latch.countDown(); + } + } + + private void handleConnectError(Connection c, KAConnectError error) { + if (error.getRequestID() == requestID) { + c.removeConnectionListener(this); + failureReason = new ComponentConnectionException(error.getReason()); + latch.countDown(); + } + } + + /** + Check if the connection succeeded and throw an exception if is has not. + */ + void testSuccess() throws ComponentConnectionException { + if (failureReason != null) { + throw failureReason; + } + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/components/AbstractComponent.java b/modules/rescuecore2/src/rescuecore2/components/AbstractComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..d9d151a169103f8c527a3aebb6b75d1386a9b7af --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/AbstractComponent.java @@ -0,0 +1,235 @@ +package rescuecore2.components; + +import java.util.Collection; +import java.util.Random; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.Shutdown; +import rescuecore2.misc.WorkerThread; +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * Abstract base class for component implementations. + * + * @param <T> The subclass of WorldModel that this component understands. + */ +public abstract class AbstractComponent<T extends WorldModel<? extends Entity>> implements Component { + + /** + * The connection to the kernel. + */ + protected Connection connection; + + /** + * The configuration. This will be automatically updated by the postConnect + * method to include config information from the kernel. + */ + protected Config config; + + /** + * The world model. + */ + protected T model; + + /** + * A random number generator. + */ + protected Random random; + + /** + * The thread that processes incoming messages. + */ + private MessageProcessor processor; + + /** + * Create a new AbstractComponent. + */ + protected AbstractComponent() { + } + + /** + * Notification that connection to the kernel succeeded. + * + * @param c The kernel connection. + * @param entities The entities that the kernel sent on startup. + * @param kernelConfig The config that the kernel sent on startup. + */ + protected final void postConnect(Connection c, Collection<Entity> entities, Config kernelConfig) { + connection = c; + model = createWorldModel(); + model.addEntities(entities); + config.merge(kernelConfig); + random = config.getRandom(); + String ndc = getPreferredNDC(); + if (ndc != null) { + Logger.pushNDC(ndc); + } + try { + Logger.info(this + " connected"); + postConnect(); + processor = new MessageProcessor(); + c.addConnectionListener(new MessageListener()); + processor.start(); + } finally { + if (ndc != null) { + Logger.popNDC(); + } + } + } + + /** + * Perform any post-connection work required before acknowledgement of the + * connection is made. The default implementation does nothing. + */ + protected void postConnect() { + } + + /** + * Construct the world model. + * + * @return The world model. + */ + protected abstract T createWorldModel(); + + /** + * Send a message to the kernel and silently ignore any errors. + * + * @param msg The message to send. + */ + protected final void send(Message msg) { + try { + connection.sendMessage(msg); + } catch (ConnectionException e) { + // Ignore and log + Logger.error("Error sending message", e); + } + } + + @Override + public String getPreferredLogContext() { + return getClass().getName(); + } + + /** + * Get the preferred nested diagnostic context to use when processing messages + * for this component. Default implementation returns null. + * + * @return The preferred NDC for this component, or null if no context is + * required. + */ + protected String getPreferredNDC() { + return null; + } + + @Override + public void initialise() { + } + + @Override + public void shutdown() { + try { + processor.kill(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public String getName() { + return getClass().getName(); + } + + @Override + public Registry getPreferredRegistry(Registry parent) { + return parent; + } + + /** + * Process an incoming message. + * + * @param msg The incoming message. + */ + protected void processMessage(Message msg) { + Logger.info("Unrecognised message type: " + msg); + } + + /** + * Process an incoming message immediately. If the message can be processed + * quickly then this method should do so and return true. If the message may + * take some time to process (e.g. if it is a sense message (for agents) or a + * command message (for simulators) then this method should return false and the + * message will be processed in a different thread via the + * {@link #processMessage(Message)} method. + * + * @param msg The incoming message. + * @return true If the message was processed immediately, false if it requires + * slower processing. + */ + protected boolean processImmediately(Message msg) { + if (msg instanceof Shutdown) { + shutdown(); + return true; + } else { + return false; + } + } + + private class MessageProcessor extends WorkerThread { + private BlockingQueue<Message> queue; + + MessageProcessor() { + queue = new LinkedBlockingQueue<Message>(); + } + + void push(Message m) { + queue.add(m); + } + + @Override + public boolean work() throws InterruptedException { + String ndc = getPreferredNDC(); + if (ndc != null) { + Logger.pushNDC(ndc); + } + try { + Logger.trace("MessageProcessor working: " + queue.size() + " messages in the queue"); + Message msg = queue.take(); + Logger.trace("Next message: " + msg); + AbstractComponent.this.processMessage(msg); + return true; + } finally { + if (ndc != null) { + Logger.popNDC(); + } + } + } + } + + private class MessageListener implements ConnectionListener { + @Override + public void messageReceived(Connection c, Message msg) { + String ndc = getPreferredNDC(); + if (ndc != null) { + Logger.pushNDC(ndc); + } + try { + if (!processImmediately(msg)) { + processor.push(msg); + } + } finally { + if (ndc != null) { + Logger.popNDC(); + } + } + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/components/AbstractSimulator.java b/modules/rescuecore2/src/rescuecore2/components/AbstractSimulator.java new file mode 100644 index 0000000000000000000000000000000000000000..1082efe8ebb13011d2d9ad86dcb4d9304456b6a9 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/AbstractSimulator.java @@ -0,0 +1,237 @@ +package rescuecore2.components; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.EntityIDRequest; +import rescuecore2.messages.control.EntityIDResponse; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.messages.control.KSConnectError; +import rescuecore2.messages.control.KSConnectOK; +import rescuecore2.messages.control.KSUpdate; +import rescuecore2.messages.control.SKAcknowledge; +import rescuecore2.messages.control.SKConnect; +import rescuecore2.messages.control.SKUpdate; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +/** + * Abstract base class for simulator implementations. + * + * @param <T> The subclass of WorldModel that this simulator understands. + */ +public abstract class AbstractSimulator<T extends WorldModel<? extends Entity>> extends AbstractComponent<T> + implements Simulator { + /** + * The ID of this simulator. + */ + protected int simulatorID; + + private int lastUpdateTime; + + private Map<Integer, List<EntityID>> idRequests; + private int nextIDRequest; + + /** + * Create a new AbstractSimulator. + */ + protected AbstractSimulator() { + } + + /** + * Get this simulator's ID. + * + * @return The simulator ID. + */ + public final int getSimulatorID() { + return simulatorID; + } + + @Override + public void postConnect(Connection c, int id, Collection<Entity> entities, Config kernelConfig) { + this.simulatorID = id; + lastUpdateTime = 0; + nextIDRequest = 0; + idRequests = new HashMap<Integer, List<EntityID>>(); + super.postConnect(c, entities, kernelConfig); + } + + @Override + public void connect(Connection connection, RequestIDGenerator generator, Config config) + throws ConnectionException, ComponentConnectionException, InterruptedException { + this.config = config; + int requestID = generator.generateRequestID(); + SKConnect connect = new SKConnect(requestID, 1, getName()); + CountDownLatch latch = new CountDownLatch(1); + SimulatorConnectionListener l = new SimulatorConnectionListener(requestID, latch); + connection.addConnectionListener(l); + connection.sendMessage(connect); + // Wait for a reply + latch.await(); + l.testSuccess(); + } + + @Override + public void shutdown() { + super.shutdown(); + } + + /** + * Handle a KSUpdate object from the server. The default implementation just + * updates the world model. + * + * @param u The Update object. + */ + protected void handleUpdate(KSUpdate u) { + ChangeSet changes = u.getChangeSet(); + int time = u.getTime(); + if (time != lastUpdateTime + 1) { + Logger.warn( + "Received an unexpected update from the kernel. Last update: " + lastUpdateTime + ", this update: " + time); + } + lastUpdateTime = time; + model.merge(changes); + } + + /** + * Handle a KSCommands object from the server. The default implementation tells + * the kernel that nothing has changed. + * + * @param c The Commands object. + */ + protected void handleCommands(KSCommands c) { + ChangeSet changes = new ChangeSet(); + processCommands(c, changes); + send(new SKUpdate(simulatorID, c.getTime(), changes)); + } + + /** + * Process the commands from the server and populate a ChangeSet. + * + * @param c The commands to process. + * @param changes The ChangeSet to populate. + */ + protected void processCommands(KSCommands c, ChangeSet changes) { + } + + /** + * Request some new entity IDs from the kernel. + * + * @param count The number to request. + * @return A list of new entity IDs. + */ + protected List<EntityID> requestNewEntityIDs(int count) throws InterruptedException { + synchronized (idRequests) { + int id = nextIDRequest++; + Logger.debug("Requesting " + count + " new IDs: request number " + id); + send(new EntityIDRequest(simulatorID, id, count)); + // Wait for a reply + Integer key = id; + while (!idRequests.containsKey(key)) { + Logger.debug("Waiting for response"); + idRequests.wait(); + } + List<EntityID> result = idRequests.get(key); + idRequests.remove(key); + return result; + } + } + + @Override + protected void processMessage(Message msg) { + if (msg instanceof KSUpdate) { + KSUpdate u = (KSUpdate) msg; + if (u.getTargetID() == simulatorID) { + handleUpdate(u); + } + } else if (msg instanceof KSCommands) { + KSCommands commands = (KSCommands) msg; + if (commands.getTargetID() == simulatorID) { + handleCommands(commands); + } + } else { + super.processMessage(msg); + } + } + + @Override + protected boolean processImmediately(Message msg) { + if (msg instanceof EntityIDResponse) { + EntityIDResponse resp = (EntityIDResponse) msg; + Logger.debug("Received " + msg); + if (resp.getSimulatorID() == simulatorID) { + synchronized (idRequests) { + Logger.debug("ID response: " + resp.getRequestID() + ", " + resp.getEntityIDs()); + idRequests.put(resp.getRequestID(), resp.getEntityIDs()); + idRequests.notifyAll(); + } + } + return true; + } else { + return super.processImmediately(msg); + } + } + + private class SimulatorConnectionListener implements ConnectionListener { + private int requestID; + private CountDownLatch latch; + private ComponentConnectionException failureReason; + + public SimulatorConnectionListener(int requestID, CountDownLatch latch) { + this.requestID = requestID; + this.latch = latch; + failureReason = null; + } + + @Override + public void messageReceived(Connection c, Message msg) { + if (msg instanceof KSConnectOK) { + handleConnectOK(c, (KSConnectOK) msg); + } + if (msg instanceof KSConnectError) { + handleConnectError(c, (KSConnectError) msg); + } + } + + private void handleConnectOK(Connection c, KSConnectOK ok) { + if (ok.getRequestID() == requestID) { + c.removeConnectionListener(this); + postConnect(c, ok.getSimulatorID(), ok.getEntities(), ok.getConfig()); + try { + c.sendMessage(new SKAcknowledge(requestID, ok.getSimulatorID())); + } catch (ConnectionException e) { + failureReason = new ComponentConnectionException(e); + } + latch.countDown(); + } + } + + private void handleConnectError(Connection c, KSConnectError error) { + if (error.getRequestID() == requestID) { + c.removeConnectionListener(this); + failureReason = new ComponentConnectionException(error.getReason()); + latch.countDown(); + } + } + + /** + * Check if the connection succeeded and throw an exception if is has not. + */ + void testSuccess() throws ComponentConnectionException { + if (failureReason != null) { + throw failureReason; + } + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/components/AbstractViewer.java b/modules/rescuecore2/src/rescuecore2/components/AbstractViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..fed96836b60ca3f0a3f8df8356ecda1e20973246 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/AbstractViewer.java @@ -0,0 +1,152 @@ +package rescuecore2.components; + +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.connection.ConnectionListener; +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.control.KVConnectError; +import rescuecore2.messages.control.KVConnectOK; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.messages.control.VKAcknowledge; +import rescuecore2.messages.control.VKConnect; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * Abstract base class for viewer implementations. + * + * @param <T> The subclass of WorldModel that this viewer understands. + */ +public abstract class AbstractViewer<T extends WorldModel<? extends Entity>> extends AbstractComponent<T> + implements Viewer { + /** + * The ID of this viewer. + */ + protected int viewerID; + + private int lastUpdateTime; + + /** + * Create a new AbstractViewer. + */ + protected AbstractViewer() { + } + + /** + * Get this viewer's ID. + * + * @return The viewer ID. + */ + public final int getViewerID() { + return viewerID; + } + + @Override + public void postConnect(Connection c, int id, Collection<Entity> entities, Config kernelConfig) { + this.viewerID = id; + lastUpdateTime = 0; + super.postConnect(c, entities, kernelConfig); + } + + @Override + public void connect(Connection connection, RequestIDGenerator generator, Config config) + throws ConnectionException, ComponentConnectionException, InterruptedException { + this.config = config; + int requestID = generator.generateRequestID(); + VKConnect connect = new VKConnect(requestID, 1, getName()); + CountDownLatch latch = new CountDownLatch(1); + ViewerConnectionListener l = new ViewerConnectionListener(requestID, latch); + connection.addConnectionListener(l); + connection.sendMessage(connect); + // Wait for a reply + latch.await(); + l.testSuccess(); + } + + /** + * Handle a KVTimestep object from the server. The default implementation just + * updates the world model. + * + * @param timestep The KVTimestep object. + */ + protected void handleTimestep(KVTimestep timestep) { + ChangeSet changes = timestep.getChangeSet(); + int time = timestep.getTime(); + if (time != lastUpdateTime + 1) { + Logger.warn( + "Received an unexpected update from the kernel. Last update: " + lastUpdateTime + ", this update: " + time); + } + lastUpdateTime = time; + model.merge(changes); + } + + @Override + protected void processMessage(Message msg) { + if (msg instanceof KVTimestep) { + KVTimestep t = (KVTimestep) msg; + if (t.getTargetID() == viewerID) { + handleTimestep(t); + } + } else { + super.processMessage(msg); + } + } + + private class ViewerConnectionListener implements ConnectionListener { + private int requestID; + private CountDownLatch latch; + private ComponentConnectionException failureReason; + + public ViewerConnectionListener(int requestID, CountDownLatch latch) { + this.requestID = requestID; + this.latch = latch; + failureReason = null; + } + + @Override + public void messageReceived(Connection c, Message msg) { + if (msg instanceof KVConnectOK) { + handleConnectOK(c, (KVConnectOK) msg); + } + if (msg instanceof KVConnectError) { + handleConnectError(c, (KVConnectError) msg); + } + } + + private void handleConnectOK(Connection c, KVConnectOK ok) { + if (ok.getRequestID() == requestID) { + c.removeConnectionListener(this); + postConnect(c, ok.getViewerID(), ok.getEntities(), ok.getConfig()); + try { + c.sendMessage(new VKAcknowledge(requestID, ok.getViewerID())); + } catch (ConnectionException e) { + failureReason = new ComponentConnectionException(e); + } + latch.countDown(); + } + } + + private void handleConnectError(Connection c, KVConnectError error) { + if (error.getRequestID() == requestID) { + c.removeConnectionListener(this); + failureReason = new ComponentConnectionException(error.getReason()); + latch.countDown(); + } + } + + /** + * Check if the connection succeeded and throw an exception if is has not. + */ + void testSuccess() throws ComponentConnectionException { + if (failureReason != null) { + throw failureReason; + } + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/components/Agent.java b/modules/rescuecore2/src/rescuecore2/components/Agent.java new file mode 100644 index 0000000000000000000000000000000000000000..e88bc243ecf58024f9bed308d30af1a53f5a75fa --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/Agent.java @@ -0,0 +1,34 @@ +package rescuecore2.components; + +import rescuecore2.connection.Connection; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.config.Config; + +import java.util.Collection; + +/** + Sub-interface for Agent components. + */ +public interface Agent extends Component { + /** + Get the list of entity URNs that this agent is willing to control. + @return An array of entity URNs. + */ + int[] getRequestedEntityURNs(); + + /** + Notification that this agent has been connected to the kernel. + @param c The connection to the kernel. + @param agentID The ID of the entity controlled by this agent. + @param entities The set of Entities the kernel sent to this agent on connection. + @param config The Config the kernel send to this agent on connection. + */ + void postConnect(Connection c, EntityID agentID, Collection<Entity> entities, Config config); + + /** + Get the ID of the entity this agent controls. If the agent has not yet been connected to the kernel then null will be returned. + @return The entity ID or null if this agent is not yet connected. + */ + EntityID getID(); +} diff --git a/modules/rescuecore2/src/rescuecore2/components/Component.java b/modules/rescuecore2/src/rescuecore2/components/Component.java new file mode 100644 index 0000000000000000000000000000000000000000..d54d7f239e49774ed4738a9e442c5ddc335853dc --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/Component.java @@ -0,0 +1,52 @@ +package rescuecore2.components; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.registry.Registry; + +/** + Top-level interface for components of the Robocup Rescue simulation. Agents, simulators and viewers are all components. + */ +public interface Component { + /** + Initialise this component before connection. + @throws ComponentInitialisationException If there is a problem initialising the component. + */ + void initialise() throws ComponentInitialisationException; + + /** + Shut this component down. + */ + void shutdown(); + + /** + Get the name of this component. This is useful for debugging. Often a class name will be sufficient. + @return A name. + */ + String getName(); + + /** + Get the registry this component would like to use for its connection. + @param parent The parent registry. + @return The preferred registry. + */ + Registry getPreferredRegistry(Registry parent); + + /** + Get the preferred log context for this component. + @return The preferred log context for this component. + */ + String getPreferredLogContext(); + + /** + Connect this component to the kernel. + @param connection The Connection to use. + @param generator The RequestIDGenerator to use. + @param config The system configuration. + @throws ConnectionException If there is a problem communicating with the kernel. + @throws ComponentConnectionException If the connection fails. + @throws InterruptedException If the thread is interrupted before the connection attempt completes. + */ + void connect(Connection connection, RequestIDGenerator generator, Config config) throws ConnectionException, ComponentConnectionException, InterruptedException; +} diff --git a/modules/rescuecore2/src/rescuecore2/components/ComponentConnectionException.java b/modules/rescuecore2/src/rescuecore2/components/ComponentConnectionException.java new file mode 100644 index 0000000000000000000000000000000000000000..66c8a8cf0951727349f2fb3291c932b819aa1a03 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/ComponentConnectionException.java @@ -0,0 +1,31 @@ +package rescuecore2.components; + +/** + Exception class for component connection errors. + */ +public class ComponentConnectionException extends Exception { + /** + Construct an exception with an error message. + @param msg A message describing the problem. + */ + public ComponentConnectionException(final String msg) { + super(msg); + } + + /** + Construct an exception with an underlying cause. + @param cause The underlying cause of this exception. + */ + public ComponentConnectionException(final Throwable cause) { + super(cause); + } + + /** + Construct an exception with an error message and underlying cause. + @param msg A message describing the problem. + @param cause The underlying cause of this exception. + */ + public ComponentConnectionException(final String msg, final Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/components/ComponentInitialisationException.java b/modules/rescuecore2/src/rescuecore2/components/ComponentInitialisationException.java new file mode 100644 index 0000000000000000000000000000000000000000..8ff8dd2ce1c5390c21e06dba97e60da8cbc78158 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/ComponentInitialisationException.java @@ -0,0 +1,37 @@ +package rescuecore2.components; + +/** + Exception class for problems with initialisation of components. + */ +public class ComponentInitialisationException extends Exception { + /** + Construct an exception with no useful information. + */ + public ComponentInitialisationException() { + } + + /** + Construct an exception with an error message. + @param msg A message describing the problem. + */ + public ComponentInitialisationException(final String msg) { + super(msg); + } + + /** + Construct an exception with an underlying cause. + @param cause The underlying cause of this exception. + */ + public ComponentInitialisationException(final Throwable cause) { + super(cause); + } + + /** + Construct an exception with an error message and underlying cause. + @param msg A message describing the problem. + @param cause The underlying cause of this exception. + */ + public ComponentInitialisationException(final String msg, final Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/components/ComponentLauncher.java b/modules/rescuecore2/src/rescuecore2/components/ComponentLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..26039dff3d45bc8831626dfec1fc014d31a91671 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/ComponentLauncher.java @@ -0,0 +1,77 @@ +package rescuecore2.components; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.ConnectionException; +import rescuecore2.registry.Registry; +import rescuecore2.log.Logger; + +/** + A class that knows how to connect components to the kernel. + */ +public abstract class ComponentLauncher implements RequestIDGenerator { + private Config config; + private int nextRequestID; + private Registry defaultRegistry; + + /** + Construct a new ComponentLauncher. + @param config The system configuration. + */ + public ComponentLauncher(Config config) { + this.config = config; + nextRequestID = 1; + defaultRegistry = Registry.SYSTEM_REGISTRY; + } + + /** + Connect a Component to the kernel. Throws a ComponentConnectionException if the connection fails due to a kernel ConnectError message. + @param c The component to connect. + @throws InterruptedException If the thread is interrupted before the connection attempt completes. + @throws ConnectionException If there is a problem communicating with the kernel. + @throws ComponentConnectionException If the connection fails. + */ + public void connect(Component c) throws InterruptedException, ConnectionException, ComponentConnectionException { + Connection connection = makeConnection(); + connection.setName("Connection from " + c.getName()); + Logger.pushLogContext(c.getPreferredLogContext()); + connection.setRegistry(c.getPreferredRegistry(defaultRegistry)); + connection.startup(); + try { + c.connect(connection, this, new Config(config)); + } + finally { + Logger.popLogContext(); + } + } + + @Override + public int generateRequestID() { + synchronized (this) { + return nextRequestID++; + } + } + + /** + Set the default registry for new connections. + @param registry The new default registry. + */ + public void setDefaultRegistry(Registry registry) { + defaultRegistry = registry; + } + + /** + Get the default registry for new connections. + @return The default registry. + */ + public Registry getDefaultRegistry() { + return defaultRegistry; + } + + /** + Make a new connection. + @return The new connection. + @throws ConnectionException If there is a problem creating the connection. + */ + protected abstract Connection makeConnection() throws ConnectionException; +} diff --git a/modules/rescuecore2/src/rescuecore2/components/RequestIDGenerator.java b/modules/rescuecore2/src/rescuecore2/components/RequestIDGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..c124bafcfc52d61c4c9906a3c2a75680eb831209 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/RequestIDGenerator.java @@ -0,0 +1,12 @@ +package rescuecore2.components; + +/** + An interface for objects that can generate request IDs. +*/ +public interface RequestIDGenerator { + /** + Generate a request ID. + @return A new, unique request ID. + */ + int generateRequestID(); +} diff --git a/modules/rescuecore2/src/rescuecore2/components/Simulator.java b/modules/rescuecore2/src/rescuecore2/components/Simulator.java new file mode 100644 index 0000000000000000000000000000000000000000..c99116429491b414d00f37ba844dca8ace2d403f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/Simulator.java @@ -0,0 +1,21 @@ +package rescuecore2.components; + +import rescuecore2.connection.Connection; +import rescuecore2.worldmodel.Entity; +import rescuecore2.config.Config; + +import java.util.Collection; + +/** + Sub-interface for Simulator components. + */ +public interface Simulator extends Component { + /** + Notification that this simulator has been connected to the kernel. + @param c The connection to the kernel. + @param simulatorID The ID of this simulator. + @param entities The set of Entities the kernel sent to this simulator on connection. + @param config The Config the kernel send to this simulator on connection. + */ + void postConnect(Connection c, int simulatorID, Collection<Entity> entities, Config config); +} diff --git a/modules/rescuecore2/src/rescuecore2/components/TCPComponentLauncher.java b/modules/rescuecore2/src/rescuecore2/components/TCPComponentLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..804ec5127153a9aa2f599a7273d89d3e52792156 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/TCPComponentLauncher.java @@ -0,0 +1,38 @@ +package rescuecore2.components; + +import rescuecore2.config.Config; +import rescuecore2.connection.Connection; +import rescuecore2.connection.TCPConnection; +import rescuecore2.connection.ConnectionException; + +import java.io.IOException; + +/** + A class that knows how to connect components to the kernel via TCP. + */ +public class TCPComponentLauncher extends ComponentLauncher { + private String host; + private int port; + + /** + Construct a new TCPComponentLauncher. + @param host The host name. + @param port The host port. + @param config The system configuration. + */ + public TCPComponentLauncher(String host, int port, Config config) { + super(config); + this.host = host; + this.port = port; + } + + @Override + protected Connection makeConnection() throws ConnectionException { + try { + return new TCPConnection(host, port); + } + catch (IOException e) { + throw new ConnectionException(e); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/components/Viewer.java b/modules/rescuecore2/src/rescuecore2/components/Viewer.java new file mode 100644 index 0000000000000000000000000000000000000000..18a743b5b0aa95e2a02ab01df14c027e0ee1c67a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/components/Viewer.java @@ -0,0 +1,21 @@ +package rescuecore2.components; + +import rescuecore2.connection.Connection; +import rescuecore2.worldmodel.Entity; +import rescuecore2.config.Config; + +import java.util.Collection; + +/** + Sub-interface for Viewer components. + */ +public interface Viewer extends Component { + /** + Notification that this viewer has been connected to the kernel. + @param c The connection to the kernel. + @param viewerID The ID of this viewer. + @param entities The set of Entities the kernel sent to this viewer on connection. + @param config The Config the kernel send to this agent on connection. + */ + void postConnect(Connection c, int viewerID, Collection<Entity> entities, Config config); +} diff --git a/modules/rescuecore2/src/rescuecore2/config/AbstractValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/AbstractValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..66289ccf7fe915865d5280691b13b42dae50ac0c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/AbstractValueConstraint.java @@ -0,0 +1,44 @@ +package rescuecore2.config; + +import java.util.Set; +import java.util.Collections; + +/** + Abstract base class for value constraints. +*/ +public abstract class AbstractValueConstraint implements ValueConstraint { + /** The key this constraint refers to. */ + protected final String key; + + /** + Construct an AbstractConstrainedConfigValue. + @param key The key this constraint applies to. + */ + protected AbstractValueConstraint(String key) { + this.key = key; + } + + @Override + public final boolean isViolated(Config config) { + String value = config.getValue(key, null); + if (value == null) { + return !undefinedIsValid(); + } + return !isValid(value, config); + } + + @Override + public final String getKey() { + return key; + } + + @Override + public final Set<String> getKeys() { + return Collections.singleton(key); + } + + @Override + public boolean undefinedIsValid() { + return false; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/ClassNameSetValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/ClassNameSetValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..594e55f09502b2a1eb5a5cbfb72a31a4f7cf35b7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/ClassNameSetValueConstraint.java @@ -0,0 +1,57 @@ +package rescuecore2.config; + +/** + A config value constraint that requires the value to be a list of valid class names. +*/ +public class ClassNameSetValueConstraint extends AbstractValueConstraint { + private Class<?> required; + private boolean allowUndefined; + + /** + Construct a ClassNameSetValueConstraint that does not require the values to be a particular class. + @param key The key this constraint applies to. + */ + public ClassNameSetValueConstraint(String key) { + this(key, null); + } + + /** + Construct a ClassNameSetValueConstraint that requires the values to be a particular class. + @param key The key this constraint applies to. + @param required The required class. + */ + public ClassNameSetValueConstraint(String key, Class<?> required) { + super(key); + this.required = required; + allowUndefined = true; + } + + @Override + public String getDescription() { + if (required == null) { + return "Must be a list of valid class names"; + } + return "Must be a list of valid class names that extend " + required.getName(); + } + + @Override + public boolean isValid(String value, Config config) { + try { + for (String next : config.getArrayValue(key)) { + Class<?> c = Class.forName(next); + if (required != null && !required.isAssignableFrom(c)) { + return false; + } + } + } + catch (ClassNotFoundException e) { + return false; + } + return true; + } + + @Override + public boolean undefinedIsValid() { + return allowUndefined; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/ClassNameValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/ClassNameValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..677e2660d2261ca7a5ea8d723dfd8d24ed682389 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/ClassNameValueConstraint.java @@ -0,0 +1,48 @@ +package rescuecore2.config; + +/** + A config value constraint that requires the value to be a valid class name. +*/ +public class ClassNameValueConstraint extends AbstractValueConstraint { + private Class<?> required; + + /** + Construct a ClassNameConstrainedConfigValue that does not require the value to be a particular class. + @param key The key this constraint applies to. + */ + public ClassNameValueConstraint(String key) { + this(key, null); + } + + /** + Construct a ClassNameConstrainedConfigValue that requires the value to be a particular class. + @param key The key this constraint applies to. + @param required The required class. + */ + public ClassNameValueConstraint(String key, Class<?> required) { + super(key); + this.required = required; + } + + @Override + public String getDescription() { + if (required == null) { + return "Must be a valid class name"; + } + return "Must be a valid class name that extends " + required.getName(); + } + + @Override + public boolean isValid(String value, Config config) { + try { + Class<?> c = Class.forName(value); + if (required != null) { + return required.isAssignableFrom(c); + } + return true; + } + catch (ClassNotFoundException e) { + return false; + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/Config.java b/modules/rescuecore2/src/rescuecore2/config/Config.java new file mode 100644 index 0000000000000000000000000000000000000000..60632d41b39f6e3dfa952b65b76f4cd31f0b53f7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/Config.java @@ -0,0 +1,1051 @@ +package rescuecore2.config; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.uncommons.maths.random.MersenneTwisterRNG; +import org.uncommons.maths.random.SeedGenerator; + +import rescuecore2.Constants; +import rescuecore2.log.Logger; + +/** + * This class represents a config file and any other config files that might + * have been included with a !include directive. Config files must be defined + * relative to a base directory so that includes can be resolved. + */ +public class Config { + private static final String ARRAY_REGEX = " |,"; + + /** + * The raw data and caches of int/float/boolean/array interpretations. + */ + private Map<String, String> data; + // Entries that should not be cached + private Set<String> noCache; + private Map<String, Integer> intData; + private Map<String, Double> floatData; + private Map<String, Boolean> booleanData; + private Map<String, List<String>> arrayData; + + private Set<ConfigConstraint> constraints; + private Set<ConfigConstraint> violatedConstraints; + + private static Random seedGenerator; + private Random random; + + /** + * Create an empty config. + */ + public Config() { + data = new HashMap<String, String>(); + noCache = new HashSet<String>(); + intData = new HashMap<String, Integer>(); + floatData = new HashMap<String, Double>(); + booleanData = new HashMap<String, Boolean>(); + arrayData = new HashMap<String, List<String>>(); + constraints = new HashSet<ConfigConstraint>(); + violatedConstraints = new HashSet<ConfigConstraint>(); + } + + /** + * Create a config that reads from a given file. Additional config files can be + * read later with the {@link #read(String)} method. + * + * @param file The config file to read. Must not be null. + * @throws ConfigException If there is an error parsing the config file or one + * of its descendants. + */ + public Config(File file) throws ConfigException { + this(); + read(file); + } + + /** + * Copy constructor. The new Config will contain all keys and values that are + * currently in this config. + * + * @param other The Config to copy. + */ + public Config(Config other) { + this(); + this.data.putAll(other.data); + } + + /** + * Read a config file from a resource that this class' classloader can find. + * + * @param resource The name of the resource. + * @throws ConfigException If there is an error parsing the config file or one + * of its descendants. + */ + public void read(String resource) throws ConfigException { + if (resource == null) { + throw new IllegalArgumentException("Resource cannot be null"); + } + new ResourceContext(resource).process(this); + checkAllConstraints(); + } + + /** + * Read a config file and add its contents. Existing entries with the same name + * will be overwritten. + * + * @param file The config file to read. Must not be null. If this is a directory + * then all files in the directory tree will be read. + * @throws ConfigException If there is an error parsing the config file or one + * of its descendants. + */ + public void read(File file) throws ConfigException { + if (file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + new FileContext(file).process(this); + checkAllConstraints(); + } + + /** + * Read config information from a Reader and add its contents. Existing entries + * with the same name will be overwritten. + * + * @param reader The Reader to read from. Must not be null. + * @param name The name of the reader being read. This is used when reporting + * errors in the file. + * @throws ConfigException If there is an error parsing the config file or one + * of its descendants. + */ + public void read(Reader reader, String name) throws ConfigException { + if (reader == null) { + throw new IllegalArgumentException("Reader cannot be null"); + } + new ReaderContext(reader, name).process(this); + checkAllConstraints(); + } + + private void readWithContext(BufferedReader reader, Context context) throws ConfigException { + String line = ""; + int lineNumber = 0; + String name = context.getName(); + try { + while (line != null) { + try { + line = reader.readLine(); + } catch (IOException e) { + throw new ConfigException(name, e); + } + ++lineNumber; + if (line != null) { + // Strip off everything after a # + int hashIndex = line.indexOf("#"); + if (hashIndex != -1) { + line = line.substring(0, hashIndex).trim(); + } + line = line.trim(); + // Ignore empty lines + if ("".equals(line)) { + continue; + } + LineType.process(line, context, this, lineNumber); + } + } + } finally { + try { + reader.close(); + } catch (IOException e) { + Logger.error("Error reading config", e); + } + } + } + + /** + * Write this config to a PrintWriter. + * + * @param out The PrintWriter to write to. Must not be null. + * @throws IOException If there is an error writing to the stream. + */ + public void write(PrintWriter out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output cannot be null"); + } + for (Map.Entry<String, String> next : data.entrySet()) { + out.print(next.getKey()); + out.print(" : "); + out.println(next.getValue()); + } + } + + /** + * Merge all keys and values from another Config into this one. + * + * @param other The Config to merge from. + */ + public void merge(Config other) { + clearCache(); + this.data.putAll(other.data); + checkAllConstraints(); + } + + /** + * Add a constraint. + * + * @param c The constraint to add. + */ + public void addConstraint(ConfigConstraint c) { + constraints.add(c); + checkConstraint(c); + } + + /** + * Remove a constraint. + * + * @param c The constraint to remove. + */ + public void removeConstraint(ConfigConstraint c) { + constraints.remove(c); + violatedConstraints.remove(c); + } + + /** + * Remove constraints on a key. + * + * @param key The key to remove constraints from. + */ + /* + * public void removeConstraint(String key) { ConstrainedConfigValue c = + * constraints.get(key); if (c != null) { removeConstraint(c); } } + */ + + /** + * Get all violated constraints. + * + * @return All violated constraints. + */ + public Set<ConfigConstraint> getViolatedConstraints() { + return Collections.unmodifiableSet(violatedConstraints); + } + + /** + * Get the constraint for a particular key. + * + * @param key The key to look up. + * @return The constraint for that key, or null if there is no constraint. + */ + /* + * public ConstrainedConfigValue getConstraint(String key) { return + * constraints.get(key); } + */ + + /** + * Find out if a value violates its key's constraints. + * + * @param key The key to look up. + * @return True if the key's value violates the constraints. + */ + /* + * public boolean isConstraintViolated(String key) { ConstrainedConfigValue c = + * getConstraint(key); if (c != null) { return violatedConstraints.contains(c); + * } return false; } + */ + + /** + * Get all keys in this config. + * + * @return An immutable view of all keys. + */ + public Set<String> getAllKeys() { + return Collections.unmodifiableSet(data.keySet()); + } + + /** + * Find out if a key is defined. + * + * @param key The key to test. + * @return True if the key has a non-null value. + */ + public boolean isDefined(String key) { + return data.containsKey(key); + } + + /** + * Get the value of a key as a String. + * + * @param key The key to look up. Must not be null. + * @return The value associated with that key. + * @throws NoSuchConfigOptionException If the key is not defined. + */ + public String getValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!data.containsKey(key)) { + throw new NoSuchConfigOptionException(key); + } + return processDollarNotation(key, data.get(key)); + } + + /** + * Get the value of a key as a String or use a default value. + * + * @param key The key to look up. Must not be null. + * @param defaultValue The default value to return if the key has no defined + * value. + * @return The value associated with that key, or the default value of the key + * has no value. + */ + public String getValue(String key, String defaultValue) { + try { + return getValue(key); + } catch (NoSuchConfigOptionException e) { + return defaultValue; + } + } + + /** + * Get the value of a key as an integer. + * + * @param key The key to look up. Must not be null. + * @return The value associated with that key interpreted as an integer. + * @throws NoSuchConfigOptionException If the key is not defined. + * @throws NumberFormatException If the value of the key cannot be + * interpreted as an integer. + */ + public int getIntValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!noCache.contains(key) && intData.containsKey(key)) { + return intData.get(key); + } + int result = Integer.parseInt(getValue(key)); + intData.put(key, result); + return result; + } + + /** + * Get the value of a key as an integer or use a default value. + * + * @param key The key to look up. Must not be null. + * @param defaultValue The default value to return if the key has no defined + * value. + * @return The value associated with that key interpreted as an integer, or the + * default value of the key has no value. + * @throws NumberFormatException If the value of the key cannot be interpreted + * as an integer. + */ + public int getIntValue(String key, int defaultValue) { + try { + return getIntValue(key); + } catch (NoSuchConfigOptionException e) { + return defaultValue; + } + } + + /** + * Get the value of a key as a floating point number. + * + * @param key The key to look up. Must not be null. + * @return The value associated with that key interpreted as a floating point + * number. + * @throws NoSuchConfigOptionException If the key is not defined. + * @throws NumberFormatException If the value of the key cannot be + * interpreted as a floating point number. + */ + public double getFloatValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!noCache.contains(key) && floatData.containsKey(key)) { + return floatData.get(key); + } + double result = Double.parseDouble(getValue(key)); + floatData.put(key, result); + return result; + } + + /** + * Get the value of a key as a floating point number or use a default value. + * + * @param key The key to look up. Must not be null. + * @param defaultValue The default value to return if the key has no defined + * value. + * @return The value associated with that key interpreted as a floating point + * number, or the default value of the key has no value. + * @throws NumberFormatException If the value of the key cannot be interpreted + * as a floating point number. + */ + public double getFloatValue(String key, double defaultValue) { + try { + return getFloatValue(key); + } catch (NoSuchConfigOptionException e) { + return defaultValue; + } + } + + /** + * Get the value of a key as a boolean. "true", "t", "yes", "y" and "1" (case + * insensitive) are all interpreted as true, all other values are false. + * + * @param key The key to look up. Must not be null. + * @return The value associated with that key interpreted as a boolean. + * @throws NoSuchConfigOptionException If the key is not defined. + */ + public boolean getBooleanValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!noCache.contains(key) && booleanData.containsKey(key)) { + return booleanData.get(key); + } + boolean result = false; + String value = getValue(key); + if ("true".equalsIgnoreCase(value) || "t".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) + || "y".equalsIgnoreCase(value) || "1".equalsIgnoreCase(value)) { + result = true; + } + booleanData.put(key, result); + return result; + } + + /** + * Get the value of a key as a boolean or use a default value. "true", "t", + * "yes", "y" and "1" (case insensitive) are all interpreted as true, all other + * values are false. + * + * @param key The key to look up. Must not be null. + * @param defaultValue The default value to return if the key has no defined + * value. + * @return The value associated with that key interpreted as a boolean, or the + * default value of the key has no value. + */ + public boolean getBooleanValue(String key, boolean defaultValue) { + try { + return getBooleanValue(key); + } catch (NoSuchConfigOptionException e) { + return defaultValue; + } + } + + /** + * Get the value of a key as an array of strings. The value will be split on + * space and comma characters and the resulting list of tokens is returned. + * + * @param key The key to look up. Must not be null. + * @return The value associated with that key interpreted as an array of + * space-and-comma-separated tokens. + * @throws NoSuchConfigOptionException If the key is not defined. + */ + public List<String> getArrayValue(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (!noCache.contains(key) && arrayData.containsKey(key)) { + List<String> entry = arrayData.get(key); + if (entry != null) { + return entry; + } + } + List<String> result = splitArrayValue(getValue(key)); + arrayData.put(key, result); + return result; + } + + /** + * Get the value of a key (or a default value) as an array of strings. The value + * will be split on space and comma characters and the resulting list of tokens + * is returned. + * + * @param key The key to look up. Must not be null. + * @param defaultValue The default value to use if the key is not defined. If + * this is null then null will be returned, otherwise it + * will be split according to the usual rules. + * @return The value associated with the key (or the default value) interpreted + * as an array of space or comma-separated tokens. + */ + public List<String> getArrayValue(String key, String defaultValue) { + try { + return getArrayValue(key); + } catch (NoSuchConfigOptionException e) { + if (defaultValue == null) { + return null; + } + return splitArrayValue(defaultValue); + } + } + + private List<String> splitArrayValue(String value) { + List<String> result = new ArrayList<String>(); + String[] s = value.split(ARRAY_REGEX); + for (String next : s) { + if (!"".equals(next)) { + result.add(next); + } + } + return result; + } + + /** + * Set the value of a key. + * + * @param key The key to set. Must not be null. + * @param value The new value. If this is null then {@link #removeKey(String)} + * is called with the given key. + */ + public void setValue(String key, String value) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (value == null) { + removeKey(key); + return; + } + clearCache(key); + noCache.remove(key); + data.put(key, value); + checkAllConstraints(); + } + + /** + * Append a value to a key. If there is no value for the key then this is + * equivalent to {@link #setValue(String, String)}. This method calls + * {@link #appendValue(String, String, String)} with a space character as the + * separator. + * + * @param key The key to append. + * @param value The value to append. + */ + public void appendValue(String key, String value) { + appendValue(key, value, " "); + } + + /** + * Append a value to a key. If there is no value for the key then this is + * equivalent to {@link #setValue(String, String)}. + * + * @param key The key to append. + * @param value The value to append. + * @param separator A string to add before the new value to separate it from the + * previous value. + */ + public void appendValue(String key, String value, String separator) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + if (value == null) { + throw new IllegalArgumentException("Value cannot be null"); + } + clearCache(key); + if (data.containsKey(key)) { + String old = data.get(key); + data.put(key, old + separator + value); + } else { + data.put(key, value); + } + checkAllConstraints(); + } + + /** + * Set the value of a key as an integer. + * + * @param key The key to set. Must not be null. + * @param value The new value. + */ + public void setIntValue(String key, int value) { + setValue(key, Integer.valueOf(value).toString()); + intData.put(key, value); + } + + /** + * Set the value of a key as a floating point number. + * + * @param key The key to set. Must not be null. + * @param value The new value. + */ + public void setFloatValue(String key, double value) { + setValue(key, Double.valueOf(value).toString()); + floatData.put(key, value); + } + + /** + * Set the value of a key as a boolean. + * + * @param key The key to set. Must not be null. + * @param value The new value. + */ + public void setBooleanValue(String key, boolean value) { + setValue(key, value ? "true" : "false"); + booleanData.put(key, value); + } + + /** + * Remove a key from the config. + * + * @param key The key to remove. Must not be null. + */ + public void removeKey(String key) { + if (key == null) { + throw new IllegalArgumentException("Key cannot be null"); + } + clearCache(key); + noCache.remove(key); + data.remove(key); + } + + /** + * Remove all keys. + */ + public void removeAllKeys() { + data.clear(); + noCache.clear(); + intData.clear(); + floatData.clear(); + booleanData.clear(); + arrayData.clear(); + } + + /** + * Remove all except a set of keys. + * + * @param exceptions The keys to keep. + */ + public void removeExcept(String... exceptions) { + removeExcept(Arrays.asList(exceptions)); + } + + /** + * Remove all except a set of keys. + * + * @param exceptions The keys to keep. + */ + public void removeExcept(Collection<String> exceptions) { + data.keySet().retainAll(exceptions); + noCache.retainAll(exceptions); + } + + /** + * Remove all keys that do not match any of the given regular expressions. + * + * @param exceptions The regular expressions describing keys to keep. + */ + public void removeExceptRegex(Collection<String> exceptions) { + Set<String> toRemove = new HashSet<String>(data.keySet()); + Logger.debug("Removing all except " + exceptions); + for (String exception : exceptions) { + Pattern p = Pattern.compile(exception); + for (String key : data.keySet()) { + if (p.matcher(key).matches()) { + Logger.debug(key + " matches " + exception); + toRemove.remove(key); + } + } + } + Logger.debug("Removing " + toRemove); + for (String next : toRemove) { + data.remove(next); + noCache.remove(next); + } + } + + /** + * Get the random number generator defined by this config. + * + * @return The random number generator. + */ + public Random getRandom() { + synchronized (this) { + if (random == null) { + String className = getValue(Constants.RANDOM_CLASS_KEY, Constants.RANDOM_CLASS_DEFAULT); + String seed = getValue(Constants.RANDOM_SEED_KEY, ""); + try { + Class<? extends Random> clazz = Class.forName(className).asSubclass(Random.class); + Logger.debug("Instantiating random number generator: " + className); + if ("".equals(seed)) { + // Create a default seed from the current time. We don't need cryptographic + // strength here, and using the default constructor will exhaust /dev/random + // quickly, causing long delays on startup. + long mtime = new Date().getTime(); + seed = String.valueOf(mtime); + } + + Logger.debug("Using seed " + seed); + // CHECKSTYLE:OFF:MagicNumber + BigInteger bi = new BigInteger(seed, 16); + // CHECKSTYLE:ON:MagicNumber + // Look for a constructor that takes a byte array + try { + Logger.trace("Trying to find a SeedGenerator constructor"); + Constructor<? extends Random> constructor = clazz.getConstructor(SeedGenerator.class); + random = constructor.newInstance(new StaticSeedGenerator(bi.toByteArray())); + Logger.trace("Success"); + } catch (IllegalAccessException e) { + Logger.trace("SeedGenerator constructor for " + className, e); + } catch (InstantiationException e) { + Logger.trace("SeedGenerator constructor for " + className, e); + } catch (NoSuchMethodException e) { + Logger.trace("SeedGenerator constructor for " + className, e); + } catch (InvocationTargetException e) { + Logger.trace("SeedGenerator constructor for " + className, e); + } + // If that failed try a long argument + if (random == null) { + Logger.trace("Trying to find a long constructor"); + try { + Constructor<? extends Random> constructor = clazz.getConstructor(Long.TYPE); + random = constructor.newInstance(bi.longValue()); + Logger.trace("Success"); + } catch (IllegalAccessException e) { + Logger.trace("Long constructor for " + className, e); + } catch (InstantiationException e) { + Logger.trace("Long constructor for " + className, e); + } catch (NoSuchMethodException e) { + Logger.trace("Long constructor for " + className, e); + } catch (InvocationTargetException e) { + Logger.trace("Long constructor for " + className, e); + } + } + if (random == null) { + // Just instantiate the RNG + try { + Logger.trace("Trying to find no-arg constructor"); + random = clazz.getDeclaredConstructor().newInstance(); + Logger.trace("Success"); + } catch (IllegalAccessException | InstantiationException | NoSuchMethodException + | InvocationTargetException e) { + Logger.trace("No-arg constructor for " + className, e); + } + } + } catch (ClassNotFoundException e) { + Logger.debug("Class not found: " + className); + } + if (random == null) { + // Everything failed + // Just return a sensible RNG + Logger.debug("Using fallback RNG"); + random = new MersenneTwisterRNG(); + } + } + return random; + } + } + + private void clearCache() { + intData.clear(); + floatData.clear(); + booleanData.clear(); + arrayData.clear(); + } + + private void clearCache(String key) { + intData.remove(key); + floatData.remove(key); + booleanData.remove(key); + arrayData.remove(key); + } + + private String processDollarNotation(String key, String value) { + int index = value.indexOf("${"); + if (index == -1) { + return value; + } + noCache.add(key); + int end = value.indexOf("}", index); + int colon = value.indexOf(":", index); + String reference = value.substring(index + 2, end); + String defaultValue = null; + if (colon > index && colon < end) { + reference = value.substring(index + 2, colon); + defaultValue = value.substring(colon + 1, end); + } + StringBuilder result = new StringBuilder(); + result.append(value.substring(0, index)); + result.append(resolveReferences(reference, defaultValue)); + result.append(processDollarNotation(key, value.substring(end + 1))); + return result.toString(); + } + + private String resolveReferences(String s, String defaultValue) { + try { + return getValue(s); + } catch (NoSuchConfigOptionException e) { + if (defaultValue != null) { + return defaultValue; + } + throw e; + } + } + + private void checkAllConstraints() { + violatedConstraints.clear(); + for (ConfigConstraint next : constraints) { + checkConstraint(next); + } + } + + private void checkConstraint(ConfigConstraint c) { + if (c == null) { + return; + } + if (c.isViolated(this)) { + violatedConstraints.add(c); + } else { + violatedConstraints.remove(c); + } + } + + private interface Context { + /** + * Get the name of this context for error logging. + * + * @return The name of this context. + */ + String getName(); + + /** + * Read the context and update a config. + * + * @param config The config to update. + * @throws ConfigException If there is a problem processing the context. + */ + void process(Config config) throws ConfigException; + + /** + * Get a new context for reading an included file. + * + * @param path The path to include. + * @return A new context object. + * @throws ConfigException If there is a problem creating the include context. + */ + Context include(String path) throws ConfigException; + } + + private static class FileContext implements Context { + private File file; + + public FileContext(File file) { + this.file = file; + } + + @Override + public String getName() { + return file.getAbsolutePath(); + } + + @Override + public void process(Config config) throws ConfigException { + if (!file.exists()) { + throw new ConfigException(getName(), "File does not exist"); + } + if (file.isDirectory()) { + for (File next : file.listFiles()) { + new FileContext(next).process(config); + } + return; + } else { + try { + config.readWithContext(new BufferedReader(new FileReader(file)), this); + } catch (IOException e) { + throw new ConfigException(getName(), e); + } + } + } + + @Override + public Context include(String path) throws ConfigException { + File newFile = new File(file.getParentFile(), path); + return new FileContext(newFile); + } + } + + private static class ReaderContext implements Context { + private BufferedReader reader; + private String name; + + public ReaderContext(Reader r, String name) { + this.name = name; + if (r instanceof BufferedReader) { + reader = (BufferedReader) r; + } else { + reader = new BufferedReader(r); + } + } + + @Override + public String getName() { + return name; + } + + @Override + public void process(Config config) throws ConfigException { + config.readWithContext(reader, this); + } + + @Override + public Context include(String path) throws ConfigException { + throw new ConfigException(name, "Cannot process include directives when reading raw data streams"); + } + } + + private static class ResourceContext implements Context { + private String name; + + public ResourceContext(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void process(Config config) throws ConfigException { + InputStream in = getClass().getClassLoader().getResourceAsStream(name); + if (in == null) { + throw new ConfigException(name, "Resource not found"); + } + config.readWithContext(new BufferedReader(new InputStreamReader(in)), this); + } + + @Override + public Context include(String path) throws ConfigException { + return new ResourceContext(path); + } + } + + private static class StaticSeedGenerator implements SeedGenerator { + private byte[] data; + + StaticSeedGenerator(byte[] data) { + this.data = data; + } + + @Override + public byte[] generateSeed(int length) { + byte[] result = new byte[length]; + System.arraycopy(data, 0, result, 0, Math.min(data.length, result.length)); + return result; + } + } + + private enum LineType { + INCLUDE { + @Override + public void process(Matcher matcher, Context context, Config config, int lineNumber) throws ConfigException { + String includeName = matcher.group(1).trim(); + if ("".equals(includeName)) { + throw new ConfigException(context.getName(), lineNumber, "Empty include directive"); + } + Logger.trace("Reading included config '" + includeName + "'"); + Context newContext = context.include(includeName); + newContext.process(config); + } + + @Override + protected String getRegex() { + return "^!include\\s*(.*)"; + } + }, + + ADDITIVE { + @Override + public void process(Matcher matcher, Context context, Config config, int lineNumber) throws ConfigException { + String key = matcher.group(1).trim(); + String value = matcher.group(2).trim(); + if ("".equals(key)) { + throw new ConfigException(context.getName(), lineNumber, "Empty key"); + } + if ("".equals(value)) { + throw new ConfigException(context.getName(), lineNumber, "Empty value"); + } + String existing = config.getValue(key, null); + if (existing != null && !"".equals(existing)) { + Logger.trace("Appending '" + value + "' to '" + key + "'"); + value = existing + " " + value; + } else { + Logger.trace("Setting '" + key + "' to '" + value + "'"); + } + config.setValue(key, value); + } + + @Override + protected String getRegex() { + return "^([^+]*)(?:\\+:|=)(.*)"; + } + }, + + NORMAL { + @Override + public void process(Matcher matcher, Context context, Config config, int lineNumber) throws ConfigException { + String key = matcher.group(1).trim(); + String value = matcher.group(2).trim(); + if ("".equals(key)) { + throw new ConfigException(context.getName(), lineNumber, "Empty key"); + } + if ("".equals(value)) { + throw new ConfigException(context.getName(), lineNumber, "Empty value"); + } + Logger.trace("Setting '" + key + "' to '" + value + "'"); + if (config.isDefined(key) && !value.equals(config.getValue(key))) { + Logger.warn("Redefining config key '" + key + "' as '" + value + "'"); + } + config.setValue(key, value); + } + + @Override + protected String getRegex() { + return "^([^:=]*)(?::|=)(.*)"; + } + }; + + private Pattern pattern; + + private LineType() { + pattern = Pattern.compile(getRegex()); + } + + public Pattern getPattern() { + return pattern; + } + + protected abstract String getRegex(); + + protected abstract void process(Matcher matcher, Context context, Config config, int lineNumber) + throws ConfigException; + + public static void process(String line, Context context, Config config, int lineNumber) throws ConfigException { + for (LineType next : values()) { + Matcher matcher = next.getPattern().matcher(line); + if (matcher.matches()) { + next.process(matcher, context, config, lineNumber); + return; + } + } + throw new ConfigException(context.getName(), lineNumber, "Unrecognised config option: '" + line + "'"); + } + } + + public Map<String, String> getData() { + return data; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/ConfigConstraint.java b/modules/rescuecore2/src/rescuecore2/config/ConfigConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..cba3d60a22a4f3985a8e349c7cef5497877cb0a7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/ConfigConstraint.java @@ -0,0 +1,27 @@ +package rescuecore2.config; + +import java.util.Set; + +/** + Interface for specifying constraints on a Config. +*/ +public interface ConfigConstraint { + /** + Check if this constraint has been violated. + @param config The Config object. + @return True iff the constraint has been violated. + */ + boolean isViolated(Config config); + + /** + Get a description of this constraint suitable for use in tooltips. + @return A description of the constraint. + */ + String getDescription(); + + /** + Get the set of keys this constriant applies to. + @return The set of relevant keys. + */ + Set<String> getKeys(); +} diff --git a/modules/rescuecore2/src/rescuecore2/config/ConfigException.java b/modules/rescuecore2/src/rescuecore2/config/ConfigException.java new file mode 100644 index 0000000000000000000000000000000000000000..82a1d1d5eb49a4c17cada7fc7a6473520bfc064b --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/ConfigException.java @@ -0,0 +1,82 @@ +package rescuecore2.config; + +/** + Exception class for problems with config files. + */ +public class ConfigException extends Exception { + /** + Construct an exception with just a filename, no message or underlying cause. + @param filename The name of the config file that caused the problem. + */ + public ConfigException(final String filename) { + this(filename, -1, "Unknown error", null); + } + + /** + Construct an exception with a filename and error message. + @param filename The name of the config file that caused the problem. + @param msg A message describing the problem. + */ + public ConfigException(final String filename, final String msg) { + this(filename, -1, msg, null); + } + + /** + Construct an exception with a filename and an underlying cause. + @param filename The name of the config file that caused the problem. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final Throwable cause) { + this(filename, -1, cause.toString(), cause); + } + + /** + Construct an exception with a filename and a line number. + @param filename The name of the config file that caused the problem. + @param linenumber The line number where the problem occurred. + */ + public ConfigException(final String filename, final int linenumber) { + this(filename, linenumber, "Unknown error", null); + } + + /** + Construct an exception with a filename, line number and error message. + @param filename The name of the config file that caused the problem. + @param linenumber The line number where the problem occurred. + @param msg A message describing the problem. + */ + public ConfigException(final String filename, final int linenumber, final String msg) { + this(filename, linenumber, msg, null); + } + + /** + Construct an exception with a filename, line number and underlying cause. + @param filename The name of the config file that caused the problem. + @param linenumber The line number where the problem occurred. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final int linenumber, final Throwable cause) { + this(filename, linenumber, cause.toString(), cause); + } + + /** + Construct an exception with a filename, error message and underlying cause. + @param filename The name of the config file that caused the problem. + @param msg A message describing the problem. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final String msg, final Throwable cause) { + this(filename, -1, msg, cause); + } + + /** + Construct an exception with a filename, error message and underlying cause. + @param filename The name of the config file that caused the problem. + @param lineNumber The line number where the problem occurred. + @param msg A message describing the problem. + @param cause The underlying cause of this exception. + */ + public ConfigException(final String filename, final int lineNumber, final String msg, final Throwable cause) { + super((filename == null ? "" : (filename + ": ")) + (lineNumber > 0 ? ("Line " + lineNumber + ": ") : "") + (msg == null ? "" : msg), cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/DiscreteValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/DiscreteValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..43aeb018e9f30b7f74137b3b675f6075627a500b --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/DiscreteValueConstraint.java @@ -0,0 +1,40 @@ +package rescuecore2.config; + +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; + +/** + A config value constraint that requires the value to be one of a discrete set of values. +*/ +public class DiscreteValueConstraint extends AbstractValueConstraint { + private Set<String> allowed; + + /** + Construct a DiscreteConstrainedConfigValue. + @param key The key this constraint applies to. + @param allowed The set of allowed values. + */ + public DiscreteValueConstraint(String key, Set<String> allowed) { + super(key); + this.allowed = new HashSet<String>(allowed); + } + + @Override + public String getDescription() { + StringBuilder result = new StringBuilder(); + result.append("Must be one of: "); + for (Iterator<String> it = allowed.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + return result.toString(); + } + + @Override + public boolean isValid(String value, Config config) { + return allowed.contains(value); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/FloatValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/FloatValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..e2d778575c23252fa4f6051cfe3db6fb803b293f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/FloatValueConstraint.java @@ -0,0 +1,58 @@ +package rescuecore2.config; + +/** + A floating-point number constraint on a config value. +*/ +public class FloatValueConstraint extends AbstractValueConstraint { + private double min; + private double max; + + /** + Construct a FloatConstrainedConfigValue that has no minimum or maximum. + @param key The key this constraint applies to. + */ + public FloatValueConstraint(String key) { + this(key, Double.NaN, Double.NaN); + } + + /** + Construct a FloatConstrainedConfigValue that has a particular range. + @param key The key this constraint applies to. + @param min The minimum value of the config entry. + @param max The maximum value of the config entry. + */ + public FloatValueConstraint(String key, double min, double max) { + super(key); + this.min = min; + this.max = max; + } + + @Override + public String getDescription() { + boolean minSpecified = !Double.isNaN(min) && !Double.isInfinite(min); + boolean maxSpecified = !Double.isNaN(max) && !Double.isInfinite(max); + if (!minSpecified && !maxSpecified) { + return "Must be a number"; + } + if (!minSpecified) { + return "Must be a number less than or equal to " + max; + } + if (!maxSpecified) { + return "Must be a number greater than or equal to " + min; + } + return "Must be a number between " + min + " and " + max + " inclusive"; + } + + @Override + public boolean isValid(String value, Config config) { + try { + double d = Double.parseDouble(value); + boolean minSpecified = !Double.isNaN(min) && !Double.isInfinite(min); + boolean maxSpecified = !Double.isNaN(max) && !Double.isInfinite(max); + return !((minSpecified && d < min) || (maxSpecified && d > max)); + } + catch (NumberFormatException e) { + return false; + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/IntegerValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/IntegerValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..454a4f1d365a28cc3d5d9d983ab1e4c7faf0948d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/IntegerValueConstraint.java @@ -0,0 +1,54 @@ +package rescuecore2.config; + +/** + An integer constraint on a config value. +*/ +public class IntegerValueConstraint extends AbstractValueConstraint { + private int min; + private int max; + + /** + Construct an IntegerValueConstraint that has no minimum or maximum. + @param key The key this constraint applies to. + */ + public IntegerValueConstraint(String key) { + this(key, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + /** + Construct an IntegerValueConstraint that has a particular range. + @param key The key this constraint applies to. + @param min The minimum value of the config entry. + @param max The maximum value of the config entry. + */ + public IntegerValueConstraint(String key, int min, int max) { + super(key); + this.min = min; + this.max = max; + } + + @Override + public String getDescription() { + if (min == Integer.MIN_VALUE && max == Integer.MAX_VALUE) { + return "Must be an integer"; + } + if (min == Integer.MIN_VALUE) { + return "Must be an integer less than or equal to " + max; + } + if (max == Integer.MAX_VALUE) { + return "Must be an integer greater than or equal to " + min; + } + return "Must be an integer between " + min + " and " + max + " inclusive"; + } + + @Override + public boolean isValid(String value, Config config) { + try { + int i = Integer.parseInt(value); + return (i >= min && i <= max); + } + catch (NumberFormatException e) { + return false; + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/NoSuchConfigOptionException.java b/modules/rescuecore2/src/rescuecore2/config/NoSuchConfigOptionException.java new file mode 100644 index 0000000000000000000000000000000000000000..aaa251577ac268b418efd344537913a2b37576f2 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/NoSuchConfigOptionException.java @@ -0,0 +1,14 @@ +package rescuecore2.config; + +/** + An unchecked exception that is thrown when an application attempts to read an undefined config option. + */ +public class NoSuchConfigOptionException extends RuntimeException { + /** + Construct an exception. + @param key The name of the key that does not exist in the config. + */ + public NoSuchConfigOptionException(final String key) { + super(key); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/config/ValueConstraint.java b/modules/rescuecore2/src/rescuecore2/config/ValueConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..6137506785ce7f08755ac1afed9d5fa506f26752 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/config/ValueConstraint.java @@ -0,0 +1,26 @@ +package rescuecore2.config; + +/** + Interface for specifying constraints on a config value. +*/ +public interface ValueConstraint extends ConfigConstraint { + /** + Get the key this restraint refers to. + @return The config key. + */ + String getKey(); + + /** + Check if a value is valid. + @param value The value to check. + @param config The Config object. + @return True iff the value is valid. + */ + boolean isValid(String value, Config config); + + /** + Find out if an undefined value is counted as valid or invalid. + @return True if an undefined value should be treated as valid, false if invalid. + */ + boolean undefinedIsValid(); +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/AbstractConnection.java b/modules/rescuecore2/src/rescuecore2/connection/AbstractConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..d69bdf811b5818d2b744a36abfcb24eada0829bf --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/AbstractConnection.java @@ -0,0 +1,297 @@ +package rescuecore2.connection; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.readMessage; +import static rescuecore2.misc.EncodingTools.writeMessage; + +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.misc.WorkerThread; +import rescuecore2.registry.Registry; +import rescuecore2.log.Logger; + +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collection; +import java.util.Collections; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + Abstract base class for Connection implementations. + */ +public abstract class AbstractConnection implements Connection { + private static final int BROADCAST_WAIT = 10000; + + private List<ConnectionListener> listeners; + private List<Message> toSend; + private MessageBroadcastThread broadcast; + private Registry registry; + + private boolean logBytes; + private String name; + + private volatile State state; + + private final Object stateLock = new Object(); + + /** + Construct an abstract connection. + */ + protected AbstractConnection() { + listeners = new ArrayList<ConnectionListener>(); + toSend = new LinkedList<Message>(); + logBytes = false; + state = State.NOT_STARTED; + registry = Registry.SYSTEM_REGISTRY; + name = toString(); + } + + @Override + public void setLogBytes(boolean enabled) { + logBytes = enabled; + } + + @Override + public final void startup() { + synchronized (stateLock) { + if (state == State.NOT_STARTED) { + Registry old = Registry.getCurrentRegistry(); + Registry.setCurrentRegistry(registry); + try { + broadcast = new MessageBroadcastThread(); + broadcast.start(); + startupImpl(); + state = State.STARTED; + } + finally { + Registry.setCurrentRegistry(old); + } + } + } + } + + @Override + public final void shutdown() { + synchronized (stateLock) { + if (state == State.STARTED) { + try { + broadcast.kill(); + } + catch (InterruptedException e) { + Logger.error("AbstractConnection interrupted while shutting down broadcast thread", e); + } + shutdownImpl(); + state = State.SHUTDOWN; + } + } + } + + @Override + public boolean isAlive() { + boolean result; + synchronized (stateLock) { + result = state == State.STARTED; + } + return result; + } + + @Override + public void addConnectionListener(ConnectionListener l) { + synchronized (listeners) { + listeners.add(l); + } + } + + @Override + public void removeConnectionListener(ConnectionListener l) { + synchronized (listeners) { + listeners.remove(l); + } + } + + @Override + public void sendMessage(Message msg) throws ConnectionException { + if (msg == null) { + throw new IllegalArgumentException("Message cannot be null"); + } + sendMessages(Collections.singleton(msg)); + } + + @Override + public void sendMessages(Collection<? extends Message> messages) throws ConnectionException { + if (messages == null) { + throw new IllegalArgumentException("Messages cannot be null"); + } + synchronized (stateLock) { + if (state == State.NOT_STARTED) { + throw new ConnectionException("Connection has not been started"); + } + if (state == State.SHUTDOWN) { + throw new ConnectionException("Connection has been shut down"); + } + if (!isAlive()) { + throw new ConnectionException("Connection is dead"); + } + } + try { +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// for (Message next : messages) { +// writeMessage(next, out); +// } +// // Add a zero to indicate no more messages +// writeInt32(0, out); +// // Send the bytes +// if (logBytes) { +// ByteLogger.log(out.toByteArray()); +// } +// sendBytes(out.toByteArray()); + for ( Message msg : messages ) { +// builder.addMessages(MsgProtoBuf.setMessageProto(msg)); + sendMessageProto( msg.toMessageProto()); + } + } + catch (IOException e) { + throw new ConnectionException(e); + } + } + + @Override + public void setRegistry(Registry r) { + this.registry = r; + } + + @Override + public Registry getRegistry() { + return registry; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String newName) { + this.name = newName; + } + + @Override + public String toString() { + if (name == null) { + return super.toString(); + } + return name; + } + + /** + Send some bytes to the other end of the connection. + @param b The bytes to send. + @throws IOException If the data cannot be sent. + */ +// protected abstract void sendBytes(byte[] b) throws IOException; + protected abstract void sendMessageProto(MessageProto messageProto) throws IOException; + + /** + Perform startup actions. This will only ever be called once. + */ + protected abstract void startupImpl(); + + /** + Perform shutdown actions. This will only ever be called once. + */ + protected abstract void shutdownImpl(); + + /** + Process some bytes that were received. The default implementation will use the Registry to decode all messages in the buffer and send them to listeners. + @param b The received bytes. + */ +// protected void bytesReceived(byte[] b) { +// InputStream decode = new ByteArrayInputStream(b); +// Message m = null; +// try { +// do { +// m = readMessage(decode); +// if (m != null) { +// fireMessageReceived(m); +// } +// } while (m != null); +// } +// catch (IOException e) { +// Logger.error("AbstractConnection error reading message", e); +// ByteLogger.log(b, this.toString()); +// } +// // CHECKSTYLE:OFF:IllegalCatch +// catch (RuntimeException e) { +// Logger.error("AbstractConnection error reading message", e); +// ByteLogger.log(b, this.toString()); +// throw e; +// } +// catch (Error e) { +// Logger.error("AbstractConnection error reading message", e); +// ByteLogger.log(b, this.toString()); +// throw e; +// } +// // CHECKSTYLE:ON:IllegalCatch +// } + protected void messageProtoReceived(MessageProto messageProto) { + fireMessageReceived(MsgProtoBuf.messageProto2Message(messageProto)); +} + /** + Fire a messageReceived event to all registered listeners. + @param m The message that was received. + */ + protected void fireMessageReceived(Message m) { + synchronized (toSend) { + toSend.add(m); + toSend.notifyAll(); + } + } + + /** + The state of this connection: either not yet started, started or shut down. + */ + protected enum State { + /** CHECKSTYLE:OFF:JavadocVariableCheck. */ + NOT_STARTED, + STARTED, + SHUTDOWN; + /** CHECKSTYLE:ON:JavadocVariableCheck. */ + } + + /** + Worker thread that broadcasts messages to listeners. + */ + private class MessageBroadcastThread extends WorkerThread { + @Override + protected boolean work() throws InterruptedException { + Message m = null; + synchronized (toSend) { + if (toSend.isEmpty()) { + toSend.wait(BROADCAST_WAIT); + return true; + } + else { + m = toSend.remove(0); + } + } + if (m == null) { + return true; + } + ConnectionListener[] l; + synchronized (listeners) { + l = new ConnectionListener[listeners.size()]; + listeners.toArray(l); + } + for (ConnectionListener next : l) { + next.messageReceived(AbstractConnection.this, m); + } + return true; + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/ByteLogger.java b/modules/rescuecore2/src/rescuecore2/connection/ByteLogger.java new file mode 100644 index 0000000000000000000000000000000000000000..37dbed50d62caa92d606c29b63bab7a044b7b885 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/ByteLogger.java @@ -0,0 +1,49 @@ +package rescuecore2.connection; + +/** + Utility class for logging raw bytes. Handy for debugging. + */ +public final class ByteLogger { + private static final Object LOCK = new Object(); + + /** Utility class: private constructor. */ + private ByteLogger() {} + + /** + Log a byte array. + @param bytes The bytes to log. + */ + public static void log(byte[] bytes) { + log(bytes, null); + } + + /** + Log a byte array. + @param bytes The bytes to log. + @param header A header to print out. + */ + public static void log(byte[] bytes, String header) { + synchronized (LOCK) { + if (header != null) { + System.err.println(header); + } + // CHECKSTYLE:OFF:MagicNumber + System.err.println(bytes.length + "-byte message"); + System.err.println("Offset Hex Int"); + for (int i = 0; i < bytes.length; i += 4) { + byte b1 = bytes[i]; + byte b2 = (i + 1) >= bytes.length ? 0 : bytes[i + 1]; + byte b3 = (i + 2) >= bytes.length ? 0 : bytes[i + 2]; + byte b4 = (i + 3) >= bytes.length ? 0 : bytes[i + 3]; + int v1 = (int)(bytes[i] & 0xFF); + int v2 = (i + 1) >= bytes.length ? 0 : (int)(bytes[i + 1] & 0xFF); + int v3 = (i + 2) >= bytes.length ? 0 : (int)(bytes[i + 2] & 0xFF); + int v4 = (i + 3) >= bytes.length ? 0 : (int)(bytes[i + 3] & 0xFF); + System.err.printf("%1$-8d 0x%2$02x%3$02x%4$02x%5$02x %6$3d %7$3d %8$3d %9$3d%n", + i, b1, b2, b3, b4, + v1, v2, v3, v4); + } + // CHECKSTYLE:ON:MagicNumber + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/Connection.java b/modules/rescuecore2/src/rescuecore2/connection/Connection.java new file mode 100644 index 0000000000000000000000000000000000000000..0b467fd2bc69a1fdb221060cd3b2cc5cde07cd6f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/Connection.java @@ -0,0 +1,83 @@ +package rescuecore2.connection; + +import rescuecore2.messages.Message; +import rescuecore2.registry.Registry; + +import java.util.Collection; + +/** + Top-level interface for a communication interface between simulator components. + */ +public interface Connection { + /** + Send a message across this connection. + @param msg The message to send. + @throws ConnectionException If the connection has not been started up or has been shut down, or if there is an error sending the message. + */ + void sendMessage(Message msg) throws ConnectionException; + + /** + Send a set of messages across this connection. + @param messages The messages to send. + @throws ConnectionException If the connection has not been started up or has been shut down, or if there is an error sending the message. + */ + void sendMessages(Collection<? extends Message> messages) throws ConnectionException; + + /** + Add a ConnectionListener. This listener will be notified when messages arrive on this connection. + @param l The listener to add. + */ + void addConnectionListener(ConnectionListener l); + + /** + Remove a ConnectionListener. + @param l The listener to remove. + */ + void removeConnectionListener(ConnectionListener l); + + /** + Start this connection up. Connections are not available for use until started. + */ + void startup(); + + /** + Find out if this connection is still alive. The connection is alive if it has been started, not stopped, and no fatal errors have occurred. + @return True if this connection is alive and is still able to send/receive messages, false otherwise. + */ + boolean isAlive(); + + /** + Shut this connection down. + */ + void shutdown(); + + /** + Turn byte-level logging on or off. + @param enabled Whether to enable byte-level logging. + */ + void setLogBytes(boolean enabled); + + /** + Set the name for this connection. + @param name The new name. + */ + void setName(String name); + + /** + Get the name for this connection. + @return The name. + */ + String getName(); + + /** + Set the Registry that this connection should use for decoding messages and entities. + @param registry The new Registry to use. + */ + void setRegistry(Registry registry); + + /** + Get the Registry that this connection is using for decoding messages and entities. + @return The Registry in use. + */ + Registry getRegistry(); +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/ConnectionException.java b/modules/rescuecore2/src/rescuecore2/connection/ConnectionException.java new file mode 100644 index 0000000000000000000000000000000000000000..77a9fa3b8b0c7e60566c25882690858dfcd2ab51 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/ConnectionException.java @@ -0,0 +1,38 @@ +package rescuecore2.connection; + +/** + An error with a Connection. This usually represents an illegal state such as sending a message after shutdown. + */ +public class ConnectionException extends Exception { + /** + Construct a ConnectionException with no explanation. + */ + public ConnectionException() { + super(); + } + + /** + Construct a ConnectionException with an error message. + @param msg The error message. + */ + public ConnectionException(String msg) { + super(msg); + } + + /** + Construct a ConnectionException that has an underlying cause. + @param cause The cause of this exception. + */ + public ConnectionException(Throwable cause) { + super(cause); + } + + /** + Construct a ConnectionException with an error message and an underlying cause. + @param msg The error message. + @param cause The cause of this exception. + */ + public ConnectionException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/ConnectionListener.java b/modules/rescuecore2/src/rescuecore2/connection/ConnectionListener.java new file mode 100644 index 0000000000000000000000000000000000000000..615618df52b76fc9d168d5dc30742a110f4b6d3c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/ConnectionListener.java @@ -0,0 +1,15 @@ +package rescuecore2.connection; + +import rescuecore2.messages.Message; + +/** + Interface for classes interested in hearing about events on Connection objects. + */ +public interface ConnectionListener { + /** + Notification that a message was received. + @param c The Connection that the message arrived on. + @param msg The Message that arrived. + */ + void messageReceived(Connection c, Message msg); +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/ConnectionManager.java b/modules/rescuecore2/src/rescuecore2/connection/ConnectionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..c14add0c303cd4838878304874c1638c1247479d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/ConnectionManager.java @@ -0,0 +1,148 @@ +package rescuecore2.connection; + +import java.net.ServerSocket; +import java.net.Socket; +import java.io.InterruptedIOException; +import java.io.IOException; + +import java.util.Set; +import java.util.HashSet; + +import rescuecore2.misc.WorkerThread; +import rescuecore2.registry.Registry; +import rescuecore2.log.Logger; + +/** + A class for managing incoming connections. + */ +public class ConnectionManager { + private Set<Reader> readers; + private boolean shutdown; + + private final Object lock = new Object(); + + /** + Construct a new ConnectionManager. + */ + public ConnectionManager() { + readers = new HashSet<Reader>(); + shutdown = false; + } + + /** + Listen for connections on a particular port. + @param port The port to listen on. + @param registry The registry to install in new connections. + @param listener A ConnectionManagerListener that will be informed of new connections. + @throws IOException If there is a problem listening on the port. + */ + public void listen(int port, Registry registry, ConnectionManagerListener listener) throws IOException { + synchronized (lock) { + if (shutdown) { + throw new IOException("Connection manager has been shut down"); + } + Logger.info("Listening for connections on port " + port); + ServerSocket socket = new ServerSocket(port); + socket.setSoTimeout(1000); + socket.setReuseAddress(true); + Reader r = new Reader(socket, registry, listener); + readers.add(r); + r.start(); + + ServerSocket socketJson = new ServerSocket(port+1); + socketJson.setSoTimeout(1000); + socketJson.setReuseAddress(true); + ReaderJson rjson = new ReaderJson(socketJson, registry, listener); + readers.add(rjson); + rjson.start(); + } + } + + /** + Shut down this ConnectionManager. + */ + public void shutdown() { + synchronized (lock) { + if (shutdown) { + return; + } + shutdown = true; + } + for (Reader next : readers) { + try { + next.kill(); + } + catch (InterruptedException e) { + Logger.error("ConnectionManager interrupted while shutting down read threads", e); + } + } + } + + /** + Find out if this ConnectionManager is alive. + @return True if this manager has not been shut down. + */ + public boolean isAlive() { + synchronized (lock) { + return !shutdown; + } + } + + private class Reader extends WorkerThread { + private ServerSocket socket; + private Registry registry; + private ConnectionManagerListener callback; + + public Reader(ServerSocket socket, Registry registry, ConnectionManagerListener callback) { + this.socket = socket; + this.registry = registry; + this.callback = callback; + } + + protected TCPConnection getTCPConnection(Socket s) throws IOException { + return new TCPConnection(s); + } + @Override + protected boolean work() { + try { + Socket s = socket.accept(); + TCPConnection conn = getTCPConnection(s); + if (ConnectionManager.this.isAlive()) { + conn.setRegistry(registry); + callback.newConnection(conn); + conn.startup(); + } + } + // CHECKSTYLE:OFF:EmptyBlock OK here + catch (InterruptedIOException e) { + // Ignore + } + // CHECKSTYLE:ON:EmptyBlock + catch (IOException e) { + Logger.error("Error listening for connection", e); + } + return true; + } + + @Override + protected void cleanup() { + try { + socket.close(); + } + catch (IOException e) { + Logger.error("Error closing server socket", e); + } + } + } + + private class ReaderJson extends Reader { + + public ReaderJson(ServerSocket socket, Registry registry, ConnectionManagerListener callback) { + super(socket, registry, callback); + } + @Override + protected TCPConnection getTCPConnection(Socket s) throws IOException { + return new JsonTCPConnection(s); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/ConnectionManagerListener.java b/modules/rescuecore2/src/rescuecore2/connection/ConnectionManagerListener.java new file mode 100644 index 0000000000000000000000000000000000000000..14a88932cf7a7cf593d1b123f831a3d2f6e85059 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/ConnectionManagerListener.java @@ -0,0 +1,12 @@ +package rescuecore2.connection; + +/** + Interface for objects interested in hearing about new connections to a ConnectionManager. + */ +public interface ConnectionManagerListener { + /** + Notification that a new connection has been made. This connection will be initialised but not started. + @param c The new connection. + */ + void newConnection(Connection c); +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/JsonTCPConnection.java b/modules/rescuecore2/src/rescuecore2/connection/JsonTCPConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..1743d225253dcf4d428fa668217635028516e84f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/JsonTCPConnection.java @@ -0,0 +1,34 @@ +package rescuecore2.connection; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; + +import com.google.protobuf.util.JsonFormat; + +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +public class JsonTCPConnection extends TCPConnection{ + + private OutputStreamWriter writer; + private InputStreamReader reader; + public JsonTCPConnection(Socket s) throws IOException { + super(s); + writer=new OutputStreamWriter(out); + reader = new InputStreamReader(in); + } + + @Override + protected void serializeMessageProto(MessageProto messageProto) throws IOException { + writer.append(JsonFormat.printer().print(messageProto)); + } + @Override + protected MessageProto deserializeMessageProto() throws IOException { + MessageProto.Builder builder=MessageProto.newBuilder(); + JsonFormat.parser().merge(reader, builder); + return builder.build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/MessageQueueConnectionListener.java b/modules/rescuecore2/src/rescuecore2/connection/MessageQueueConnectionListener.java new file mode 100644 index 0000000000000000000000000000000000000000..f35a584fa538b36bc3d0e70fee0aff68e22e3e71 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/MessageQueueConnectionListener.java @@ -0,0 +1,61 @@ +package rescuecore2.connection; + +import rescuecore2.messages.Message; +import java.util.Deque; +import java.util.ArrayDeque; + +/** + ConnectionListener implementation that maintains a queue of messages. + */ +public class MessageQueueConnectionListener implements ConnectionListener { + private Deque<Message> messages; + + /** + Construct a MessageQueueConnectionListener. + */ + public MessageQueueConnectionListener() { + messages = new ArrayDeque<Message>(); + } + + @Override + public void messageReceived(Connection c, Message msg) { + synchronized (messages) { + messages.addLast(msg); + messages.notifyAll(); + } + } + + /** + Wait for a message. + @return The next message. + @throws InterruptedException If this thread is interrupted. + */ + public Message waitForMessage() throws InterruptedException { + synchronized (messages) { + while (messages.isEmpty()) { + messages.wait(); + } + return messages.removeFirst(); + } + } + + /** + Wait for a message. + @param timeout The maximum amount of time to wait in milliseconds. + @return The next message or null if the timeout is reached. + @throws InterruptedException If this thread is interrupted. + */ + public Message waitForMessage(long timeout) throws InterruptedException { + synchronized (messages) { + long end = System.currentTimeMillis() + timeout; + while (messages.isEmpty()) { + long now = System.currentTimeMillis(); + if (now >= end) { + return null; + } + messages.wait(end - now); + } + return messages.removeFirst(); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/StreamConnection.java b/modules/rescuecore2/src/rescuecore2/connection/StreamConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..d2e62fd199bd2b142f5f79a374b9d624c8c760ce --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/StreamConnection.java @@ -0,0 +1,225 @@ +package rescuecore2.connection; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.EOFException; +import java.io.InterruptedIOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.List; + +import org.tukaani.xz.LZMA2Options; +import org.tukaani.xz.XZ; +import org.tukaani.xz.XZInputStream; +import org.tukaani.xz.XZOutputStream; + +import java.util.LinkedList; + +import rescuecore2.misc.WorkerThread; +import rescuecore2.misc.EncodingTools; +import rescuecore2.misc.Pair; +import rescuecore2.registry.Registry; +import rescuecore2.log.Logger; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * Connection implementation that uses InputStreams and OutputStreams. + */ +public class StreamConnection extends AbstractConnection { + private static final int SEND_WAIT = 10000; + + protected InputStream in; + protected OutputStream out; + private ReadThread readThread; + private WriteThread writeThread; + private List<MessageProto> toWrite; + + protected static final boolean GZIP_ENABLE = false; + + /** + * Create a StreamConnection. + * + * @param in The InputStream to read. + * @param out The OutputStream to write to. + * @throws IOException + */ + public StreamConnection(InputStream in, OutputStream out) + throws IOException { + super(); + if (GZIP_ENABLE) { + LZMA2Options options = new LZMA2Options(); + options.setPreset(7); // play with this number: 6 is default but 7 works better for mid sized archives ( > 8mb) + this.out = new XZOutputStream(out,options); + this.out.flush(); + this.in = new XZInputStream(in); + } else { + this.in = in; + this.out = out; + } + toWrite = new LinkedList<MessageProto>(); + } + + @Override + protected void startupImpl() { + Logger.debug("Starting " + this + ". Registry: " + + Registry.getCurrentRegistry()); + readThread = new ReadThread(); + writeThread = new WriteThread(); + readThread.start(); + writeThread.start(); + } + + @Override + public boolean isAlive() { + return super.isAlive() && readThread.isRunning() + && writeThread.isRunning(); + } + + @Override + protected void shutdownImpl() { + Logger.info("Shutting down " + this); + try { + readThread.kill(); + } catch (InterruptedException e) { + Logger.error( + "StreamConnection interrupted while shutting down read thread", + e); + } + try { + writeThread.kill(); + } catch (InterruptedException e) { + Logger.error( + "StreamConnection interrupted while shutting down write thread", + e); + } + try { + out.flush(); + } catch (IOException e) { + Logger.error("StreamConnection error flushing output buffer", e); + } + try { + out.close(); + } catch (IOException e) { + Logger.error("StreamConnection error closing output buffer", e); + } + try { + in.close(); + } catch (IOException e) { + Logger.error("StreamConnection error closing input buffer", e); + } + } + +// @Override +// protected void sendBytes(byte[] b) throws IOException { +// synchronized (toWrite) { +// toWrite.add(b); +// toWrite.notifyAll(); +// } +// } + + protected void serializeMessageProto(MessageProto messageProto) + throws IOException { +// messageProto.writeDelimitedTo(out); + byte[] bytes = messageProto.toByteArray(); + EncodingTools.writeInt32(bytes.length, out); + out.write(bytes); + + } + + protected MessageProto deserializeMessageProto() throws IOException { +// return MessageProto.parseDelimitedFrom(in); + int size = EncodingTools.readInt32(in); + byte[] bytes = in.readNBytes(size); + return MessageProto.parseFrom(bytes); + } + + protected void sendMessageProto(MessageProto messageProto) + throws IOException { + synchronized (toWrite) { + toWrite.add(messageProto); + toWrite.notifyAll(); + } + } + + /** + * Worker thread that reads from the input stream. + */ + private class ReadThread extends WorkerThread { + @Override + protected boolean work() { +// byte[] buffer = {}; +// int size = -1; + try { +// size = readInt32(in); +// if (size > 0) { +// buffer = readBytes(size, in); +// bytesReceived(buffer); +// } +// return true; + MessageProto messageProto = deserializeMessageProto(); + messageProtoReceived(messageProto); + return true; + } catch (InterruptedIOException e) { + return true; + } catch (EOFException e) { + return false; + } catch (IOException e) { + Logger.error("Error reading from StreamConnection " + + StreamConnection.this, e); + return false; + } + } + } + + /** + * Worker thread that writes to the output stream. + */ + private class WriteThread extends WorkerThread { + @Override + protected boolean work() throws InterruptedException { + MessageProto messageProto = null; + synchronized (toWrite) { + if (toWrite.isEmpty()) { + toWrite.wait(SEND_WAIT); + return true; + } else { + messageProto = toWrite.remove(0); + } + } + if (messageProto == null) { + return true; + } + try { +// writeInt32(bytes.length, out); +// out.write(bytes); + serializeMessageProto(messageProto); + out.flush(); + return true; + } catch (IOException e) { + Logger.error("Error writing to StreamConnection " + + StreamConnection.this, e); + return false; + } + } + } + + /** + * Create and start a pair of connections that pipe input to each other. + * + * @return A pair of connections. + */ + public static Pair<Connection, Connection> createConnectionPair() { + try { + PipedInputStream in1 = new PipedInputStream(); + PipedInputStream in2 = new PipedInputStream(); + PipedOutputStream out1 = new PipedOutputStream(in2); + PipedOutputStream out2 = new PipedOutputStream(in1); + Connection c1 = new StreamConnection(in1, out1); + Connection c2 = new StreamConnection(in2, out2); + return new Pair<Connection, Connection>(c1, c2); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/connection/TCPConnection.java b/modules/rescuecore2/src/rescuecore2/connection/TCPConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..0d0f0506fd562163da31c2d95fa1b127bd7bc4f7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/connection/TCPConnection.java @@ -0,0 +1,55 @@ +package rescuecore2.connection; + +import java.net.Socket; +import java.io.IOException; + +import rescuecore2.log.Logger; + +/** + TCP implementation of a Connection. + */ +public class TCPConnection extends StreamConnection { + private Socket socket; + + /** + Make a connection to the local host on a given port. + @param port The port to connect to. + @throws IOException If the host cannot be contacted. + */ + public TCPConnection(int port) throws IOException { + this(null, port); + } + + /** + Make a connection to a specific host on a given port. + @param address The address of the host. + @param port The port to connect to. + @throws IOException If the host cannot be contacted. + */ + public TCPConnection(String address, int port) throws IOException { + this(new Socket(address, port)); + } + + /** + Create a TCPConnection from an existing socket. + @param socket The socket to attach to. + @throws IOException If there is a problem opening the streams. + */ + public TCPConnection(Socket socket) throws IOException { + super(socket.getInputStream(), socket.getOutputStream()); + this.socket = socket; + socket.setSoTimeout(1000); + setName("TCPConnection: local port " + socket.getLocalPort() + ", endpoint = " + socket.getInetAddress() + ":" + socket.getPort()); + } + + @Override + protected void shutdownImpl() { + super.shutdownImpl(); + try { + socket.close(); + } + catch (IOException e) { + Logger.error("Error closing TCP connection", e); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/AbstractLogReader.java b/modules/rescuecore2/src/rescuecore2/log/AbstractLogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..a7f36e19b6f2956b8e9654e76cc837c6b4c989c2 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/AbstractLogReader.java @@ -0,0 +1,21 @@ +package rescuecore2.log; + +import rescuecore2.registry.Registry; + +/** + Abstract base class for LogReader implementations. +*/ +public abstract class AbstractLogReader implements LogReader { + /** + The registry to use for reading log entries. + */ + protected Registry registry; + + /** + Create a new AbstractLogReader. + @param registry The registry to use for reading log entries. + */ + protected AbstractLogReader(Registry registry) { + this.registry = registry; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/AbstractLogWriter.java b/modules/rescuecore2/src/rescuecore2/log/AbstractLogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..2fb353356416c61af1755d8068ff7adee35395bb --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/AbstractLogWriter.java @@ -0,0 +1,65 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; + +/** + * Abstract base class for log writer implementations. + */ +public abstract class AbstractLogWriter implements LogWriter { + protected boolean isV2; + + public AbstractLogWriter(boolean isV2) { + this.isV2 = isV2; + } + + @Override + public final void writeRecord(LogRecord entry) throws LogException { + if (isV2) + writeRecordProtoBuf(entry); + else + writeRecordV1(entry); + } + + private final void writeRecordProtoBuf(LogRecord entry) + throws LogException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + LogProto proto = entry.toLogProto(); +// byte[] data = JsonFormat.printer().print(proto).getBytes(); FOR JSON PRINT + byte[] data = proto.toByteString().toByteArray(); + writeInt32(data.length, out); + out.write(data); + write(out.toByteArray()); + } catch (IOException e) { + throw new LogException(e); + } + } + + private final void writeRecordV1(LogRecord entry) throws LogException { + ByteArrayOutputStream gather = new ByteArrayOutputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + entry.write(gather); + byte[] data = gather.toByteArray(); + writeInt32(entry.getRecordType().getID(), out); + writeInt32(data.length, out); + out.write(data); + write(out.toByteArray()); + } catch (IOException e) { + throw new LogException(e); + } + } + + /** + * Write a set of bytes to the log. + * + * @param bytes The bytes to write. + * @throws LogException If there is a problem writing the bytes. + */ + protected abstract void write(byte[] bytes) throws LogException; +} diff --git a/modules/rescuecore2/src/rescuecore2/log/CommandsRecord.java b/modules/rescuecore2/src/rescuecore2/log/CommandsRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..7fc694056c168147f4d2150345cd50235cea0782 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/CommandsRecord.java @@ -0,0 +1,121 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeMessage; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readMessage; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.ArrayList; + +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.messages.Command; + +/** + A commands record. +*/ +public class CommandsRecord implements LogRecord { + private int time; + private Collection<Command> commands; + + /** + Construct a new CommandsRecord. + @param time The timestep of this commands record. + @param commands The set of agent commands. + */ + public CommandsRecord(int time, Collection<Command> commands) { + this.time = time; + this.commands = commands; + } + + /** + Construct a new CommandsRecord and read data from an InputStream. + @param in The InputStream to read from. + @throws IOException If there is a problem reading the stream. + @throws LogException If there is a problem reading the log record. + */ + public CommandsRecord(InputStream in) throws IOException, LogException { + read(in); + } + + public CommandsRecord(LogProto log) { + fromLogProto(log); + } + + @Override + public RecordType getRecordType() { + return RecordType.COMMANDS; + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(time, out); + writeInt32(commands.size(), out); + for (Command next : commands) { + writeMessage(next, out); + } + } + + @Override + public void read(InputStream in) throws IOException, LogException { + time = readInt32(in); + commands = new ArrayList<Command>(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + Message m = readMessage(in); + if (m == null) { + throw new LogException("Could not read message from stream"); + } + if (!(m instanceof Command)) { + throw new LogException("Illegal message type in commands record: " + m); + } + commands.add((Command)m); + } + } + + /** + Get the timestamp for this record. + @return The timestamp. + */ + public int getTime() { + return time; + } + + /** + Get the commands. + @return The commands. + */ + public Collection<Command> getCommands() { + return commands; + } + + @Override + public void fromLogProto(LogProto log) { + CommandLogProto command = log.getCommand(); + time=command.getTime(); + ArrayList<Command> comms = new ArrayList<>(); + for (MessageProto m :command.getCommandsList()) { + comms.add((Command)MsgProtoBuf.messageProto2Message(m)); + } + commands=comms; + + } + + @Override + public LogProto toLogProto() { + CommandLogProto.Builder builder = CommandLogProto.newBuilder() + .setTime(time); + for (Command c : commands) + builder.addCommands(c.toMessageProto()); + return LogProto.newBuilder().setCommand(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/ConfigRecord.java b/modules/rescuecore2/src/rescuecore2/log/ConfigRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..041ffecf881e67226e1f8219b100282bba66e1d2 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/ConfigRecord.java @@ -0,0 +1,101 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeString; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readString; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +import rescuecore2.config.Config; +import rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSProto.ConfigProto; + +/** + * A configuration record. + */ +public class ConfigRecord implements LogRecord { + private Config config; + + /** + * Construct a new ConfigRecord. + * + * @param config The Config to record. + */ + public ConfigRecord(Config config) { + this.config = config; + } + + /** + * Construct a new ConfigRecord and read data from an InputStream. + * + * @param in The InputStream to read from. + * @throws IOException If there is a problem reading the stream. + */ + public ConfigRecord(InputStream in) throws IOException { + read(in); + } + + public ConfigRecord(LogProto log) { + fromLogProto(log); + } + + @Override + public RecordType getRecordType() { + return RecordType.CONFIG; + } + + @Override + public void write(OutputStream out) throws IOException { + Set<String> all = config.getAllKeys(); + writeInt32(all.size(), out); + for (String key : all) { + String value = config.getValue(key); + writeString(key, out); + writeString(value, out); + } + } + + @Override + public void read(InputStream in) throws IOException { + config = new Config(); + int size = readInt32(in); + for (int i = 0; i < size; ++i) { + String key = readString(in); + String value = readString(in); + config.setValue(key, value); + } + } + + /** + * Get the Config. + * + * @return The config. + */ + public Config getConfig() { + return config; + } + + @Override + public void fromLogProto(LogProto log) { + config = new Config(); + for (Entry<String, String> entry : log.getConfig().getConfig() + .getDataMap().entrySet()) { + config.setValue(entry.getKey(), entry.getValue()); + } + } + + @Override + public LogProto toLogProto() { + return LogProto.newBuilder() + .setConfig(ConfigLogProto.newBuilder().setConfig( + ConfigProto.newBuilder().putAllData(config.getData()))) + .build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/EndLogRecord.java b/modules/rescuecore2/src/rescuecore2/log/EndLogRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..d7f83258ba473c8cb09217f488243622decdfbf2 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/EndLogRecord.java @@ -0,0 +1,52 @@ +package rescuecore2.log; + +import java.io.OutputStream; + +import rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto; + +import java.io.InputStream; +import java.io.IOException; + +/** + Marker record at the end of the log. +*/ +public class EndLogRecord implements LogRecord { + /** + Construct a new EndLogRecord. + */ + public EndLogRecord() { + } + + /** + Construct a new EndLogRecord and read data from an InputStream. + @param in The InputStream to read from. + @throws IOException If there is a problem reading the stream. + */ + public EndLogRecord(InputStream in) throws IOException { + read(in); + } + + @Override + public RecordType getRecordType() { + return RecordType.END_OF_LOG; + } + + @Override + public void write(OutputStream out) throws IOException { + } + + @Override + public void read(InputStream in) throws IOException { + } + + @Override + public void fromLogProto(LogProto log) { + } + + @Override + public LogProto toLogProto() { + return LogProto.newBuilder().setEnd(EndLogProto.newBuilder()).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/FileLogReader.java b/modules/rescuecore2/src/rescuecore2/log/FileLogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..6b95d44301135bf6fe73162f83db95cb15d8239c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/FileLogReader.java @@ -0,0 +1,48 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.readBytes; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.io.RandomAccessFile; +import java.io.ByteArrayInputStream; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.NavigableMap; +import java.util.TreeMap; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.config.Config; +import rescuecore2.registry.Registry; + +/** + A log reader that reads from a file. + */ +public class FileLogReader extends StreamLogReader { + public FileLogReader(String name, Registry registry) throws IOException, LogException { + this(new File(name), registry); + } + + /** + Construct a new FileLogReader. + @param file The file object to read. + @param registry The registry to use for reading log entries. + @throws IOException If the file cannot be read. + @throws LogException If there is a problem reading the log. + */ + public FileLogReader(File file, Registry registry) throws IOException, LogException { + super(new FileInputStream(file),registry); + Logger.info("Reading file log: " + file.getAbsolutePath()); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/FileLogReaderV1.java b/modules/rescuecore2/src/rescuecore2/log/FileLogReaderV1.java new file mode 100644 index 0000000000000000000000000000000000000000..78e79ac681eb64799df487a3391440deea37f97a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/FileLogReaderV1.java @@ -0,0 +1,311 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.readBytes; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.reallySkip; + +import java.io.File; +import java.io.IOException; +import java.io.EOFException; +import java.io.RandomAccessFile; +import java.io.ByteArrayInputStream; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.NavigableMap; +import java.util.TreeMap; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.config.Config; +import rescuecore2.registry.Registry; + +/** + A log reader that reads from a file. + */ +public class FileLogReaderV1 extends AbstractLogReader { + private static final int KEY_FRAME_BUFFER_MAX_SIZE = 10; + + private RandomAccessFile file; + private int maxTime; + private NavigableMap<Integer, WorldModel<? extends Entity>> keyFrames; + private Map<Integer, Map<EntityID, Long>> perceptionIndices; + private Map<Integer, Long> updatesIndices; + private Map<Integer, Long> commandsIndices; + private Config config; + + /** + Construct a new FileLogReader. + @param name The name of the file to read. + @param registry The registry to use for reading log entries. + @throws IOException If the file cannot be read. + @throws LogException If there is a problem reading the log. + */ + public FileLogReaderV1(String name, Registry registry) throws IOException, LogException { + this(new File(name), registry); + } + + /** + Construct a new FileLogReader. + @param file The file object to read. + @param registry The registry to use for reading log entries. + @throws IOException If the file cannot be read. + @throws LogException If there is a problem reading the log. + */ + public FileLogReaderV1(File file, Registry registry) throws IOException, LogException { + super(registry); + Logger.info("Reading file log: " + file.getAbsolutePath()); + this.file = new RandomAccessFile(file, "r"); + index(); + } + + @Override + public Config getConfig() { + return config; + } + + @Override + public int getMaxTimestep() throws LogException { + return maxTime; + } + + @Override + public WorldModel<? extends Entity> getWorldModel(int time) throws LogException { + Logger.debug("Getting world model at time " + time); + WorldModel<? extends Entity> result = new DefaultWorldModel<Entity>(Entity.class); + // Look for a key frame + Map.Entry<Integer, WorldModel<? extends Entity>> entry = keyFrames.floorEntry(time); + int startTime = entry.getKey(); + Logger.trace("Found key frame " + startTime); + // Copy the initial conditions + Logger.trace("Cloning initial conditions"); + for (Entity next : entry.getValue()) { + result.addEntity(next.copy()); + } + // Go through updates and apply them all + for (int i = startTime + 1; i <= time; ++i) { + ChangeSet updates = getUpdates(i).getChangeSet(); + Logger.trace("Merging " + updates.getChangedEntities().size() + " updates for timestep " + i); + result.merge(updates); + } + Logger.trace("Done"); + // Remove stale key frames + removeStaleKeyFrames(); + // Store this as a key frame - it's quite likely that the next timestep will be viewed soon. + keyFrames.put(time, result); + return result; + } + + @Override + public Set<EntityID> getEntitiesWithUpdates(int time) throws LogException { + Map<EntityID, Long> timestepMap = perceptionIndices.get(time); + if (timestepMap == null) { + return new HashSet<EntityID>(); + } + return timestepMap.keySet(); + } + + @Override + public PerceptionRecord getPerception(int time, EntityID entity) throws LogException { + Map<EntityID, Long> timestepMap = perceptionIndices.get(time); + if (timestepMap == null) { + return null; + } + Long l = timestepMap.get(entity); + if (l == null) { + return null; + } + try { + file.seek(l); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + return new PerceptionRecord(new ByteArrayInputStream(bytes)); + } + catch (IOException e) { + throw new LogException(e); + } + } + + @Override + public CommandsRecord getCommands(int time) throws LogException { + Long index = commandsIndices.get(time); + if (index == null) { + return null; + } + try { + file.seek(index); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + return new CommandsRecord(new ByteArrayInputStream(bytes)); + } + catch (IOException e) { + throw new LogException(e); + } + } + + @Override + public UpdatesRecord getUpdates(int time) throws LogException { + Long index = updatesIndices.get(time); + if (index == null) { + return null; + } + try { + file.seek(index); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + return new UpdatesRecord(new ByteArrayInputStream(bytes)); + } + catch (IOException e) { + throw new LogException(e); + } + } + + private void index() throws LogException { + try { + Registry.setCurrentRegistry(registry); + keyFrames = new TreeMap<Integer, WorldModel<? extends Entity>>(); + perceptionIndices = new HashMap<Integer, Map<EntityID, Long>>(); + updatesIndices = new HashMap<Integer, Long>(); + commandsIndices = new HashMap<Integer, Long>(); + file.seek(0); + int id; + RecordType type; + boolean startFound = false; + do { + id = readInt32(file); + type = RecordType.fromID(id); + if (!startFound) { + if (!RecordType.START_OF_LOG.equals(type)) { + throw new LogException("Log does not start with correct magic number"); + } + startFound = true; + } + indexRecord(type); + } while (!RecordType.END_OF_LOG.equals(type)); + } + catch (EOFException e) { + Logger.debug("EOF found"); + } + catch (IOException e) { + throw new LogException(e); + } + } + + private void indexRecord(RecordType type) throws IOException, LogException { + switch (type) { + case START_OF_LOG: + indexStart(); + break; + case INITIAL_CONDITIONS: + indexInitialConditions(); + break; + case PERCEPTION: + indexPerception(); + break; + case COMMANDS: + indexCommands(); + break; + case UPDATES: + indexUpdates(); + break; + case CONFIG: + indexConfig(); + break; + case END_OF_LOG: + indexEnd(); + break; + default: + throw new LogException("Unexpected record type: " + type); + } + } + + private void indexStart() throws IOException { + int size = readInt32(file); + reallySkip(file, size); + } + + private void indexEnd() throws IOException { + int size = readInt32(file); + reallySkip(file, size); + } + + private void indexInitialConditions() throws IOException, LogException { + int size = readInt32(file); + if (size < 0) { + throw new LogException("Invalid initial conditions size: " + size); + } + byte[] bytes = readBytes(size, file); + InitialConditionsRecord record = new InitialConditionsRecord(new ByteArrayInputStream(bytes)); + keyFrames.put(0, record.getWorldModel()); + } + + private void indexPerception() throws IOException, LogException { + long position = file.getFilePointer(); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + PerceptionRecord record = new PerceptionRecord(new ByteArrayInputStream(bytes)); + int time = record.getTime(); + EntityID agentID = record.getEntityID(); + Map<EntityID, Long> timestepMap = perceptionIndices.get(time); + if (timestepMap == null) { + timestepMap = new HashMap<EntityID, Long>(); + perceptionIndices.put(time, timestepMap); + } + timestepMap.put(agentID, position); + } + + private void indexCommands() throws IOException, LogException { + long position = file.getFilePointer(); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + CommandsRecord record = new CommandsRecord(new ByteArrayInputStream(bytes)); + int time = record.getTime(); + commandsIndices.put(time, position); + maxTime = Math.max(time, maxTime); + } + + private void indexUpdates() throws IOException, LogException { + long position = file.getFilePointer(); + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + UpdatesRecord record = new UpdatesRecord(new ByteArrayInputStream(bytes)); + int time = record.getTime(); + updatesIndices.put(time, position); + maxTime = Math.max(time, maxTime); + } + + private void indexConfig() throws IOException, LogException { + int size = readInt32(file); + byte[] bytes = readBytes(size, file); + ConfigRecord record = new ConfigRecord(new ByteArrayInputStream(bytes)); + config = record.getConfig(); + } + + private void removeStaleKeyFrames() { + Logger.trace("Removing stale key frames"); + int size = keyFrames.size(); + if (size < KEY_FRAME_BUFFER_MAX_SIZE) { + Logger.trace("Key frame buffer is not full: " + size + (size == 1 ? " entry" : " entries")); + return; + } + // Try to balance the number of key frames. + int window = maxTime / KEY_FRAME_BUFFER_MAX_SIZE; + for (int i = 0; i < maxTime; i += window) { + NavigableMap<Integer, WorldModel<? extends Entity>> next = keyFrames.subMap(i, false, i + window, true); + Logger.trace("Window " + i + " -> " + (i + window) + " has " + next.size() + " entries"); + if (next.size() > 1) { + // Remove all but the last entry in this window + Map.Entry<Integer, WorldModel<? extends Entity>> last = next.lastEntry(); + next.clear(); + next.put(last.getKey(), last.getValue()); + Logger.trace("Retained entry " + last); + } + } + Logger.trace("New key frame set: " + keyFrames); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/FileLogWriter.java b/modules/rescuecore2/src/rescuecore2/log/FileLogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..9e3bc3ec958b976f14e8331175940e6113de6a39 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/FileLogWriter.java @@ -0,0 +1,35 @@ +package rescuecore2.log; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * A class for writing the kernel log to a file. + */ +public class FileLogWriter extends StreamLogWriter { + /** + * Create a file log writer and open it for writing. + * + * @param name The name of the file to write to. + * @throws IOException If the log file cannot be opened. + * @throws LogException If the log cannot be written. + */ + public FileLogWriter(String name, boolean isV2) + throws IOException, LogException { + this(new File(name), isV2); + } + + /** + * Create a file log writer and open it for writing. + * + * @param file The file to write to. + * @throws IOException If the log file cannot be opened. + * @throws LogException If the log cannot be written. + */ + public FileLogWriter(File file, boolean isV2) + throws IOException, LogException { + super(new BufferedOutputStream(new FileOutputStream(file)), isV2); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/InitialConditionsRecord.java b/modules/rescuecore2/src/rescuecore2/log/InitialConditionsRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..325e7745e8ed5c81be73c43c0114b24b3460ef53 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/InitialConditionsRecord.java @@ -0,0 +1,105 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeEntity; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readEntity; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.util.Collection; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSProto.EntityProto; +import rescuecore2.worldmodel.DefaultWorldModel; + +/** + An initial conditions record. +*/ +public class InitialConditionsRecord implements LogRecord { + private WorldModel<Entity> model; + + /** + Construct a new InitialConditionsRecord. + @param model The world model to record. + */ + public InitialConditionsRecord(WorldModel<? extends Entity> model) { + this.model = DefaultWorldModel.create(); + this.model.merge(model.getAllEntities()); + } + + /** + Construct a new InitialConditionsRecord and read data from an InputStream. + @param in The InputStream to read from. + @throws IOException If there is a problem reading the stream. + @throws LogException If there is a problem reading the log record. + */ + public InitialConditionsRecord(InputStream in) throws IOException, LogException { + read(in); + } + public InitialConditionsRecord(LogProto in) throws LogException { + fromLogProto(in); + } + + @Override + public RecordType getRecordType() { + return RecordType.INITIAL_CONDITIONS; + } + + @Override + public void write(OutputStream out) throws IOException { + Collection<? extends Entity> all = model.getAllEntities(); + writeInt32(all.size(), out); + for (Entity e : all) { + writeEntity(e, out); + } + } + + @Override + public void read(InputStream in) throws IOException, LogException { + model = DefaultWorldModel.create(); + int size = readInt32(in); + for (int i = 0; i < size; ++i) { + Entity e = readEntity(in); + if (e == null) { + throw new LogException("Could not read entity from stream"); + } + model.addEntity(e); + } + } + + /** + Get the world model. + @return The world model. + */ + public WorldModel<Entity> getWorldModel() { + return model; + } + + @Override + public void fromLogProto(LogProto log) throws LogException { + model = DefaultWorldModel.create(); + for(EntityProto e:log.getInitialCondition().getEntitiesList()) { + Entity en = MsgProtoBuf.entityProto2Entity(e); + if(en==null) + throw new LogException("Could not read entity from stream"); + model.addEntity(en); + } + } + + @Override + public LogProto toLogProto() { + InitialConditionsLogProto.Builder builder=InitialConditionsLogProto.newBuilder(); + Collection<? extends Entity> all = model.getAllEntities(); + for (Entity e : all) { + builder.addEntities(e.toEntityProto()); + } + return LogProto.newBuilder().setInitialCondition(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogException.java b/modules/rescuecore2/src/rescuecore2/log/LogException.java new file mode 100644 index 0000000000000000000000000000000000000000..75370fc82863dbc28cb085d4bd72450a2672ee35 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogException.java @@ -0,0 +1,38 @@ +package rescuecore2.log; + +/** + Logging exceptions. + */ +public class LogException extends Exception { + /** + Construct a log exception with no information. + */ + public LogException() { + super(); + } + + /** + Construct a log exception with an error message. + @param msg The error message. + */ + public LogException(String msg) { + super(msg); + } + + /** + Construct a log exception that was caused by another exception. + @param cause The cause of this exception. + */ + public LogException(Throwable cause) { + super(cause); + } + + /** + Construct a log exception with an error message and an underlying cause. + @param msg The error message. + @param cause The cause of this exception. + */ + public LogException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogExtractor.java b/modules/rescuecore2/src/rescuecore2/log/LogExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..3b5ece96964ebc37e6b39736786eb82399bfb4f7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogExtractor.java @@ -0,0 +1,244 @@ +package rescuecore2.log; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; + +import rescuecore2.Constants; +import rescuecore2.Timestep; +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.registry.Registry; +import rescuecore2.score.ScoreFunction; +import rescuecore2.view.ViewComponent; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * A class for viewing log files. + */ +public class LogExtractor { + private static final String VIEWERS_KEY = "log.viewers"; + + private ScoreFunction scoreFunction; + private LogReader log; + private List<ViewComponent> viewers; + private int maxTime; + + private int current_time; + WorldModel<? extends Entity> current_model = null; + + /** + * Construct a LogViewer. + * + * @param reader The LogReader to read. + * @param config The system configuration. + * @throws LogException If there is a problem reading the log. + */ + public LogExtractor(LogReader reader, Config config) throws LogException { + this.log = reader; + registerViewers(config); + maxTime = log.getMaxTimestep(); + scoreFunction = makeScoreFunction(config); + showTimestep(0); + } + + /** + * Show a particular timestep in the viewer. + * + * @param time The timestep to show. If this value is out of range then this + * method will silently return. + */ + public void showTimestep(int time) { + try { + if (time < 0 || time > maxTime) { + return; + } + // CommandsRecord commandsRecord = null;//log.getCommands(time); + // UpdatesRecord updatesRecord = log.getUpdates(time); + /* + * if (updatesRecord != null) { + * updates.addAll(updatesRecord.getChangeSet()); } + */ + current_model = log.getWorldModel(time); + /* + * for (ViewComponent next : viewers) { next.view(model, + * commandsRecord == null ? null : commandsRecord.getCommands(), + * updatesRecord == null ? null : updatesRecord.getChangeSet()); + * next.repaint(); } + */ + current_time = time; + } catch (LogException e) { + System.out.println("Error: " + e.getMessage()); + } + } + + public int getTime() { + return current_time; + } + + public boolean step() { + if (current_time == maxTime) { + return false; + } + showTimestep(current_time + 1); + return true; + } + + public void setDimension(int width, int height) { + for (ViewComponent next : viewers) { + next.setBounds(0, 0, width, height); + } + } + + public double getScore() { + return scoreFunction.score(current_model, new Timestep(current_time)); + } + + public BufferedImage paintImage() { + if (viewers.isEmpty()) { + return null; + } + ViewComponent view = viewers.get(0); + view.view(current_model, null, null); + // Create the image + GraphicsConfiguration configuration = GraphicsEnvironment + .getLocalGraphicsEnvironment().getDefaultScreenDevice() + .getDefaultConfiguration(); + BufferedImage image = configuration.createCompatibleImage( + view.getWidth(), view.getHeight(), Transparency.TRANSLUCENT); + + // Render the component onto the image + Graphics graphics = image.createGraphics(); + view.paint(graphics); + graphics.dispose(); + return image; + } + + public void writeImage(String filename) { + BufferedImage bi = paintImage(); + File outfile = new File(filename); + try { + ImageIO.write(bi, "png", outfile); + } catch (IOException e) { + System.out.println("Error writing image: " + e.getMessage()); + } + } + + private void registerViewers(Config config) { + viewers = new ArrayList<ViewComponent>(); + for (String next : config.getArrayValue(VIEWERS_KEY, "")) { + ViewComponent viewer = instantiate(next, ViewComponent.class); + if (viewer != null) { + viewer.initialise(config); + viewers.add(viewer); + } + } + } + + private ScoreFunction makeScoreFunction(Config config) { + String className = config.getValue(Constants.SCORE_FUNCTION_KEY); + ScoreFunction result = instantiate(className, ScoreFunction.class); + try { + WorldModel<? extends Entity> model = log.getWorldModel(0); + result.initialise(model, config); + } catch (LogException e) { + System.out.println("Error: " + e.getMessage()); + } + return result; + } + + /** + * Launch a new LogViewer. + * + * @param args Command line arguments. Accepts only one argument: the name + * of a log file. + */ + public static void main(String[] args) { + Config config = new Config(); + try { + args = CommandLineOptions.processArgs(args, config); + if (args.length != 2) { + printUsage(); + return; + } + String name = args[0]; + String outdir = args[1]; + processJarFiles(config); + LogReader reader = RCRSLogFactory.getLogReader(name, + Registry.SYSTEM_REGISTRY); + LogExtractor log = new LogExtractor(reader, config); + log.setDimension(1024, 786); + // viewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + List<Double> scores = new ArrayList<Double>(); + scores.add(log.getScore()); + writeFile(outdir + "/init-score.txt", "" + log.getScore()); + while (log.step()) { + if (log.getTime() == 1) { + log.writeImage(outdir + "/snapshot-init.png"); + } + if (log.getTime() % 50 == 0) { + log.writeImage( + outdir + "/snapshot-" + log.getTime() + ".png"); + } + scores.add(log.getScore()); + } + log.writeImage(outdir + "/snapshot-final.png"); + writeFile(outdir + "/final-score.txt", "" + log.getScore()); + + StringBuffer scoreString = new StringBuffer(); + for (Double score : scores) { + if (scoreString.length() != 0) { + scoreString.append(" "); + } + scoreString.append(score); + } + writeFile(outdir + "/scores.txt", scoreString.toString()); + } catch (IOException e) { + Logger.error("Error reading log", e); + } catch (ConfigException e) { + Logger.error("Configuration error", e); + } catch (LogException e) { + Logger.error("Error reading log", e); + } + + System.exit(0); + } + + private static void writeFile(String filename, String content) { + try { + PrintWriter out = new PrintWriter( + new BufferedWriter(new FileWriter(filename))); + out.print(content); + out.close(); + } catch (IOException e) { + System.out.println("Error writing file: " + e.getMessage()); + } + + } + + private static void printUsage() { + System.out.println("Usage: LogExtractor <filename> <output directory>"); + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.process(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogReader.java b/modules/rescuecore2/src/rescuecore2/log/LogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..568a52802f6ef4ef164350e637c81a7d8a439dfe --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogReader.java @@ -0,0 +1,68 @@ +package rescuecore2.log; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.config.Config; + +import java.util.Set; + +/** + An interface for objects that know how to read log files. + */ +public interface LogReader { + /** + Get the configuration entry in the log file. + @return The configuration stored in the log. + @throws LogException If there is a problem reading the log. + */ + Config getConfig() throws LogException; + + /** + Get the last timestep recorded in this log. + @return The last timestep number. + @throws LogException If there is a problem reading the log. + */ + int getMaxTimestep() throws LogException; + + /** + Get the state of the world at a particular timestep. + @param time The timestep to look up. If this is zero then the world initial conditions will be returned. + @return The world model at the given timestep. + @throws LogException If there is a problem reading the log. + */ + WorldModel<? extends Entity> getWorldModel(int time) throws LogException; + + /** + Get the set of EntityIDs that have perception updates in a given timestep. + @param time The timestep to look up. + @return The set of EntityIDs that have perception records for that time. + @throws LogException If there is a problem reading the log. + */ + Set<EntityID> getEntitiesWithUpdates(int time) throws LogException; + + /** + Get the perception record for a particular entity at a particular time. + @param time The timestep to look up. + @param entity The entity to look up. + @return The perception record for the timestep/agent, or null if there is no such entry. + @throws LogException If there is a problem reading the log. + */ + PerceptionRecord getPerception(int time, EntityID entity) throws LogException; + + /** + Get the agent commands at a particular timestep. + @param time The timestep to look up. + @return The commands record for the timestep, or null if there is no commands record at that timestep. + @throws LogException If there is a problem reading the log. + */ + CommandsRecord getCommands(int time) throws LogException; + + /** + Get the simulator updates at a particular timestep. + @param time The timestep to look up. + @return The updates record for the timestep, or null if there is no updates record at that timestep. + @throws LogException If there is a problem reading the log. + */ + UpdatesRecord getUpdates(int time) throws LogException; +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogRecord.java b/modules/rescuecore2/src/rescuecore2/log/LogRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..7f6b014d4e78545a14b903720df312aecb242378 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogRecord.java @@ -0,0 +1,37 @@ +package rescuecore2.log; + +import java.io.OutputStream; + +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; + +import java.io.InputStream; +import java.io.IOException; + +/** + Interface for entries in the log. +*/ +public interface LogRecord { + /** + Get the type of this record. + @return The record type. + */ + RecordType getRecordType(); + + /** + Write this log record to a stream. + @param out The OutputStream to write to. + @throws IOException If there is a problem writing to the stream. + */ + void write(OutputStream out) throws IOException; + + /** + Read this log record's data from a stream. + @param in The InputStream to read from. + @throws IOException If there is a problem reading the stream. + @throws LogException If there is a problem reading the log record. + */ + void read(InputStream in) throws IOException, LogException; + + void fromLogProto(LogProto log) throws LogException; + LogProto toLogProto(); +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogViewer.java b/modules/rescuecore2/src/rescuecore2/log/LogViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..396f6e1208a799a8a3b5376870203247cbc6612d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogViewer.java @@ -0,0 +1,297 @@ +package rescuecore2.log; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JToggleButton; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.messages.Command; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.gui.ListModelList; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.registry.Registry; +import rescuecore2.view.EntityInspector; +import rescuecore2.view.RenderedObject; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +/** + * A class for viewing log files. + */ +public class LogViewer extends JPanel { + private static final String VIEWERS_KEY = "log.viewers"; + + private static final int TICK_STEP_SIZE = 10; + + private static final int VIEWER_SIZE = 500; + private static final int FRAME_DELAY = 1000; + + private LogReader log; + private JLabel timestep; + private EntityInspector inspector; + private JSlider slider; + private JList commandsList; + private JList updatesList; + private ListModelList<Command> commands; + private ListModelList<Entity> updates; + private List<ViewComponent> viewers; + private JButton down; + private JButton up; + private int maxTime; + + /** + * Construct a LogViewer. + * + * @param reader The LogReader to read. + * @param config The system configuration. + * @throws LogException If there is a problem reading the log. + */ + public LogViewer(LogReader reader, Config config) throws LogException { + super(new BorderLayout()); + this.log = reader; + inspector = new EntityInspector(); + registerViewers(config); + maxTime = log.getMaxTimestep(); + slider = new JSlider(0, maxTime); + down = new JButton("<-"); + up = new JButton("->"); + slider.setSnapToTicks(true); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinorTickSpacing(1); + Dictionary<Integer, JComponent> labels = new Hashtable<Integer, JComponent>(); + labels.put(maxTime, new JLabel(String.valueOf(maxTime))); + for (int i = 0; i < maxTime; i += TICK_STEP_SIZE) { + labels.put(i, new JLabel(String.valueOf(i))); + } + slider.setLabelTable(labels); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + showTimestep(slider.getValue()); + } + }); + down.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int value = slider.getValue() - 1; + if (value >= 0) { + slider.setValue(value); + } + } + }); + up.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int value = slider.getValue() + 1; + if (value <= maxTime) { + slider.setValue(value); + } + } + }); + final JToggleButton play = new JToggleButton(">>"); + play.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + while (play.isSelected()) { + int value = slider.getValue() + 1; + if (value <= maxTime) { + slider.setValue(value); + } else { + play.setSelected(false); + } + try { + Thread.sleep(FRAME_DELAY); + } catch (InterruptedException e1) { + Logger.warn("Player interrupted", e1); + } + } + } + }); + t.start(); + } + }); + JPanel lists = new JPanel(new GridLayout(0, 1)); + commands = new ListModelList<Command>(); + commandsList = new JList(commands); + updates = new ListModelList<Entity>(); + updatesList = new JList(updates); + JScrollPane s = new JScrollPane(commandsList); + s.setBorder(BorderFactory.createTitledBorder("Commands")); + s.setPreferredSize(commandsList.getPreferredScrollableViewportSize()); + lists.add(s); + s = new JScrollPane(updatesList); + s.setBorder(BorderFactory.createTitledBorder("Updates")); + s.setPreferredSize(updatesList.getPreferredScrollableViewportSize()); + lists.add(s); + timestep = new JLabel("Timestep: 0"); + JTabbedPane tabs = new JTabbedPane(); + for (ViewComponent next : viewers) { + tabs.addTab(next.getViewerName(), next); + next.addViewListener(new ViewListener() { + @Override + public void objectsClicked(ViewComponent view, + List<RenderedObject> objects) { + for (RenderedObject next : objects) { + if (next.getObject() instanceof Entity) { + inspector.inspect((Entity) next.getObject()); + return; + } + } + } + + @Override + public void objectsRollover(ViewComponent view, + List<RenderedObject> objects) { + } + }); + } + JSplitPane split1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + inspector, tabs); + JSplitPane split2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, split1, + lists); + add(split2, BorderLayout.CENTER); + Box bottom = Box.createHorizontalBox(); + bottom.add(down); + bottom.add(slider); + bottom.add(up); + bottom.add(play); + add(bottom, BorderLayout.SOUTH); + add(timestep, BorderLayout.NORTH); + slider.setValue(0); + } + + /** + * Show a particular timestep in the viewer. + * + * @param time The timestep to show. If this value is out of range then this + * method will silently return. + */ + public void showTimestep(int time) { + try { + if (time < 0 || time > maxTime) { + return; + } + timestep.setText("Timestep: " + time); + commands.clear(); + updates.clear(); + CommandsRecord commandsRecord = log.getCommands(time); + if (commandsRecord != null) { + commands.addAll(commandsRecord.getCommands()); + } + UpdatesRecord updatesRecord = log.getUpdates(time); + /* + * if (updatesRecord != null) { + * updates.addAll(updatesRecord.getChangeSet()); } + */ + WorldModel<? extends Entity> model = log.getWorldModel(time); + for (ViewComponent next : viewers) { + next.view(model, + commandsRecord == null ? null + : commandsRecord.getCommands(), + updatesRecord == null ? null + : updatesRecord.getChangeSet()); + next.repaint(); + } + down.setEnabled(time != 0); + up.setEnabled(time != maxTime); + } catch (LogException e) { + JOptionPane.showMessageDialog(this, e, "Error", + JOptionPane.ERROR_MESSAGE); + } + } + + private void registerViewers(Config config) { + viewers = new ArrayList<ViewComponent>(); + for (String next : config.getArrayValue(VIEWERS_KEY, "")) { + ViewComponent viewer = instantiate(next, ViewComponent.class); + if (viewer != null) { + viewer.initialise(config); + viewers.add(viewer); + } + } + } + + /** + * Launch a new LogViewer. + * + * @param args Command line arguments. Accepts only one argument: the name + * of a log file. + */ + public static void main(String[] args) { + Config config = new Config(); + try { + args = CommandLineOptions.processArgs(args, config); + if (args.length != 1) { + printUsage(); + return; + } + String name = args[0]; + processJarFiles(config); + LogReader reader = RCRSLogFactory.getLogReader(name, + Registry.SYSTEM_REGISTRY); + LogViewer viewer = new LogViewer(reader, config); + viewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + JFrame frame = new JFrame("Log viewer: " + name); + frame.getContentPane().add(viewer, BorderLayout.CENTER); + frame.pack(); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + frame.setVisible(true); + } catch (IOException e) { + Logger.error("Error reading log", e); + } catch (ConfigException e) { + Logger.error("Configuration error", e); + } catch (LogException e) { + Logger.error("Error reading log", e); + } + } + + private static void printUsage() { + System.out.println("Usage: LogViewer <filename>"); + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.process(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/LogWriter.java b/modules/rescuecore2/src/rescuecore2/log/LogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..216d4529f211e5e9e67f28c7239a7852c36ea9f6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/LogWriter.java @@ -0,0 +1,18 @@ +package rescuecore2.log; + +/** + An interface for objects that know how to write log entries. + */ +public interface LogWriter { + /** + Write a log entry. + @param entry The entry to write. + @throws LogException If there is a problem writing the log. + */ + void writeRecord(LogRecord entry) throws LogException; + + /** + Close the log. + */ + void close(); +} diff --git a/modules/rescuecore2/src/rescuecore2/log/Logger.java b/modules/rescuecore2/src/rescuecore2/log/Logger.java new file mode 100644 index 0000000000000000000000000000000000000000..0352ef4a0fe01170500e61214cea09df54b7e7ff --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/Logger.java @@ -0,0 +1,179 @@ +package rescuecore2.log; + +import org.apache.log4j.LogManager; +import org.apache.log4j.NDC; + +import java.util.Deque; +import java.util.ArrayDeque; + +/** + System-wide logging facilities. +*/ +public final class Logger { + private static final InheritableThreadLocal<Deque<org.apache.log4j.Logger>> LOG = new InheritableThreadLocal<Deque<org.apache.log4j.Logger>>() { + @Override + protected Deque<org.apache.log4j.Logger> initialValue() { + return new ArrayDeque<org.apache.log4j.Logger>(); + } + + @Override + protected Deque<org.apache.log4j.Logger> childValue(Deque<org.apache.log4j.Logger> parent) { + return new ArrayDeque<org.apache.log4j.Logger>(parent); + } + }; + + private Logger() { + } + + /** + Set the log context for this thread and all child threads. + @param context The new log context. + */ + public static void setLogContext(String context) { + Deque<org.apache.log4j.Logger> queue = LOG.get(); + queue.clear(); + queue.addLast(LogManager.getLogger(context)); + } + + /** + Push a log context onto the stack. + @param context The new log context. + */ + public static void pushLogContext(String context) { + Deque<org.apache.log4j.Logger> queue = LOG.get(); + queue.addLast(LogManager.getLogger(context)); + } + + /** + Pop a log context from the stack. + */ + public static void popLogContext() { + Deque<org.apache.log4j.Logger> queue = LOG.get(); + queue.removeLast(); + } + + private static org.apache.log4j.Logger get() { + Deque<org.apache.log4j.Logger> queue = LOG.get(); + if (queue.isEmpty()) { + return LogManager.getRootLogger(); + } + return queue.getLast(); + } + + /** + Push an item onto the nested diagnostic context. + @param s The item to push. + */ + public static void pushNDC(String s) { + NDC.push(s); + } + + /** + Pop an item from the nested diagnostic context. + */ + public static void popNDC() { + NDC.pop(); + } + + /** + Log a trace level message. + @param msg The message to log. + */ + public static void trace(String msg) { + get().trace(msg); + } + + /** + Log a trace level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void trace(String msg, Throwable t) { + get().trace(msg, t); + } + + /** + Log a debug level message. + @param msg The message to log. + */ + public static void debug(String msg) { + get().debug(msg); + } + + /** + Log a debug level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void debug(String msg, Throwable t) { + get().debug(msg, t); + } + + /** + Log an info level message. + @param msg The message to log. + */ + public static void info(String msg) { + get().info(msg); + } + + /** + Log an info level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void info(String msg, Throwable t) { + get().info(msg, t); + } + + /** + Log a warn level message. + @param msg The message to log. + */ + public static void warn(String msg) { + get().warn(msg); + } + + /** + Log a warn level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void warn(String msg, Throwable t) { + get().warn(msg, t); + } + + /** + Log an error level message. + @param msg The message to log. + */ + public static void error(String msg) { + get().error(msg); + } + + /** + Log an error level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void error(String msg, Throwable t) { + get().error(msg, t); + } + + /** + Log a fatal level message. + @param msg The message to log. + */ + public static void fatal(String msg) { + get().fatal(msg); + } + + /** + Log a fatal level message along with a throwable. + @param msg The message to log. + @param t The throwable stack trace to log. + */ + public static void fatal(String msg, Throwable t) { + get().fatal(msg, t); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/PerceptionRecord.java b/modules/rescuecore2/src/rescuecore2/log/PerceptionRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..dc41a8225cc0f3ff4138357704c70d62f0e36d49 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/PerceptionRecord.java @@ -0,0 +1,157 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeMessage; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readMessage; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.util.Collection; +import java.util.ArrayList; + +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.messages.Command; +import rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto; + +/** + * A perception record. + */ +public class PerceptionRecord implements LogRecord { + private int time; + private EntityID entityID; + private ChangeSet visible; + private Collection<Command> communications; + + /** + * Construct a new PerceptionRecord. + * + * @param time The timestep of this perception record. + * @param id The ID of the entity. + * @param visible The set of visible changes to entities. + * @param communications The set of communication messages. + */ + public PerceptionRecord(int time, EntityID id, ChangeSet visible, + Collection<Command> communications) { + this.time = time; + this.entityID = id; + this.visible = visible; + this.communications = communications; + } + + /** + * Construct a new PerceptionRecord and read data from an InputStream. + * + * @param in The InputStream to read from. + * @throws IOException If there is a problem reading the stream. + * @throws LogException If there is a problem reading the log record. + */ + public PerceptionRecord(InputStream in) throws IOException, LogException { + read(in); + } + + public PerceptionRecord(LogProto in) { + fromLogProto(in); + } + + @Override + public RecordType getRecordType() { + return RecordType.PERCEPTION; + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(entityID.getValue(), out); + writeInt32(time, out); + visible.write(out); + writeInt32(communications.size(), out); + for (Command next : communications) { + writeMessage(next, out); + } + } + + @Override + public void read(InputStream in) throws IOException, LogException { + entityID = new EntityID(readInt32(in)); + time = readInt32(in); + visible = new ChangeSet(); + visible.read(in); + communications = new ArrayList<Command>(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + Command c = (Command) readMessage(in); + if (c == null) { + throw new LogException("Could not read message from stream"); + } + communications.add(c); + } + } + + /** + * Get the timestamp for this record. + * + * @return The timestamp. + */ + public int getTime() { + return time; + } + + /** + * Get the EntityID for this record. + * + * @return The EntityID. + */ + public EntityID getEntityID() { + return entityID; + } + + /** + * Get the set of visible entity updates. + * + * @return The visible entity updates. + */ + public ChangeSet getChangeSet() { + return visible; + } + + /** + * Get the set of communication messages heard. + * + * @return The communication messages heard. + */ + public Collection<Command> getHearing() { + return communications; + } + + @Override + public void fromLogProto(LogProto log) { + PerceptionLogProto perception = log.getPerception(); + time=perception.getTime(); + entityID=new EntityID(perception.getEntityID()); + visible=new ChangeSet(); + visible.fromChangeSetProto(perception.getVisible()); + ArrayList<Command> comms = new ArrayList<>(); + for (MessageProto m :perception.getCommunicationsList()) { + comms.add((Command)MsgProtoBuf.messageProto2Message(m)); + } + communications=comms; + } + + @Override + public LogProto toLogProto() { + PerceptionLogProto.Builder builder = PerceptionLogProto.newBuilder() + .setEntityID(entityID.getValue()).setTime(time) + .setVisible(visible.toChangeSetProto()); + for (Command c : communications) + builder.addCommunications(c.toMessageProto()); + + return LogProto.newBuilder().setPerception(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/RCRSLogFactory.java b/modules/rescuecore2/src/rescuecore2/log/RCRSLogFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..76ee74ad068bd7f17f6b0cbf5bea7b0d00ac0aa3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/RCRSLogFactory.java @@ -0,0 +1,32 @@ +package rescuecore2.log; + +import java.io.File; +import java.io.IOException; + +import rescuecore2.registry.Registry; + +public class RCRSLogFactory { + + public static LogReader getLogReader(String filename, Registry registry) + throws LogException, IOException { + if (filename.endsWith(".7z")) + return new ZipLogReader(filename, registry); + if (filename.endsWith(".xz")) + return new FileLogReader(filename, registry); + if (filename.endsWith(".log")) + return new FileLogReaderV1(filename, registry); + throw new LogException("Undefined Format"); + } + + public static LogWriter getLogWriter(File file) + throws LogException, IOException { + String filename = file.getName(); + if (filename.endsWith(".7z")) + return new ZipLogWriter(file); + if (filename.endsWith(".xz")) + return new FileLogWriter(file, true); + if (filename.endsWith(".log")) + return new FileLogWriter(file, false); + throw new LogException("Undefined Format"); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/RecordType.java b/modules/rescuecore2/src/rescuecore2/log/RecordType.java new file mode 100644 index 0000000000000000000000000000000000000000..3f6ed90db5b4e0c2fb48785f56d49f4b4c8b320a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/RecordType.java @@ -0,0 +1,50 @@ +package rescuecore2.log; + +/** + Enumeration of possible record types in a log file. + */ +public enum RecordType { + /** Start of log marker. */ + START_OF_LOG(0xE542D585), + /** End of log marker. */ + END_OF_LOG(0x00), + /** Initial conditions record. */ + INITIAL_CONDITIONS(0x01), + /** Agent perception record. */ + PERCEPTION(0x02), + /** Commands record. */ + COMMANDS(0x03), + /** Updates record. */ + UPDATES(0x04), + /** Config record. */ + CONFIG(0x05); + + private int id; + + private RecordType(int id) { + this.id = id; + } + + /** + Get the ID of this record type for writing into the log. + @return The 32-bit ID of this record type. + */ + public int getID() { + return id; + } + + /** + Get the RecordType representing a type ID. + @param id The ID to look up. + @return A RecordType object. + @throws IllegalArgumentException If the id is invalid. + */ + public static RecordType fromID(int id) { + for (RecordType next : values()) { + if (next.id == id) { + return next; + } + } + throw new IllegalArgumentException("Unrecognised RecordType: " + id); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/StartLogRecord.java b/modules/rescuecore2/src/rescuecore2/log/StartLogRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..220153d8be7d9fd404a2be64872c2ea2eaa62cbe --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/StartLogRecord.java @@ -0,0 +1,52 @@ +package rescuecore2.log; + +import java.io.OutputStream; + +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto; + +import java.io.InputStream; +import java.io.IOException; + +/** + Marker record at the start of the log. +*/ +public class StartLogRecord implements LogRecord { + /** + Construct a new StartLogRecord. + */ + public StartLogRecord() { + } + + /** + Construct a new StartLogRecord and read data from an InputStream. + @param in The InputStream to read from. + @throws IOException If there is a problem reading the stream. + */ + public StartLogRecord(InputStream in) throws IOException { + read(in); + } + + @Override + public RecordType getRecordType() { + return RecordType.START_OF_LOG; + } + + @Override + public void write(OutputStream out) throws IOException { + } + + @Override + public void read(InputStream in) throws IOException { + } + + @Override + public void fromLogProto(LogProto log) { + + } + + @Override + public LogProto toLogProto() { + return LogProto.newBuilder().setStart(StartLogProto.newBuilder()).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/StreamLogReader.java b/modules/rescuecore2/src/rescuecore2/log/StreamLogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..58bbece044cbfe9a2f9363dc63a0291d028d8668 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/StreamLogReader.java @@ -0,0 +1,337 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readBytes; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.config.Config; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto.LogCase; +import rescuecore2.registry.Registry; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.tukaani.xz.LZMAInputStream; +import org.tukaani.xz.XZInputStream; + +import java.util.HashSet; + +import java.io.InputStream; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + An class for reading kernel logs from a stream. + */ +public class StreamLogReader extends AbstractLogReader { + private int maxTime; + private Map<Integer, CommandsRecord> commands; + private Map<Integer, UpdatesRecord> updates; + private Map<Integer, WorldModel<? extends Entity>> worldModels; + private Map<Integer, Map<EntityID, PerceptionRecord>> perception; + private Config config; + + /** + Construct a StreamLogReader. + @param in The InputStream to read. + @param registry The registry to use for reading log entries. + @throws LogException If there is a problem reading the log. + */ + public StreamLogReader(InputStream in, Registry registry) throws LogException { + super(registry); + commands = new HashMap<Integer, CommandsRecord>(); + updates = new HashMap<Integer, UpdatesRecord>(); + worldModels = new HashMap<Integer, WorldModel<? extends Entity>>(); + perception = new HashMap<Integer, Map<EntityID, PerceptionRecord>>(); + try { + readLog(in); + } + catch (IOException e) { + throw new LogException(e); + } + } + + @Override + public Config getConfig() throws LogException { + if (config == null) { + throw new LogException("No config record found"); + } + return config; + } + + @Override + public int getMaxTimestep() throws LogException { + return maxTime; + } + + @Override + public WorldModel<? extends Entity> getWorldModel(int time) throws LogException { + checkTime(time); + WorldModel<? extends Entity> result = worldModels.get(time); + if (result == null) { + result = DefaultWorldModel.create(); + } + return result; + } + + @Override + public Set<EntityID> getEntitiesWithUpdates(int time) throws LogException { + checkTime(time); + Map<EntityID, PerceptionRecord> agentData = perception.get(time); + Set<EntityID> result = new HashSet<EntityID>(); + if (agentData != null) { + result.addAll(agentData.keySet()); + } + return result; + } + + @Override + public PerceptionRecord getPerception(int time, EntityID entity) throws LogException { + checkTime(time); + Map<EntityID, PerceptionRecord> agentData = perception.get(time); + if (agentData == null) { + return null; + } + PerceptionRecord result = agentData.get(entity); + return result; + } + + @Override + public CommandsRecord getCommands(int time) throws LogException { + checkTime(time); + return commands.get(time); + } + + @Override + public UpdatesRecord getUpdates(int time) throws LogException { + checkTime(time); + return updates.get(time); + } + + private void checkTime(int time) { + if (time < 0 || time > maxTime) { + throw new IllegalArgumentException("Time is out of range: " + time + " should be between 0 and " + maxTime); + } + } + + private void readLog(InputStream in) throws IOException, LogException { + Registry.setCurrentRegistry(registry); + InputStream gin = new LZMAInputStream(in); + readLogProto(gin); +// readLogV1(in); + } + + private void readLogProto(InputStream in) throws IOException, LogException { + LogCase type; + boolean startFound = false; + int i = 0; + do { + int size = readInt32(in); + byte[] bytes = in.readNBytes(size); + LogProto log = LogProto.parseFrom(bytes); + type = log.getLogCase(); + if (!startFound) { + if (type != LogCase.START) { + throw new LogException("Log does not start with correct magic number"); + } + startFound = true; + } + + // パーセプション以外を処ç†ã™ã‚‹ + if (type != LogCase.PERCEPTION) { + readRecord(type, log); + // ファイルをä¿å˜ + Path path2 = Paths.get("output/" + i); + Files.write(path2.toAbsolutePath(), bytes); + i++; + } + } + while (type != LogCase.END); + System.out.println("å…¨ã¦ã®ãƒã‚°ã‚’èªã¿è¾¼ã¿ã¾ã—ãŸ"); + System.exit(0); + } + + private void readRecord(LogCase type, LogProto log) throws LogException { + Registry.setCurrentRegistry(registry); + switch (type) { + case INITIALCONDITION: + System.out.println("Initial condition"); + readInitialConditions(log); + break; + case PERCEPTION: + // System.out.println("Perception"); + // readPerception(log); + break; + case COMMAND: + System.out.println("Command"); + readCommands(log); + break; + case UPDATE: + System.out.println("Update"); + readUpdates(log); + break; + case CONFIG: + System.out.println("Config"); + readConfig(log); + break; + case END: + System.out.println("End"); + return; + case START: + System.out.println("Start"); + return; + default: + throw new LogException("Unexpected record type: " + type); + } + + } + + private void readLogV1(InputStream in) throws IOException, LogException { + Registry.setCurrentRegistry(registry); + + int id; + RecordType type; + boolean startFound = false; + do { + id = readInt32(in); + type = RecordType.fromID(id); + if (!startFound) { + if (type != RecordType.START_OF_LOG) { + throw new LogException("Log does not start with correct magic number"); + } + startFound = true; + } + readRecord(type, in); + } + while (type != RecordType.END_OF_LOG); + } + + private void readRecord(RecordType type, InputStream in) throws IOException, LogException { + int size = readInt32(in); + byte[] data = readBytes(size, in); + InputStream d = new ByteArrayInputStream(data); + switch (type) { + case INITIAL_CONDITIONS: + readInitialConditions(d); + break; + case PERCEPTION: + readPerception(d); + break; + case COMMANDS: + readCommands(d); + break; + case UPDATES: + readUpdates(d); + break; + case CONFIG: + readConfig(d); + break; + case END_OF_LOG: + return; + default: + throw new LogException("Unexpected record type: " + type); + } + } + + private void readInitialConditions(InputStream in) throws IOException, LogException { + InitialConditionsRecord record = new InitialConditionsRecord(in); + worldModels.put(0, record.getWorldModel()); + } + private void readInitialConditions(LogProto log) throws LogException { + InitialConditionsRecord record =new InitialConditionsRecord(log); + worldModels.put(0, record.getWorldModel()); + } + + private void readPerception(InputStream in) throws IOException, LogException { + PerceptionRecord record = new PerceptionRecord(in); + int time = record.getTime(); + Map<EntityID, PerceptionRecord> agentData = perception.get(time); + if (agentData == null) { + agentData = new HashMap<EntityID, PerceptionRecord>(); + perception.put(time, agentData); + } + agentData.put(record.getEntityID(), record); + maxTime = Math.max(time, maxTime); + } + private void readPerception(LogProto log) { + PerceptionRecord record = new PerceptionRecord(log); + int time = record.getTime(); + Map<EntityID, PerceptionRecord> agentData = perception.get(time); + if (agentData == null) { + agentData = new HashMap<EntityID, PerceptionRecord>(); + perception.put(time, agentData); + } + agentData.put(record.getEntityID(), record); + maxTime = Math.max(time, maxTime); + } + + private void readCommands(InputStream in) throws IOException, LogException { + CommandsRecord record = new CommandsRecord(in); + commands.put(record.getTime(), record); + maxTime = Math.max(record.getTime(), maxTime); + } + + private void readCommands(LogProto log) { + CommandsRecord record = new CommandsRecord(log); + commands.put(record.getTime(), record); + maxTime = Math.max(record.getTime(), maxTime); + } + private void readUpdates(InputStream in) throws IOException, LogException { + UpdatesRecord record = new UpdatesRecord(in); + int time = record.getTime(); + updates.put(time, record); + // Make the world model for this timestep + WorldModel<? extends Entity> newWorld = new DefaultWorldModel<Entity>(Entity.class); + WorldModel<? extends Entity> oldWorld = getWorldModel(time - 1); + if (oldWorld != null) { + Set<Entity> copy = new HashSet<Entity>(); + for (Entity next : oldWorld) { + copy.add(next.copy()); + } + newWorld.merge(copy); + } + newWorld.merge(record.getChangeSet()); + worldModels.put(time, newWorld); + maxTime = Math.max(time, maxTime); + } + private void readUpdates(LogProto log) throws LogException { + UpdatesRecord record = new UpdatesRecord(log); + int time = record.getTime(); + updates.put(time, record); + // Make the world model for this timestep + WorldModel<? extends Entity> newWorld = new DefaultWorldModel<Entity>(Entity.class); + WorldModel<? extends Entity> oldWorld = getWorldModel(time - 1); + if (oldWorld != null) { + Set<Entity> copy = new HashSet<Entity>(); + for (Entity next : oldWorld) { + copy.add(next.copy()); + } + newWorld.merge(copy); + } + newWorld.merge(record.getChangeSet()); + worldModels.put(time, newWorld); + maxTime = Math.max(time, maxTime); + } + + private void readConfig(InputStream in) throws IOException, LogException { + ConfigRecord record = new ConfigRecord(in); + config = record.getConfig(); + } + private void readConfig(LogProto log) { + ConfigRecord record = new ConfigRecord(log); + config = record.getConfig(); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/log/StreamLogWriter.java b/modules/rescuecore2/src/rescuecore2/log/StreamLogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..4210511790ebcbc6e89e821603c915da35d3548f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/StreamLogWriter.java @@ -0,0 +1,54 @@ +package rescuecore2.log; + +import java.io.IOException; +import java.io.OutputStream; + +import org.tukaani.xz.LZMA2Options; +import org.tukaani.xz.LZMAOutputStream; + +/** + * A class for writing the kernel log to an output stream. + */ +public class StreamLogWriter extends AbstractLogWriter { + private OutputStream out; + + /** + * Create a stream log writer. + * + * @param stream The stream to write to. + * @throws IOException + */ + public StreamLogWriter(OutputStream stream, boolean isV2) + throws IOException { + super(isV2); + if (isV2) + this.out = new LZMAOutputStream(stream, new LZMA2Options(7), -1); + } + + @Override + protected void write(byte[] bytes) throws LogException { + try { + out.write(bytes); + } catch (IOException e) { + throw new LogException(e); + } + } + + @Override + public void close() { + if (!isV2) { + /* not supported for LZMA */ + try { + out.flush(); + } catch (IOException e) { + Logger.error("Error flushing log stream", e); + } + } + try { + out.close(); + } catch (IOException e) { + Logger.error("Error closing log stream", e); + } + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/log/UpdatesRecord.java b/modules/rescuecore2/src/rescuecore2/log/UpdatesRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..658921e37f2fe0a070f822f8bffb8c531e7cf44e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/UpdatesRecord.java @@ -0,0 +1,97 @@ +package rescuecore2.log; + +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.readInt32; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto; +import rescuecore2.worldmodel.ChangeSet; + +/** + * An updates record. + */ +public class UpdatesRecord implements LogRecord { + private int time; + private ChangeSet changes; + + /** + * Construct a new UpdatesRecord. + * + * @param time The timestep of this updates record. + * @param changes The set of changes. + */ + public UpdatesRecord(int time, ChangeSet changes) { + this.time = time; + this.changes = changes; + } + + /** + * Construct a new UpdatesRecord and read data from an InputStream. + * + * @param in The InputStream to read from. + * @throws IOException If there is a problem reading the stream. + * @throws LogException If there is a problem reading the log record. + */ + public UpdatesRecord(InputStream in) throws IOException, LogException { + read(in); + } + + public UpdatesRecord(LogProto log) { + fromLogProto(log); + } + + @Override + public RecordType getRecordType() { + return RecordType.UPDATES; + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(time, out); + changes.write(out); + } + + @Override + public void read(InputStream in) throws IOException, LogException { + time = readInt32(in); + changes = new ChangeSet(); + changes.read(in); + } + + /** + * Get the timestamp for this record. + * + * @return The timestamp. + */ + public int getTime() { + return time; + } + + /** + * Get the entity updates. + * + * @return The changes. + */ + public ChangeSet getChangeSet() { + return changes; + } + + @Override + public void fromLogProto(LogProto log) { + time = log.getUpdate().getTime(); + changes = new ChangeSet(); + changes.fromChangeSetProto(log.getUpdate().getChanges()); + } + + @Override + public LogProto toLogProto() { + return LogProto + .newBuilder().setUpdate(UpdatesLogProto.newBuilder() + .setTime(time).setChanges(changes.toChangeSetProto())) + .build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/log/ZipLogReader.java b/modules/rescuecore2/src/rescuecore2/log/ZipLogReader.java new file mode 100644 index 0000000000000000000000000000000000000000..cb2b312ba68c0d4be1ef46dbf2ef225e35683c60 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/ZipLogReader.java @@ -0,0 +1,163 @@ +package rescuecore2.log; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry; +import org.apache.commons.compress.archivers.sevenz.SevenZFile; + +import rescuecore2.config.Config; +import rescuecore2.messages.protobuf.RCRSLogProto.LogProto; +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +public class ZipLogReader extends AbstractLogReader { + + private SevenZFile sevenZFile; + private Map<String, SevenZArchiveEntry> entries = new HashMap<>(); + private Map<Integer, WorldModel<? extends Entity>> worldModels = new HashMap<>(); + private Map<Integer, Set<EntityID>> entitiesWithUpdates = new HashMap<>(); + private int maxCycle = 0; + + public ZipLogReader(String path, Registry registry) throws LogException { + this(new File(path), registry); + } + + public ZipLogReader(File file, Registry registry) throws LogException { + super(registry); + try { + sevenZFile = new SevenZFile(file); + } catch (IOException e) { + throw new LogException(e); + } + + for (SevenZArchiveEntry entry : sevenZFile.getEntries()) { + if (!entry.isDirectory()) { + entries.put(entry.getName(), entry); + if (entry.getName().contains(RecordType.UPDATES.name())) { + String[] splt = entry.getName().split("/"); + int c = Integer.parseInt(splt[0]); + maxCycle = Math.max(maxCycle, c); + } else if (entry.getName() + .contains(RecordType.PERCEPTION.name())) { + String[] splt = entry.getName().split("/"); + int time = Integer.parseInt(splt[0]); + if (!entitiesWithUpdates.containsKey(time)) + entitiesWithUpdates.put(time, new HashSet<>()); + int id = Integer.parseInt(splt[splt.length - 1]); + entitiesWithUpdates.get(time).add(new EntityID(id)); + } + + } + } + Logger.info("Found " + maxCycle + " cycles."); +// System.out.println(entitiesWithUpdates); +// System.out.println(entries); + buildWorldModels(); + } + + private void buildWorldModels() throws LogException { + worldModels.put(0, getInitialConditions().getWorldModel()); + for (int i = 1; i <= getMaxTimestep(); i++) { + buildWorldModelForTime(i); + } + } + + private void buildWorldModelForTime(int time) throws LogException { + UpdatesRecord record = getUpdates(time); + + if (time % 10 == 0) + System.out.println(time); + Logger.info("Building worldmodel of " + time + "."); + + // Make the world model for this timestep + WorldModel<? extends Entity> newWorld = new DefaultWorldModel<Entity>( + Entity.class); + WorldModel<? extends Entity> oldWorld = getWorldModel(time - 1); + if (oldWorld != null) { + Set<Entity> copy = new HashSet<Entity>(); + for (Entity next : oldWorld) { + copy.add(next.copy()); + } + newWorld.merge(copy); + } + newWorld.merge(record.getChangeSet()); + worldModels.put(time, newWorld); + } + + private LogProto readFromFile(String path) throws LogException { + SevenZArchiveEntry entry = entries.get(path); + if (entry == null) + return null; + byte[] content = new byte[(int) entry.getSize()]; + try { + sevenZFile.getInputStream(entry).read(content, 0, content.length); + return LogProto.parseFrom(content); + } catch (IOException e) { + throw new LogException(e); + } + + } + + @Override + public Config getConfig() throws LogException { + return new ConfigRecord(readFromFile(RecordType.CONFIG.name())) + .getConfig(); + } + + private InitialConditionsRecord getInitialConditions() throws LogException { + return new InitialConditionsRecord( + readFromFile(RecordType.INITIAL_CONDITIONS.name())); + } + + @Override + public int getMaxTimestep() throws LogException { + return maxCycle; + } + + @Override + public WorldModel<? extends Entity> getWorldModel(int time) + throws LogException { + + return worldModels.get(time); + } + + @Override + public Set<EntityID> getEntitiesWithUpdates(int time) throws LogException { + return entitiesWithUpdates.get(time); + } + + @Override + public PerceptionRecord getPerception(int time, EntityID entity) + throws LogException { + LogProto log = readFromFile(time + "/" + RecordType.PERCEPTION.name() + + "/" + entity.getValue()); + if (log != null) + return new PerceptionRecord(log); + return null; + } + + @Override + public CommandsRecord getCommands(int time) throws LogException { + LogProto log = readFromFile(time + "/" + RecordType.COMMANDS.name()); + if (log != null) + return new CommandsRecord(log); + return null; + } + + @Override + public UpdatesRecord getUpdates(int time) throws LogException { + LogProto log = readFromFile(time + "/" + RecordType.UPDATES.name()); + if (log != null) + return new UpdatesRecord(log); + return null; + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/log/ZipLogWriter.java b/modules/rescuecore2/src/rescuecore2/log/ZipLogWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..e96ee6d4860d44882275307787ed92cb1d334d1a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/log/ZipLogWriter.java @@ -0,0 +1,71 @@ +package rescuecore2.log; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry; +import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile; + +public class ZipLogWriter implements LogWriter { + + private SevenZOutputFile sevenZOutput; + + public ZipLogWriter(File file) throws IOException { + sevenZOutput = new SevenZOutputFile(file); + } + + @Override + public void writeRecord(LogRecord record) throws LogException { + try { + sevenZOutput.putArchiveEntry(getRecordArchive(record)); + sevenZOutput.write(record.toLogProto().toByteArray()); + sevenZOutput.closeArchiveEntry(); + } catch (IOException e) { + throw new LogException(e); + } + } + + @Override + public void close() { + try { + sevenZOutput.close(); + } catch (IOException e) { + Logger.error("Error closing log stream", e); + } + } + + private SevenZArchiveEntry createArchiveEntry(String path) { + final SevenZArchiveEntry entry = new SevenZArchiveEntry(); + entry.setDirectory(false); + entry.setName(path); + return entry; + } + + private ArchiveEntry getRecordArchive(LogRecord record) { + String recordType = record.getRecordType().name(); + switch (record.getRecordType()) { + case COMMANDS: + CommandsRecord crecord = (CommandsRecord) record; + return createArchiveEntry(crecord.getTime() + "/" + recordType); + case PERCEPTION: + PerceptionRecord precord = (PerceptionRecord) record; + return createArchiveEntry(precord.getTime() + "/" + recordType + "/" + + precord.getEntityID()); + case UPDATES: + UpdatesRecord urecord = (UpdatesRecord) record; + return createArchiveEntry(urecord.getTime() + "/" + recordType); + case CONFIG: + case INITIAL_CONDITIONS: + case START_OF_LOG: + case END_OF_LOG: + return createArchiveEntry(recordType); + + default: + throw new IllegalArgumentException( + "Unexpected value: " + record.getRecordType()); + } + + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/AbstractCommand.java b/modules/rescuecore2/src/rescuecore2/messages/AbstractCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..a79fcc526ef8ff3e3e7add4268695706092b269e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/AbstractCommand.java @@ -0,0 +1,111 @@ +package rescuecore2.messages; + +import org.json.JSONObject; + +import rescuecore2.URN; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.control.ControlMessageComponentURN; +import rescuecore2.worldmodel.EntityID; + +/** + * A sub-interface of Message that tags messages that are interpreted as agent + * commands. + */ +public abstract class AbstractCommand extends AbstractMessage implements Command { + private EntityIDComponent agentID; + private IntComponent time; + + /** + * Construct a new abstract command. + * + * @param urn The urn of the command. + */ + protected AbstractCommand(int urn) { + super(urn); + init(); + } + + /** + * Construct a new abstract command. + * + * @param urn The urn of the command. + * @param agentID The ID of the agent issuing the command. + * @param time The time this command was issued. + */ + protected AbstractCommand(int urn, EntityID agentID, int time) { + super(urn); + init(agentID, time); + } + + /** + * Construct a new abstract command. + * + * @param urn The urn of the command. + */ + protected AbstractCommand(URN urn) { + super(urn); + init(); + } + + /** + * Construct a new abstract command. + * + * @param urn The urn of the command. + * @param agentID The ID of the agent issuing the command. + * @param time The time this command was issued. + */ + protected AbstractCommand(URN urn, EntityID agentID, int time) { + super(urn); + init(agentID, time); + } + + @Override + public EntityID getAgentID() { + return agentID.getValue(); + } + + @Override + public int getTime() { + return time.getValue(); + } + + /** + * Set the ID of the agent issuing the command. + * + * @param agentID The new agent ID. + */ + protected void setAgentID(EntityID agentID) { + this.agentID.setValue(agentID); + } + + /** + * Set the time of the command. + * + * @param time The new time. + */ + protected void setTime(int time) { + this.time.setValue(time); + } + + private void init() { + agentID = new EntityIDComponent(ControlMessageComponentURN.AgentID); + time = new IntComponent(ControlMessageComponentURN.Time); + addMessageComponent(agentID); + addMessageComponent(time); + } + + private void init(EntityID id, int t) { + init(); + setAgentID(id); + setTime(t); + } + + @Override + public JSONObject toJson() { + JSONObject json = new JSONObject(); + json.put("Name", getURN()); + json.put("AgentId", this.getAgentID().getValue()); + return json; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/AbstractMessage.java b/modules/rescuecore2/src/rescuecore2/messages/AbstractMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..e3167ae54d80e00bd878be92018c6c49c485ac8e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/AbstractMessage.java @@ -0,0 +1,111 @@ +package rescuecore2.messages; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * An abstract base class for Message objects. This class is implemented in + * terms of MessageComponent objects so subclasses need only provide a urn and a + * list of MessageComponent objects. + */ +public abstract class AbstractMessage implements Message { + private int urn; + private List<MessageComponent> components; + + /** + * Construct a message with a given urn. + * + * @param urn The urn of the message. + */ + protected AbstractMessage(int urn) { + this.urn = urn; + this.components = new ArrayList<MessageComponent>(); + } + + /** + * Construct a message with a urn defined as an enum. + * + * @param urn The urn of the message. + */ + protected AbstractMessage(URN urn) { + this(urn.getURNId()); + } + + @Override + public final int getURN() { + return urn; + } + + /** + * Get all the components of this message. + * + * @return A List of MessageComponent objects. + */ + public final List<MessageComponent> getComponents() { + return components; + } + + /** + * Add a message component. + * + * @param component The component to add. + */ + protected void addMessageComponent(MessageComponent component) { + components.add(component); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(urn); + result.append(" : "); + for (Iterator<MessageComponent> it = components.iterator(); it.hasNext();) { + MessageComponent next = it.next(); + result.append(next.toString()); + if (it.hasNext()) { + result.append(", "); + } + } + return result.toString(); + } + + @Override + public void write(OutputStream out) throws IOException { + for (MessageComponent next : components) { + next.write(out); + } + } + + @Override + public void read(InputStream in) throws IOException { + for (MessageComponent next : components) { + next.read(in); + } + } + + @Override + public MessageProto toMessageProto() { + MessageProto.Builder builder = MessageProto.newBuilder().setUrn(getURN()); + for (MessageComponent next : components) { + builder.putComponents(next.getName().getURNId(), next.toMessageComponentProto()); + } + return builder.build(); + } + + @Override + public void fromMessageProto(MessageProto proto) { + Map<Integer, MessageComponentProto> receivedcomponents = proto.getComponentsMap(); + for (MessageComponent next : components) { + next.fromMessageComponentProto(receivedcomponents.get(next.getName().getURNId())); + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/AbstractMessageComponent.java b/modules/rescuecore2/src/rescuecore2/messages/AbstractMessageComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..d06101e41dc2373bb112836fde3185dd6b6e49ea --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/AbstractMessageComponent.java @@ -0,0 +1,23 @@ +package rescuecore2.messages; + +import rescuecore2.URN; + +/** + Abstract base class for message components. + */ +public abstract class AbstractMessageComponent implements MessageComponent { + private final URN name; + + /** + Construct a message component with a name. + @param name The name of this component. + */ + protected AbstractMessageComponent(URN name) { + this.name = name; + } + + @Override + public URN getName() { + return name; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/Command.java b/modules/rescuecore2/src/rescuecore2/messages/Command.java new file mode 100644 index 0000000000000000000000000000000000000000..a2cca26dca27749d31859e4dff194901f9dd0484 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/Command.java @@ -0,0 +1,23 @@ +package rescuecore2.messages; + +import org.json.JSONObject; +import rescuecore2.worldmodel.EntityID; + +/** + A sub-interface of Message that tags messages that are interpreted as agent commands. + */ +public interface Command extends Message { + /** + Get the id of the agent-controlled entity that has issued this command. + @return The id of the agent. + */ + EntityID getAgentID(); + + /** + Get the timestep this command is intended for. + @return The timestep. + */ + int getTime(); + + JSONObject toJson(); +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/Message.java b/modules/rescuecore2/src/rescuecore2/messages/Message.java new file mode 100644 index 0000000000000000000000000000000000000000..778cff1c7daa812c39c5ec52bdf30f3effe38d92 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/Message.java @@ -0,0 +1,37 @@ +package rescuecore2.messages; + +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +import java.io.IOException; + +/** + The top-level interface for messages that are sent between simulator components. + */ +public interface Message { + /** + Get the urn of this message type. + @return The message urn. + */ + int getURN(); + + /** + Write the content of this message to a stream. The content should not include the message type ID. + @param out The stream to write to. + @throws IOException If the write fails. + */ + void write(OutputStream out) throws IOException; + + /** + Read the content of this message from a stream. The content should not include the message type ID. + @param in The stream to read from. + @throws IOException If the read fails. + */ + void read(InputStream in) throws IOException; + + + MessageProto toMessageProto(); + void fromMessageProto(MessageProto proto); +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/MessageComponent.java b/modules/rescuecore2/src/rescuecore2/messages/MessageComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..cd8e7875516aa88d756d30c405f502ea8ed3b2c1 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/MessageComponent.java @@ -0,0 +1,37 @@ +package rescuecore2.messages; + +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +import java.io.IOException; + +/** + A piece of a message. + */ +public interface MessageComponent { + /** + Get the name of this component of the message. + @return The name of this component. + */ + URN getName(); + + /** + Write this component to a stream. + @param out The stream to write to. + @throws IOException If the write fails. + */ + void write(OutputStream out) throws IOException; + + /** + Read this component from a stream. + @param in The stream to read from. + @throws IOException If the read fails. + */ + void read(InputStream in) throws IOException; + + void fromMessageComponentProto(MessageComponentProto proto); + MessageComponentProto toMessageComponentProto(); +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/ChangeSetComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/ChangeSetComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..ea5e0722199198c49d2d317028b6e2de4c4c6e55 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/ChangeSetComponent.java @@ -0,0 +1,78 @@ +package rescuecore2.messages.components; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +/** + An ChangeSet component to a message. + */ +public class ChangeSetComponent extends AbstractMessageComponent { + private ChangeSet changes; + + /** + Construct a ChangeSetComponent with no content. + @param name The name of the component. + */ + public ChangeSetComponent(URN name) { + super(name); + changes = new ChangeSet(); + } + + /** + Construct a ChangeSetComponent with a specific set of changes. + @param name The name of the component. + @param changes The changes in this message component. + */ + public ChangeSetComponent(URN name, ChangeSet changes) { + super(name); + this.changes = new ChangeSet(changes); + } + + /** + Get the ChangeSet. + @return The ChangeSet. + */ + public ChangeSet getChangeSet() { + return changes; + } + + /** + Set the ChangeSet. + @param newChanges The new ChangeSet. + */ + public void setChangeSet(ChangeSet newChanges) { + this.changes = new ChangeSet(newChanges); + } + + @Override + public void write(OutputStream out) throws IOException { + changes.write(out); + } + + @Override + public void read(InputStream in) throws IOException { + changes = new ChangeSet(); + changes.read(in); + } + + @Override + public String toString() { + return getName() + " = " + changes.getChangedEntities().size() + " entities"; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + changes=new ChangeSet(); + changes.fromChangeSetProto(proto.getChangeSet()); + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setChangeSet(changes.toChangeSetProto()).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/CommandListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/CommandListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..d4334db2940fda433baec9dccfaffb0e1319027d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/CommandListComponent.java @@ -0,0 +1,116 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readMessage; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeMessage; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.Command; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageListProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.messages.protobuf.MsgProtoBuf; + +/** + A message component made up of a list of agent commands. + */ +public class CommandListComponent extends AbstractMessageComponent { + private List<Command> commands; + + /** + Construct a CommandListComponent with no content. + @param name The name of the component. + */ + public CommandListComponent(URN name) { + super(name); + commands = new ArrayList<Command>(); + } + + /** + Construct a CommandListComponent with a specific list of agent commands. + @param name The name of the component. + @param commands The agent commands in this message component. + */ + public CommandListComponent(URN name, Collection<? extends Command> commands) { + super(name); + this.commands = new ArrayList<Command>(commands); + } + + /** + Get the agent commands that make up this message component. + @return The agent commands in this component. + */ + public List<Command> getCommands() { + return commands; + } + + /** + Set the commands that make up this message component. + @param commands The commands in this component. + */ + public void setCommands(Collection<? extends Command> commands) { + this.commands = new ArrayList<Command>(commands); + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(commands.size(), out); + for (Command next : commands) { + writeMessage(next, out); + } + } + + @Override + public void read(InputStream in) throws IOException { + commands.clear(); + int size = readInt32(in); + for (int i = 0; i < size; ++i) { + Message m = readMessage(in); + if (m instanceof Command) { + commands.add((Command)m); + } + else { + throw new IOException("Command list stream contained a non-command message: " + m + " (" + m.getClass().getName() + ")"); + } + } + } + + @Override + public String toString() { + return commands.size() + " commands"; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + commands.clear(); + for (MessageProto commandProto : proto.getCommandList().getCommandsList()) { + + Message m = MsgProtoBuf.messageProto2Message(commandProto); + if (m instanceof Command) { + commands.add((Command)m); + } + else { + throw new Error("Command list stream contained a non-command message: " + m + " (" + m.getClass().getName() + ")"); + } + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + MessageListProto.Builder builder=MessageListProto.newBuilder(); + for (Command next : commands) { + builder.addCommands(next.toMessageProto()); + } + return MessageComponentProto.newBuilder().setCommandList(builder).build(); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/ConfigComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/ConfigComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..e10bc5763f1f844f677628bb10fe82c8bbf3da81 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/ConfigComponent.java @@ -0,0 +1,98 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.readString; +import static rescuecore2.misc.EncodingTools.writeString; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.ConfigProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.config.Config; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.util.Map.Entry; +import java.util.Set; + +/** + A Config component to a message. + */ +public class ConfigComponent extends AbstractMessageComponent { + private Config config; + + /** + Construct a ConfigComponent with no content. + @param name The name of the component. + */ + public ConfigComponent(URN name) { + super(name); + config = new Config(); + } + + /** + Construct a ConfigComponent with a specific config as content. + @param name The name of the component. + @param data The content. + */ + public ConfigComponent(URN name, Config data) { + super(name); + this.config = data; + } + + /** + Get the content of this message component. + @return The content of the component. + */ + public Config getConfig() { + return config; + } + + /** + Set the content of this message component. + @param config The new content. + */ + public void setConfig(Config config) { + this.config = config; + } + + @Override + public void write(OutputStream out) throws IOException { + Set<String> keys = config.getAllKeys(); + writeInt32(keys.size(), out); + for (String key : keys) { + writeString(key, out); + writeString(config.getValue(key), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + int count = readInt32(in); + config = new Config(); + for (int i = 0; i < count; ++i) { + String key = readString(in); + String value = readString(in); + config.setValue(key, value); + } + } + + @Override + public String toString() { + return getName() + " (" + config.getAllKeys().size() + " entries)"; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + config = new Config(); + for (Entry<String, String> entry : proto.getConfig().getDataMap().entrySet()) { + config.setValue(entry.getKey(), entry.getValue()); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setConfig(ConfigProto.newBuilder().putAllData(config.getData())).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/EntityComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/EntityComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..0afdf1457f7c17f4854604d2da52aa84a337355d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/EntityComponent.java @@ -0,0 +1,81 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readEntity; +import static rescuecore2.misc.EncodingTools.writeEntity; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.worldmodel.Entity; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + An Entity component to a message. + */ +public class EntityComponent extends AbstractMessageComponent { + private Entity entity; + + /** + Construct an EntityComponent with no content. + @param name The name of the component. + */ + public EntityComponent(URN name) { + super(name); + entity = null; + } + + /** + Construct an EntityComponent with a specific entity value. + @param name The name of the component. + @param entity The value of this component. + */ + public EntityComponent(URN name, Entity entity) { + super(name); + this.entity = entity; + } + + /** + Get the entity. + @return The entity. + */ + public Entity getEntity() { + return entity; + } + + /** + Set the entity. + @param e The new entity. + */ + public void setEntity(Entity e) { + entity = e; + } + + @Override + public void write(OutputStream out) throws IOException { + writeEntity(entity, out); + } + + @Override + public void read(InputStream in) throws IOException { + entity = readEntity(in); + } + + @Override + public String toString() { + return getName() + " = " + entity.toString(); + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + entity=MsgProtoBuf.entityProto2Entity(proto.getEntity()); + + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setEntity(entity.toEntityProto()).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..35912b927a81b6b9adbe8ded22c14732177d7aa3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDComponent.java @@ -0,0 +1,84 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.URN; +import rescuecore2.messages.AbstractMessageComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An EntityID component to a message. + */ +public class EntityIDComponent extends AbstractMessageComponent { + private EntityID value; + + /** + * Construct an EntityIDComponent with no content. + * + * @param name The name of the component. + */ + public EntityIDComponent(URN name) { + super(name); + value = null; + } + + /** + * Construct an EntityIDComponent with a specific value. + * + * @param name The name of the component. + * @param value The value of this component. + */ + public EntityIDComponent(URN name, EntityID value) { + super(name); + this.value = value; + } + + /** + * Get the value of this message component. + * + * @return The value of the component. + */ + public EntityID getValue() { + return value; + } + + /** + * Set the value of this message component. + * + * @param id The new value of the component. + */ + public void setValue(EntityID id) { + value = id; + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(value.getValue(), out); + } + + @Override + public void read(InputStream in) throws IOException { + value = new EntityID(readInt32(in)); + } + + @Override + public String toString() { + return getName() + " = " + value; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + value = new EntityID(proto.getEntityID()); + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setEntityID(value.getValue()).build(); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..badf1ae3872ba03e199fd01c9ae360f5529ac262 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/EntityIDListComponent.java @@ -0,0 +1,96 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.IntListProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.worldmodel.EntityID; + +/** + A message component that is a list of entity IDs. + */ +public class EntityIDListComponent extends AbstractMessageComponent { + private List<EntityID> ids; + + /** + Construct an EntityIDListComponent with no data. + @param name The name of the component. + */ + public EntityIDListComponent(URN name) { + super(name); + ids = new ArrayList<EntityID>(); + } + + /** + Construct an EntityIDListComponent with a list of entity IDs. + @param name The name of the component. + @param ids The data. + */ + public EntityIDListComponent(URN name, List<EntityID> ids) { + super(name); + this.ids = new ArrayList<EntityID>(ids); + } + + /** + Get the list of entity IDs in this component. + @return An immutable view of the list of entity IDs. + */ + public List<EntityID> getIDs() { + return Collections.unmodifiableList(ids); + } + + /** + Set the list of entity IDs in this component. + @param newIDs The new set of entity IDs. + */ + public void setIDs(List<EntityID> newIDs) { + this.ids = new ArrayList<EntityID>(newIDs); + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(ids.size(), out); + for (EntityID next : ids) { + writeInt32(next.getValue(), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + ids.clear(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + ids.add(new EntityID(readInt32(in))); + } + } + + @Override + public String toString() { + return getName() + " = " + ids.toString(); + } + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + ids.clear(); + for (Integer val : proto.getEntityIDList().getValuesList()) { + ids.add(new EntityID(val)); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + IntListProto.Builder builder=IntListProto.newBuilder(); + for (EntityID next : ids) { + builder.addValues(next.getValue()); + } + return MessageComponentProto.newBuilder().setEntityIDList(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/EntityListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/EntityListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..de82be158bc244f8607f81947abc77a5297a33da --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/EntityListComponent.java @@ -0,0 +1,113 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readEntity; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeEntity; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.EntityListProto; +import rescuecore2.messages.protobuf.RCRSProto.EntityProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.worldmodel.Entity; + +/** + * An EntityList component to a message. + */ +public class EntityListComponent extends AbstractMessageComponent { + private List<Entity> entities; + + /** + * Construct an EntityListComponent with no content. + * + * @param name The name of the component. + */ + public EntityListComponent(URN name) { + super(name); + entities = new ArrayList<Entity>(); + } + + /** + * Construct an EntityListComponent with a specific list of entities. + * + * @param name The name of the component. + * @param entities The entities in this message component. + */ + public EntityListComponent(URN name, + Collection<? extends Entity> entities) { + super(name); + this.entities = new ArrayList<Entity>(entities); + } + + /** + * Get the entities that make up this message component. + * + * @return The entities in this component. + */ + public List<Entity> getEntities() { + return entities; + } + + /** + * Set the entities that make up this message component. + * + * @param entities The entities in this component. + */ + public void setEntities(Collection<? extends Entity> entities) { + this.entities = new ArrayList<Entity>(entities); + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(entities.size(), out); + for (Entity next : entities) { + writeEntity(next, out); + } + } + + @Override + public void read(InputStream in) throws IOException { + entities.clear(); + int size = readInt32(in); + for (int i = 0; i < size; ++i) { + Entity e = readEntity(in); + if (e != null) { + entities.add(e); + } + } + } + + @Override + public String toString() { + return getName() + " = " + entities.size() + " entities"; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + entities.clear(); + for (EntityProto entityProto : proto.getEntityList() + .getEntitiesList()) { + Entity entity = MsgProtoBuf.entityProto2Entity(entityProto); + if(entity!=null) + entities.add(entity); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + EntityListProto.Builder builder = EntityListProto.newBuilder(); + for (Entity next : entities) { + builder.addEntities(next.toEntityProto()); + } + return MessageComponentProto.newBuilder().setEntityList(builder) + .build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/FloatListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/FloatListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..7c1ad386d9461c7c3ecccfe4e74ec63663703ba6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/FloatListComponent.java @@ -0,0 +1,141 @@ +package rescuecore2.messages.components; + + +import static rescuecore2.misc.EncodingTools.readFloat32; +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeFloat32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.FloatListProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +/** + * A message component that is a list of floats. + */ +public class FloatListComponent extends AbstractMessageComponent { + private List<Float> data; + + /** + * Construct an FloatListComponent with no data. + * + * @param name + * The name of the component. + */ + public FloatListComponent(URN name) { + super(name); + data = new ArrayList<Float>(); + } + + /** + * Construct an FloatListComponent with a list of floats. + * + * @param name + * The name of the component. + * @param data + * The data. + */ + public FloatListComponent(URN name, List<Float> data) { + super(name); + this.data = new ArrayList<Float>(data); + } + + /** + * Get the list of Floats in this component. + * + * @return An immutable view of the list of Floats. + */ + public List<Float> getValues() { + return Collections.unmodifiableList(data); + } + + /** + * Set the list of values in this component. + * + * @param newData + * The new set of values. + */ + public void setValues(List<Float> newData) { + this.data = new ArrayList<Float>(newData); + } + + /** + * Set the list of values in this component. + * + * @param newData + * The new set of values. + */ + public void setValues(float... newData) { + this.data = new ArrayList<Float>(); + for (float i : newData) { + data.add(i); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(data.size(), out); + for (Float next : data) { + writeFloat32(next.floatValue(), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + data.clear(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + data.add(readFloat32(in)); + } + } + + @Override + public String toString() { + return getName() + " = " + data.toString(); + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + data.clear(); + for (Float val : proto.getFloatList().getValuesList()) { + data.add(val); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + FloatListProto.Builder builder=FloatListProto.newBuilder(); + for (float next : data) { + builder.addValues(next); + } + return MessageComponentProto.newBuilder().setFloatList(builder).build(); + } + + // +// public static void main(String[] args) throws IOException { +// System.out.println("Test starts..."); +// FloatListComponent flc = new FloatListComponent("test1"); +// flc.setValues(new float[] { (float) 1.2, (float) 4444.1, +// (float) 12313.4112 }); +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// flc.write(out); +// ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); +// FloatListComponent flc2 = new FloatListComponent("test2"); +// flc2.read(in); +// List<Float> values1 = flc.getValues(); +// List<Float> values2 = flc2.getValues(); +// for (int i = 0; i < values1.size(); i++) { +// float f1 = values1.get(i); +// float f2 = values2.get(i); +// System.out.println(f1 + " , " + f2 + " => " + (f1 == f2)); +// } +// +// } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/IntComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/IntComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..ae8ceb8cbca3d6061aabc3b12449999a1fa541e6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/IntComponent.java @@ -0,0 +1,82 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.URN; +import rescuecore2.messages.AbstractMessageComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +/** + * An integer component to a message. + */ +public class IntComponent extends AbstractMessageComponent { + private int value; + + /** + * Construct an IntComponent with no content. + * + * @param name The name of the component. + */ + public IntComponent(URN name) { + super(name); + } + + /** + * Construct an IntComponent with a specific value. + * + * @param name The name of the component. + * @param value The value of this component. + */ + public IntComponent(URN name, int value) { + super(name); + this.value = value; + } + + /** + * Get the value of this message component. + * + * @return The value of the component. + */ + public int getValue() { + return value; + } + + /** + * Set the value of this message component. + * + * @param value The value of the component. + */ + public void setValue(int value) { + this.value = value; + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(value, out); + } + + @Override + public void read(InputStream in) throws IOException { + value = readInt32(in); + } + + @Override + public String toString() { + return getName() + " = " + value; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + value = proto.getIntValue(); + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setIntValue(value).build(); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/IntListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/IntListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..5a83f2217b60a2c405c0758431e4abda94807d6f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/IntListComponent.java @@ -0,0 +1,107 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.IntListProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +/** + A message component that is a list of integers. + */ +public class IntListComponent extends AbstractMessageComponent { + private List<Integer> data; + + /** + Construct an IntListComponent with no data. + @param name The name of the component. + */ + public IntListComponent(URN name) { + super(name); + data = new ArrayList<Integer>(); + } + + /** + Construct an IntListComponent with a list of integers. + @param name The name of the component. + @param data The data. + */ + public IntListComponent(URN name, List<Integer> data) { + super(name); + this.data = new ArrayList<Integer>(data); + } + + /** + Get the list of Integers in this component. + @return An immutable view of the list of Integers. + */ + public List<Integer> getValues() { + return Collections.unmodifiableList(data); + } + + /** + Set the list of values in this component. + @param newData The new set of values. + */ + public void setValues(List<Integer> newData) { + this.data = new ArrayList<Integer>(newData); + } + + /** + Set the list of values in this component. + @param newData The new set of values. + */ + public void setValues(int... newData) { + this.data = new ArrayList<Integer>(); + for (int i : newData) { + data.add(i); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(data.size(), out); + for (Integer next : data) { + writeInt32(next.intValue(), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + data.clear(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + data.add(readInt32(in)); + } + } + + @Override + public String toString() { + return getName() + " = " + data.toString(); + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + data.clear(); + for (Integer val : proto.getIntList().getValuesList()) { + data.add(val); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + IntListProto.Builder builder=IntListProto.newBuilder(); + for (Integer next : data) { + builder.addValues(next); + } + return MessageComponentProto.newBuilder().setIntList(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/RawDataComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/RawDataComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..4758e650532d39f5ab2257a22280e79723794360 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/RawDataComponent.java @@ -0,0 +1,86 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.readBytes; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +import java.io.InputStream; +import java.io.OutputStream; + +import com.google.protobuf.ByteString; + +import java.io.IOException; + +/** + A raw data component to a message. + */ +public class RawDataComponent extends AbstractMessageComponent { + private byte[] data; + + /** + Construct a RawDataComponent with no content. + @param name The name of the component. + */ + public RawDataComponent(URN name) { + super(name); + } + + /** + Construct a RawDataComponent with some data. + @param name The name of the component. + @param data The data of this component. + */ + public RawDataComponent(URN name, byte[] data) { + super(name); + this.data = new byte[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + + /** + Get the data in this message component. + @return A copy of the data. + */ + public byte[] getData() { + byte[] result = new byte[data.length]; + System.arraycopy(data, 0, result, 0, data.length); + return result; + } + + /** + Set the data for this message component. + @param newData The new data. + */ + public void setData(byte[] newData) { + this.data = new byte[newData.length]; + System.arraycopy(newData, 0, this.data, 0, newData.length); + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(data.length, out); + out.write(data); + } + + @Override + public void read(InputStream in) throws IOException { + data = readBytes(readInt32(in), in); + } + + @Override + public String toString() { + return getName() + " = " + data.length + " bytes of raw data"; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + data = proto.getRawData().toByteArray(); + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setRawData(ByteString.copyFrom((byte[]) data)).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/StringComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/StringComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..2ed64c32db16d628c06d659c335bd415557036e6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/StringComponent.java @@ -0,0 +1,78 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readString; +import static rescuecore2.misc.EncodingTools.writeString; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + A string component to a message. + */ +public class StringComponent extends AbstractMessageComponent { + private String value; + + /** + Construct a StringComponent with no content. + @param name The name of the component. + */ + public StringComponent(URN name) { + super(name); + value = ""; + } + + /** + Construct a StringComponent with a specific value. + @param name The name of the component. + @param value The value of this component. + */ + public StringComponent(URN name, String value) { + super(name); + this.value = value; + } + + /** + Get the value of this message component. + @return The value of the component. + */ + public String getValue() { + return value; + } + + /** + Set the value of this message component. + @param value The value of the component. + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public void write(OutputStream out) throws IOException { + writeString(value, out); + } + + @Override + public void read(InputStream in) throws IOException { + value = readString(in); + } + + @Override + public String toString() { + return getName() + " = " + value; + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + value = proto.getStringValue(); + } + + @Override + public MessageComponentProto toMessageComponentProto() { + return MessageComponentProto.newBuilder().setStringValue(value).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/components/StringListComponent.java b/modules/rescuecore2/src/rescuecore2/messages/components/StringListComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..8fc2f000884c0809222452365b81e6b415436877 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/components/StringListComponent.java @@ -0,0 +1,109 @@ +package rescuecore2.messages.components; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readString; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeString; + +import rescuecore2.messages.AbstractMessageComponent;import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto; +import rescuecore2.messages.protobuf.RCRSProto.StrListProto; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +/** + A message component that is a list of strings. + */ +public class StringListComponent extends AbstractMessageComponent { + private List<String> data; + + /** + Construct an StringListComponent with no data. + @param name The name of the component. + */ + public StringListComponent(URN name) { + super(name); + data = new ArrayList<String>(); + } + + /** + Construct an StringListComponent with a list of strings. + @param name The name of the component. + @param data The data. + */ + public StringListComponent(URN name, List<String> data) { + super(name); + this.data = new ArrayList<String>(data); + } + + /** + Get the list of Strings in this component. + @return An immutable view of the list of Strings. + */ + public List<String> getValues() { + return Collections.unmodifiableList(data); + } + + /** + Set the list of values in this component. + @param newData The new set of values. + */ + public void setValues(List<String> newData) { + this.data = new ArrayList<String>(newData); + } + + /** + Set the list of values in this component. + @param newData The new set of values. + */ + public void setValues(String... newData) { + this.data = new ArrayList<String>(); + for (String s : newData) { + data.add(s); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(data.size(), out); + for (String next : data) { + writeString(next, out); + } + } + + @Override + public void read(InputStream in) throws IOException { + data.clear(); + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + data.add(readString(in)); + } + } + + @Override + public String toString() { + return getName() + " = " + data.toString(); + } + + @Override + public void fromMessageComponentProto(MessageComponentProto proto) { + data.clear(); + for (String val : proto.getStringList().getValuesList()) { + data.add(val); + } + } + + @Override + public MessageComponentProto toMessageComponentProto() { + StrListProto.Builder builder=StrListProto.newBuilder(); + for (String next : data) { + builder.addValues(next); + } + return MessageComponentProto.newBuilder().setStringList(builder).build(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/AKAcknowledge.java b/modules/rescuecore2/src/rescuecore2/messages/control/AKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..5e2ee10f5443d7d9e4fcd2cab91c1f8447537003 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/AKAcknowledge.java @@ -0,0 +1,72 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * A message for acknowleding a connection to the kernel. + */ +public class AKAcknowledge extends AbstractMessage { + private IntComponent requestID; + private EntityIDComponent agentID; + + /** + * An AKAcknowledge message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKAcknowledge(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * AKAcknowledge message with specific request ID and agent ID components. + * + * @param requestID The request ID. + * @param agentID The agent ID. + */ + public AKAcknowledge(int requestID, EntityID agentID) { + this(); + this.requestID.setValue(requestID); + this.agentID.setValue(agentID); + } + + private AKAcknowledge() { + super(ControlMessageURN.AK_ACKNOWLEDGE); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + agentID = new EntityIDComponent(ControlMessageComponentURN.AgentID); + addMessageComponent(requestID); + addMessageComponent(agentID); + } + + public AKAcknowledge(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the request ID of this acknowledgement. + * + * @return The request ID component. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the agent ID of this acknowledgement. + * + * @return The agent ID component. + */ + public EntityID getAgentID() { + return agentID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/AKConnect.java b/modules/rescuecore2/src/rescuecore2/messages/control/AKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..d3cfead35d6358bd0d2071af94a1b6386d1f86c6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/AKConnect.java @@ -0,0 +1,119 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.IntListComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.components.StringListComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.registry.Registry; + +/** + * A message for connecting an agent to the kernel. + */ +public class AKConnect extends AbstractMessage { + private IntComponent requestID; + private IntComponent version; + private StringComponent agentName; + private StringListComponent requestedStrEntityTypes; + private IntListComponent requestedIntEntityTypes; + + /** + * An AKConnect message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKConnect(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * An AKConnect with particular version, requestID and requested entity types. + * + * @param requestID The request ID. + * @param version The version number. + * @param agentName The name of the agent. + * @param requestedEntityTypes The set of requested entity types. + */ + public AKConnect(int requestID, int version, String agentName, int[] requestedEntityTypes) { + this(); + this.requestID.setValue(requestID); + this.version.setValue(version); + this.agentName.setValue(agentName); + this.requestedIntEntityTypes.setValues(requestedEntityTypes); + + ArrayList<String> strRequests = new ArrayList<String>(); + for (int request : this.requestedIntEntityTypes.getValues()) { + strRequests.add(Registry.getCurrentRegistry().toURN_Str(request)); + } + this.requestedStrEntityTypes.setValues(strRequests); + } + + private AKConnect() { + super(ControlMessageURN.AK_CONNECT); + this.requestID = new IntComponent(ControlMessageComponentURN.RequestID); + this.version = new IntComponent(ControlMessageComponentURN.Version); + this.agentName = new StringComponent(ControlMessageComponentURN.Name); + this.requestedIntEntityTypes = new IntListComponent(ControlMessageComponentURN.RequestedEntityTypes); + this.requestedStrEntityTypes = new StringListComponent(ControlMessageComponentURN.RequestedEntityTypes); + addMessageComponent(requestID); + addMessageComponent(version); + addMessageComponent(agentName); + + switch (this.version.getValue()) { + case 1: + addMessageComponent(requestedStrEntityTypes); + break; + default: + addMessageComponent(requestedIntEntityTypes); + } + } + + public AKConnect(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the version number of this request. + * + * @return The version number. + */ + public int getVersion() { + return version.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the name of the agent making this request. + * + * @return The agent name. + */ + public String getAgentName() { + return agentName.getValue(); + } + + /** + * Get the requested entity types. + * + * @return The requested entity types. + */ + public List<Integer> getRequestedEntityTypes() { + return requestedIntEntityTypes.getValues(); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentFactory.java b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e2f77e710bcd8ab28a5a4d5deb9fcebb16fa7598 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentFactory.java @@ -0,0 +1,15 @@ +package rescuecore2.messages.control; + +import rescuecore2.registry.AbstractMessageComponentFactory; + +/** + * A factory for control messages. + */ +public final class ControlMessageComponentFactory extends AbstractMessageComponentFactory<ControlMessageComponentURN> { + /** Singleton instance. */ + public static final ControlMessageComponentFactory INSTANCE = new ControlMessageComponentFactory(); + + private ControlMessageComponentFactory() { + super(ControlMessageComponentURN.class); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentURN.java b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentURN.java new file mode 100644 index 0000000000000000000000000000000000000000..ba19cdc497fbc4dd2e5ecc26047e025e2b272554 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageComponentURN.java @@ -0,0 +1,57 @@ +package rescuecore2.messages.control; + +import static rescuecore2.Constants.CONTROL_MSG_COMPONENT_URN_PREFIX; + +import java.util.Map; + +import rescuecore2.URN; + +public enum ControlMessageComponentURN implements URN { + RequestID(CONTROL_MSG_COMPONENT_URN_PREFIX | 1, "Request ID"), + AgentID(CONTROL_MSG_COMPONENT_URN_PREFIX | 2, "Agent ID"), Version(CONTROL_MSG_COMPONENT_URN_PREFIX | 3, "Version"), + Name(CONTROL_MSG_COMPONENT_URN_PREFIX | 4, "Name"), + RequestedEntityTypes(CONTROL_MSG_COMPONENT_URN_PREFIX | 5, "Requested entity types"), + SimulatorID(CONTROL_MSG_COMPONENT_URN_PREFIX | 6, "Simulator ID"), + RequestNumber(CONTROL_MSG_COMPONENT_URN_PREFIX | 7, "Request number"), + NumberOfIDs(CONTROL_MSG_COMPONENT_URN_PREFIX | 8, "Number of IDs"), + NewEntityIDs(CONTROL_MSG_COMPONENT_URN_PREFIX | 9, "New entity IDs"), + Reason(CONTROL_MSG_COMPONENT_URN_PREFIX | 10, "Reason"), Entities(CONTROL_MSG_COMPONENT_URN_PREFIX | 11, "Entities"), + ViewerID(CONTROL_MSG_COMPONENT_URN_PREFIX | 12, "ViewerID"), + AgentConfig(CONTROL_MSG_COMPONENT_URN_PREFIX | 13, "Agent config"), + Time(CONTROL_MSG_COMPONENT_URN_PREFIX | 14, "Time"), Updates(CONTROL_MSG_COMPONENT_URN_PREFIX | 15, "Updates"), + Hearing(CONTROL_MSG_COMPONENT_URN_PREFIX | 16, "Hearing"), + INTENSITIES(CONTROL_MSG_COMPONENT_URN_PREFIX | 17, "INTENSITIES"), + TIMES(CONTROL_MSG_COMPONENT_URN_PREFIX | 18, "TIMES"), ID(CONTROL_MSG_COMPONENT_URN_PREFIX | 19, "ID"), + Commands(CONTROL_MSG_COMPONENT_URN_PREFIX | 20, "Commands"), + SimulatorConfig(CONTROL_MSG_COMPONENT_URN_PREFIX | 21, "Simulator config"), + Changes(CONTROL_MSG_COMPONENT_URN_PREFIX | 22, "Changes"); + + private int urnId; + private String urnStr; + public static final Map<Integer, ControlMessageComponentURN> MAP = URN.generateMap(ControlMessageComponentURN.class); + public static final Map<String, ControlMessageComponentURN> MAPSTR = URN + .generateMapStr(ControlMessageComponentURN.class); + + ControlMessageComponentURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return urnId; + } + + @Override + public String getURNStr() { + return urnStr; + } + + public static ControlMessageComponentURN fromInt(int urnId) { + return MAP.get(urnId); + } + + public static ControlMessageComponentURN fromString(String urnStr) { + return MAPSTR.get(urnStr); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageFactory.java b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..df300da6ac5bc4560c6fa440fb687de40a6dfdf8 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageFactory.java @@ -0,0 +1,137 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.registry.AbstractMessageFactory; + +/** + * A factory for control messages. + */ +public final class ControlMessageFactory extends AbstractMessageFactory<ControlMessageURN> { + /** Singleton instance. */ + public static final ControlMessageFactory INSTANCE = new ControlMessageFactory(); + + private ControlMessageFactory() { + super(ControlMessageURN.class); + } + + @Override + public Message makeMessage(ControlMessageURN urn, InputStream data) throws IOException { + switch (urn) { + case KG_CONNECT: + return new KGConnect(data); + case KG_ACKNOWLEDGE: + return new KGAcknowledge(data); + case GK_CONNECT_OK: + return new GKConnectOK(data); + case GK_CONNECT_ERROR: + return new GKConnectError(data); + case SK_CONNECT: + return new SKConnect(data); + case SK_ACKNOWLEDGE: + return new SKAcknowledge(data); + case SK_UPDATE: + return new SKUpdate(data); + case KS_CONNECT_OK: + return new KSConnectOK(data); + case KS_CONNECT_ERROR: + return new KSConnectError(data); + case KS_AFTERSHOCKS_INFO: + return new KSAfterShocksInfo(data); + case KS_UPDATE: + return new KSUpdate(data); + case KS_COMMANDS: + return new KSCommands(data); + case VK_CONNECT: + return new VKConnect(data); + case VK_ACKNOWLEDGE: + return new VKAcknowledge(data); + case KV_CONNECT_OK: + return new KVConnectOK(data); + case KV_CONNECT_ERROR: + return new KVConnectError(data); + case KV_TIMESTEP: + return new KVTimestep(data); + case AK_CONNECT: + return new AKConnect(data); + case AK_ACKNOWLEDGE: + return new AKAcknowledge(data); + case KA_CONNECT_OK: + return new KAConnectOK(data); + case KA_CONNECT_ERROR: + return new KAConnectError(data); + case KA_SENSE: + return new KASense(data); + case SHUTDOWN: + return new Shutdown(data); + case ENTITY_ID_REQUEST: + return new EntityIDRequest(data); + case ENTITY_ID_RESPONSE: + return new EntityIDResponse(data); + default: + Logger.warn("Unrecognised message urn: " + urn); + return null; + } + } + + @Override + public Message makeMessage(ControlMessageURN urn, MessageProto proto) { + switch (urn) { + case KG_CONNECT: + return new KGConnect(proto); + case KG_ACKNOWLEDGE: + return new KGAcknowledge(proto); + case GK_CONNECT_OK: + return new GKConnectOK(proto); + case GK_CONNECT_ERROR: + return new GKConnectError(proto); + case SK_CONNECT: + return new SKConnect(proto); + case SK_ACKNOWLEDGE: + return new SKAcknowledge(proto); + case SK_UPDATE: + return new SKUpdate(proto); + case KS_CONNECT_OK: + return new KSConnectOK(proto); + case KS_CONNECT_ERROR: + return new KSConnectError(proto); + case KS_UPDATE: + return new KSUpdate(proto); + case KS_COMMANDS: + return new KSCommands(proto); + case VK_CONNECT: + return new VKConnect(proto); + case VK_ACKNOWLEDGE: + return new VKAcknowledge(proto); + case KV_CONNECT_OK: + return new KVConnectOK(proto); + case KV_CONNECT_ERROR: + return new KVConnectError(proto); + case KV_TIMESTEP: + return new KVTimestep(proto); + case AK_CONNECT: + return new AKConnect(proto); + case AK_ACKNOWLEDGE: + return new AKAcknowledge(proto); + case KA_CONNECT_OK: + return new KAConnectOK(proto); + case KA_CONNECT_ERROR: + return new KAConnectError(proto); + case KA_SENSE: + return new KASense(proto); + case SHUTDOWN: + return new Shutdown(proto); + case ENTITY_ID_REQUEST: + return new EntityIDRequest(proto); + case ENTITY_ID_RESPONSE: + return new EntityIDResponse(proto); + default: + Logger.warn("Unrecognised message urn: " + urn); + return null; + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageURN.java b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageURN.java new file mode 100644 index 0000000000000000000000000000000000000000..98e6f9245f078d22d36df12ddb264c0332a64ba9 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/ControlMessageURN.java @@ -0,0 +1,100 @@ +package rescuecore2.messages.control; + +import static rescuecore2.Constants.CONTROL_MSG_URN_PREFIX; + +import java.util.Map; + +import rescuecore2.URN; + +/** + * URNs for control messages. + */ +public enum ControlMessageURN implements URN { + /** Kernel-GIS connect. */ + KG_CONNECT(CONTROL_MSG_URN_PREFIX | 1, "urn:rescuecore2:messages.control:kg_connect"), + /** Kernel-GIS acknowledge. */ + KG_ACKNOWLEDGE(CONTROL_MSG_URN_PREFIX | 2, "urn:rescuecore2:messages.control:kg_acknowledge"), + /** GIS-Kernel OK. */ + GK_CONNECT_OK(CONTROL_MSG_URN_PREFIX | 3, "urn:rescuecore2:messages.control:gk_connect_ok"), + /** GIS-Kernel error. */ + GK_CONNECT_ERROR(CONTROL_MSG_URN_PREFIX | 4, "urn:rescuecore2:messages.control:gk_connect_error"), + /** Simulator-Kernel connect. */ + SK_CONNECT(CONTROL_MSG_URN_PREFIX | 5, "urn:rescuecore2:messages.control:sk_connect"), + /** Simulator-Kernel acknowledge. */ + SK_ACKNOWLEDGE(CONTROL_MSG_URN_PREFIX | 6, "urn:rescuecore2:messages.control:sk_acknowledge"), + /** Simulator-Kernel update. */ + SK_UPDATE(CONTROL_MSG_URN_PREFIX | 7, "urn:rescuecore2:messages.control:sk_update"), + /** Kernel-Simulator OK. */ + KS_CONNECT_OK(CONTROL_MSG_URN_PREFIX | 8, "urn:rescuecore2:messages.control:ks_connect_ok"), + /** Kernel-Simulator error. */ + KS_CONNECT_ERROR(CONTROL_MSG_URN_PREFIX | 9, "urn:rescuecore2:messages.control:ks_connect_error"), + /** Kernel update broadcast. */ + KS_UPDATE(CONTROL_MSG_URN_PREFIX | 10, "urn:rescuecore2:messages.control:ks_update"), + /** Kernel commands broadcast. */ + KS_COMMANDS(CONTROL_MSG_URN_PREFIX | 11, "urn:rescuecore2:messages.control:ks_commands"), + /** Kernel commands aftershocks info. */ + KS_AFTERSHOCKS_INFO(CONTROL_MSG_URN_PREFIX | 12, "urn:rescuecore2:messages.control:ks_aftershocks_info"), + + /** Viewer-Kernel connect. */ + VK_CONNECT(CONTROL_MSG_URN_PREFIX | 13, "urn:rescuecore2:messages.control:vk_connect"), + /** Viewer-Kernel acknowledge. */ + VK_ACKNOWLEDGE(CONTROL_MSG_URN_PREFIX | 14, "urn:rescuecore2:messages.control:vk_acknowledge"), + /** Kernel-Viewer OK. */ + KV_CONNECT_OK(CONTROL_MSG_URN_PREFIX | 15, "urn:rescuecore2:messages.control:kv_connect_ok"), + /** Kernel-Viewer error. */ + KV_CONNECT_ERROR(CONTROL_MSG_URN_PREFIX | 16, "urn:rescuecore2:messages.control:kv_connect_error"), + /** Kernel-Viewer timestep. */ + KV_TIMESTEP(CONTROL_MSG_URN_PREFIX | 17, "urn:rescuecore2:messages.control:kv_timestep"), + + /** Agent-Kernel connect. */ + AK_CONNECT(CONTROL_MSG_URN_PREFIX | 18, "urn:rescuecore2:messages.control:ak_connect"), + /** Agent-Kernel acknowledge. */ + AK_ACKNOWLEDGE(CONTROL_MSG_URN_PREFIX | 19, "urn:rescuecore2:messages.control:ak_acknowledge"), + /** Kernel-Agent OK. */ + KA_CONNECT_OK(CONTROL_MSG_URN_PREFIX | 20, "urn:rescuecore2:messages.control:ka_connect_ok"), + /** Kernel-Agent error. */ + KA_CONNECT_ERROR(CONTROL_MSG_URN_PREFIX | 21, "urn:rescuecore2:messages.control:ka_connect_error"), + /** Kernel-Agent perception update. */ + KA_SENSE(CONTROL_MSG_URN_PREFIX | 22, "urn:rescuecore2:messages.control:ka_sense"), + + /** Shutdown message. */ + SHUTDOWN(CONTROL_MSG_URN_PREFIX | 23, "urn:rescuecore2:messages.control:shutdown"), + /** New EntityID request. */ + ENTITY_ID_REQUEST(CONTROL_MSG_URN_PREFIX | 24, "urn:rescuecore2:messages.control:entity_id_request"), + /** New EntityID response. */ + ENTITY_ID_RESPONSE(CONTROL_MSG_URN_PREFIX | 25, "urn:rescuecore2:messages.control:entity_id_response"); + + private int urnId; + private String urnStr; + public static final Map<Integer, ControlMessageURN> MAP = URN.generateMap(ControlMessageURN.class); + public static final Map<String, ControlMessageURN> MAPSTR = URN.generateMapStr(ControlMessageURN.class); + + private ControlMessageURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return urnId; + } + + @Override + public String getURNStr() { + return urnStr; + } + + /** + * Convert a String to a ControlMessageURN. + * + * @param s The String to convert. + * @return A ControlMessageURN. + */ + public static ControlMessageURN fromInt(int s) { + return MAP.get(s); + } + + public static ControlMessageURN fromString(String urn) { + return MAPSTR.get(urn); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDRequest.java b/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..dab813512991d2fd6e8ec7adeea15c2b93aaaf9c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDRequest.java @@ -0,0 +1,84 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message from a simulator requesting a new EntityID. + */ +public class EntityIDRequest extends AbstractMessage { + private IntComponent simID; + private IntComponent requestID; + private IntComponent count; + + /** + * Construct an EntityIDRequest message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public EntityIDRequest(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an EntityIDRequest message. + * + * @param simID The ID of the simulator making the request. + * @param requestID A unique ID number for this request. + * @param number The number of IDs requested. + */ + public EntityIDRequest(int simID, int requestID, int number) { + this(); + this.simID.setValue(simID); + this.requestID.setValue(requestID); + this.count.setValue(number); + } + + private EntityIDRequest() { + super(ControlMessageURN.ENTITY_ID_REQUEST); + simID = new IntComponent(ControlMessageComponentURN.SimulatorID); + requestID = new IntComponent(ControlMessageComponentURN.RequestNumber); + count = new IntComponent(ControlMessageComponentURN.NumberOfIDs); + addMessageComponent(simID); + addMessageComponent(requestID); + addMessageComponent(count); + } + + public EntityIDRequest(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the ID of the simulator making the request. + * + * @return The simulator ID. + */ + public int getSimulatorID() { + return simID.getValue(); + } + + /** + * Get the ID of this request. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the number of IDs requested. + * + * @return The number of IDs requested. + */ + public int getCount() { + return count.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDResponse.java b/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..0a4fe54b7e134555b265290743c182586c954a14 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/EntityIDResponse.java @@ -0,0 +1,99 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.EntityIDListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * A message from a the kernel supplying a new EntityID. + */ +public class EntityIDResponse extends AbstractMessage { + private IntComponent simID; + private IntComponent requestID; + private EntityIDListComponent newID; + + /** + * Construct an EntityIDResponse message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public EntityIDResponse(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an EntityIDResponse message. + * + * @param simID The ID of the simulator making the request. + * @param requestID A unique ID number for this request. + * @param ids The new EntityIDs. + */ + public EntityIDResponse(int simID, int requestID, EntityID... ids) { + this(simID, requestID, Arrays.asList(ids)); + } + + /** + * Construct an EntityIDResponse message. + * + * @param simID The ID of the simulator making the request. + * @param requestID A unique ID number for this request. + * @param ids The new EntityIDs. + */ + public EntityIDResponse(int simID, int requestID, List<EntityID> ids) { + this(); + this.simID.setValue(simID); + this.requestID.setValue(requestID); + this.newID.setIDs(ids); + } + + private EntityIDResponse() { + super(ControlMessageURN.ENTITY_ID_RESPONSE); + simID = new IntComponent(ControlMessageComponentURN.SimulatorID); + requestID = new IntComponent(ControlMessageComponentURN.RequestNumber); + newID = new EntityIDListComponent(ControlMessageComponentURN.NewEntityIDs); + addMessageComponent(simID); + addMessageComponent(requestID); + addMessageComponent(newID); + } + + public EntityIDResponse(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the new entity IDs. + * + * @return The new entity IDs. + */ + public List<EntityID> getEntityIDs() { + return newID.getIDs(); + } + + /** + * Get the ID of the simulator making the request. + * + * @return The simulator ID. + */ + public int getSimulatorID() { + return simID.getValue(); + } + + /** + * Get the ID of this request. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectError.java b/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..af912b4655349061d6e7e175d6ba195504c52ddb --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectError.java @@ -0,0 +1,56 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for signalling an unsuccessful connection to the GIS. + */ +public class GKConnectError extends AbstractMessage { + private StringComponent reason; + + /** + * A GKConnectError message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public GKConnectError(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A GKConnectError with a specified reason. + * + * @param reason The reason for the error. + */ + public GKConnectError(String reason) { + this(); + this.reason.setValue(reason); + } + + private GKConnectError() { + super(ControlMessageURN.GK_CONNECT_ERROR); + reason = new StringComponent(ControlMessageComponentURN.Reason); + addMessageComponent(reason); + } + + public GKConnectError(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the reason for the error. + * + * @return The reason for the error. + */ + public String getReason() { + return reason.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectOK.java b/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..8706ad0092f3e88ca6959512aeca9f37473cf04a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/GKConnectOK.java @@ -0,0 +1,59 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.EntityListComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.Entity; + +/** + * A message for signalling a successful connection to the GIS. + */ +public class GKConnectOK extends AbstractMessage { + private EntityListComponent world; + + /** + * A GKConnectOK message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public GKConnectOK(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A GKConnectOK with a specified entity list. + * + * @param entities The entities to send. + */ + public GKConnectOK(Collection<? extends Entity> entities) { + this(); + world.setEntities(entities); + } + + private GKConnectOK() { + super(ControlMessageURN.GK_CONNECT_OK); + world = new EntityListComponent(ControlMessageComponentURN.Entities); + addMessageComponent(world); + } + + public GKConnectOK(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the entity list. + * + * @return All entities. + */ + public List<Entity> getEntities() { + return world.getEntities(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectError.java b/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..96b076de58653036dd2067cf9e77c2d7871ffb13 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectError.java @@ -0,0 +1,71 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for signalling an unsuccessful connection to the kernel. + */ +public class KAConnectError extends AbstractMessage { + private IntComponent requestID; + private StringComponent reason; + + /** + * A KAConnectError message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KAConnectError(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A KAConnectError with specified request ID and reason. + * + * @param id The ID of the request that failed. + * @param message The reason for the error. + */ + public KAConnectError(int id, String message) { + this(); + requestID.setValue(id); + reason.setValue(message); + } + + private KAConnectError() { + super(ControlMessageURN.KA_CONNECT_ERROR); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + reason = new StringComponent(ControlMessageComponentURN.Reason); + addMessageComponent(requestID); + addMessageComponent(reason); + } + + public KAConnectError(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the request ID for the message. + * + * @return The request ID for the message. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the reason for the error. + * + * @return The reason for the error. + */ + public String getReason() { + return reason.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectOK.java b/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..fadb0d415926c0f6a59a6ed2a35b23df7bb2db13 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KAConnectOK.java @@ -0,0 +1,106 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.EntityListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * A message for signalling a successful connection to the kernel. + */ +public class KAConnectOK extends AbstractMessage { + private IntComponent requestID; + private EntityIDComponent agentID; + private EntityListComponent world; + private ConfigComponent config; + + /** + * A KAConnectOK message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KAConnectOK(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KAConnectOK message. + * + * @param requestID The request ID. + * @param agentID The ID of the Entity that the agent will be controlling. + * @param allEntities All Entities that the agent knows about, including the + * controlled object. + * @param config The Config that the agent knows about. + */ + public KAConnectOK(int requestID, EntityID agentID, Collection<? extends Entity> allEntities, Config config) { + this(); + this.requestID.setValue(requestID); + this.agentID.setValue(agentID); + this.world.setEntities(allEntities); + this.config.setConfig(config); + } + + private KAConnectOK() { + super(ControlMessageURN.KA_CONNECT_OK); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + agentID = new EntityIDComponent(ControlMessageComponentURN.AgentID); + world = new EntityListComponent(ControlMessageComponentURN.Entities); + config = new ConfigComponent(ControlMessageComponentURN.AgentConfig); + addMessageComponent(requestID); + addMessageComponent(agentID); + addMessageComponent(world); + addMessageComponent(config); + } + + public KAConnectOK(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the requestID for this message. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the ID of the agent-controlled object. + * + * @return The agent ID. + */ + public EntityID getAgentID() { + return agentID.getValue(); + } + + /** + * Get the entity list. + * + * @return All entities in the world. + */ + public Collection<Entity> getEntities() { + return world.getEntities(); + } + + /** + * Get the Config. + * + * @return The agent config. + */ + public Config getConfig() { + return config.getConfig(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KASense.java b/modules/rescuecore2/src/rescuecore2/messages/control/KASense.java new file mode 100644 index 0000000000000000000000000000000000000000..0e32c71069f0303a68d723f44b2965d0a3f900c8 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KASense.java @@ -0,0 +1,105 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.Command; +import rescuecore2.messages.components.ChangeSetComponent; +import rescuecore2.messages.components.CommandListComponent; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +/** + * A message for signalling a perception update for an agent. + */ +public class KASense extends AbstractMessage { + private EntityIDComponent agentID; + private IntComponent time; + private ChangeSetComponent updates; + private CommandListComponent hear; + + /** + * A KASense message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KASense(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KASense message. + * + * @param agentID The ID of the Entity that is receiving the update. + * @param time The timestep of the simulation. + * @param changes All changes that the agent can perceive. + * @param hear The messages that the agent can hear. + */ + public KASense(EntityID agentID, int time, ChangeSet changes, Collection<? extends Command> hear) { + this(); + this.agentID.setValue(agentID); + this.time.setValue(time); + this.updates.setChangeSet(changes); + this.hear.setCommands(hear); + } + + private KASense() { + super(ControlMessageURN.KA_SENSE); + agentID = new EntityIDComponent(ControlMessageComponentURN.AgentID); + time = new IntComponent(ControlMessageComponentURN.Time); + updates = new ChangeSetComponent(ControlMessageComponentURN.Updates); + hear = new CommandListComponent(ControlMessageComponentURN.Hearing); + addMessageComponent(agentID); + addMessageComponent(time); + addMessageComponent(updates); + addMessageComponent(hear); + } + + public KASense(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the ID of the agent. + * + * @return The agent ID. + */ + public EntityID getAgentID() { + return agentID.getValue(); + } + + /** + * Get the time. + * + * @return The time. + */ + public int getTime() { + return time.getValue(); + } + + /** + * Get the changed entities. + * + * @return The ChangeSet. + */ + public ChangeSet getChangeSet() { + return updates.getChangeSet(); + } + + /** + * Get the messages the agent can hear. + * + * @return The agent messages. + */ + public Collection<Command> getHearing() { + return hear.getCommands(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KGAcknowledge.java b/modules/rescuecore2/src/rescuecore2/messages/control/KGAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..81c09e92a841af3c4720f0de53902832d5dc2b45 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KGAcknowledge.java @@ -0,0 +1,35 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for acknowleding a connection to the GIS. + */ +public class KGAcknowledge extends AbstractMessage { + /** + * A KGAcknowledge message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KGAcknowledge(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A KGAcknowldge message. + */ + public KGAcknowledge() { + super(ControlMessageURN.KG_ACKNOWLEDGE); + } + + public KGAcknowledge(MessageProto proto) { + this(); + fromMessageProto(proto); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KGConnect.java b/modules/rescuecore2/src/rescuecore2/messages/control/KGConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..b1ad44582f830011a9fd85d3a91adcb2109663f4 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KGConnect.java @@ -0,0 +1,56 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for connecting to the GIS. + */ +public class KGConnect extends AbstractMessage { + private IntComponent version; + + /** + * A KGConnect message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KGConnect(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A KGConnect message with a specified version number. + * + * @param version The version number field. + */ + public KGConnect(int version) { + this(); + this.version.setValue(version); + } + + private KGConnect() { + super(ControlMessageURN.KG_CONNECT); + version = new IntComponent(ControlMessageComponentURN.Version, 0); + addMessageComponent(version); + } + + public KGConnect(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the version number of the message. + * + * @return The version number field. + */ + public int getVersion() { + return version.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KSAfterShocksInfo.java b/modules/rescuecore2/src/rescuecore2/messages/control/KSAfterShocksInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..5cdff5dd5a6629ff1043703e12812eea8b631b59 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KSAfterShocksInfo.java @@ -0,0 +1,108 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.FloatListComponent; +import rescuecore2.messages.components.IntListComponent; +import rescuecore2.scenario.Scenario; +import rescuecore2.scenario.compatibilities.CollapseSimCompatibaleScenarioV1_1; +import rescuecore2.scenario.exceptions.UncompatibleScenarioException; + +/** + * + * The Message class that contains after shocks' information and is sent from + * kernel to collapse simulator in early cycles + * + * @author Salim + * + */ +public class KSAfterShocksInfo extends AbstractMessage { + private IntListComponent times; + private FloatListComponent intensities; + + /** + * Reads the message from the inputstream and initiates its data. + * + * @param in + * @throws IOException + */ + public KSAfterShocksInfo(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Creates a message from the input scenario. + * + * @param aftershocks + * @throws UncompatibleScenarioException + */ + public KSAfterShocksInfo(Scenario scenario) throws UncompatibleScenarioException { + this(); + HashMap<Integer, Float> aftershocks = null; + if (scenario instanceof CollapseSimCompatibaleScenarioV1_1) { + aftershocks = ((CollapseSimCompatibaleScenarioV1_1) scenario).getAftershocks(); + } else { + UncompatibleScenarioException e = new UncompatibleScenarioException(); + throw e; + } + List<Integer> times = new ArrayList<Integer>(); + List<Float> intensities = new ArrayList<Float>(); + for (Entry<Integer, Float> e : aftershocks.entrySet()) { + times.add(e.getKey()); + intensities.add(e.getValue()); + } + this.times.setValues(times); + this.intensities.setValues(intensities); + + } + + /** + * Creates a message from the input map. + * + * @param aftershocks + */ + public KSAfterShocksInfo(HashMap<Integer, Float> aftershocks) { + this(); + List<Integer> times = new ArrayList<Integer>(); + List<Float> intensities = new ArrayList<Float>(); + for (Entry<Integer, Float> e : aftershocks.entrySet()) { + times.add(e.getKey()); + intensities.add(e.getValue()); + } + this.times.setValues(times); + this.intensities.setValues(intensities); + + } + + /** + * Initiates an empty Message + */ + protected KSAfterShocksInfo() { + super(ControlMessageURN.KS_AFTERSHOCKS_INFO); + intensities = new FloatListComponent(ControlMessageComponentURN.INTENSITIES); + times = new IntListComponent(ControlMessageComponentURN.TIMES); + addMessageComponent(times); + addMessageComponent(intensities); + } + + /** + * Returns a map containing aftershocks' information. Key and value of the map + * are time and intensity. + * + * @return float + */ + public HashMap<Integer, Float> getAftershocks() { + HashMap<Integer, Float> map = new HashMap<Integer, Float>(); + for (int i = 0; i < times.getValues().size(); i++) { + map.put(times.getValues().get(i), intensities.getValues().get(i)); + } + return map; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KSCommands.java b/modules/rescuecore2/src/rescuecore2/messages/control/KSCommands.java new file mode 100644 index 0000000000000000000000000000000000000000..cdef4d150ac7c0372cdfad1d08753e758e6282ff --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KSCommands.java @@ -0,0 +1,89 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.Command; +import rescuecore2.messages.components.CommandListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message containing a list of agent commands. This is sent from the kernel + * to all simulators. + */ +public class KSCommands extends AbstractMessage { + private IntComponent id; + private IntComponent time; + private CommandListComponent commands; + + /** + * A KSCommands message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KSCommands(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KSCommands message. + * + * @param id The id of the simulator receiving the update. + * @param time The timestep of the simulation. + * @param commands All agent Commands. + */ + public KSCommands(int id, int time, Collection<? extends Command> commands) { + this(); + this.id.setValue(id); + this.time.setValue(time); + this.commands.setCommands(commands); + } + + private KSCommands() { + super(ControlMessageURN.KS_COMMANDS); + id = new IntComponent(ControlMessageComponentURN.ID); + time = new IntComponent(ControlMessageComponentURN.Time); + commands = new CommandListComponent(ControlMessageComponentURN.Commands); + addMessageComponent(id); + addMessageComponent(time); + addMessageComponent(commands); + } + + public KSCommands(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the id of the component that this message is addressed to. + * + * @return The ID of the target component. + */ + public int getTargetID() { + return id.getValue(); + } + + /** + * Get the time of the simulation. + * + * @return The simulation time. + */ + public int getTime() { + return time.getValue(); + } + + /** + * Get the list of agent commands. + * + * @return The agent commands. + */ + public List<Command> getCommands() { + return commands.getCommands(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectError.java b/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..f358b0230a75e694f4e03c5fb5f0ba64f4ac6692 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectError.java @@ -0,0 +1,71 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for signalling an unsuccessful connection to the kernel. + */ +public class KSConnectError extends AbstractMessage { + private IntComponent requestID; + private StringComponent reason; + + /** + * A KSConnectError message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KSConnectError(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A KSConnectError with specified request ID and reason. + * + * @param requestID The request ID. + * @param message The reason for the error. + */ + public KSConnectError(int requestID, String message) { + this(); + this.requestID.setValue(requestID); + reason.setValue(message); + } + + private KSConnectError() { + super(ControlMessageURN.KS_CONNECT_ERROR); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + reason = new StringComponent(ControlMessageComponentURN.Reason); + addMessageComponent(requestID); + addMessageComponent(reason); + } + + public KSConnectError(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the reason for the error. + * + * @return The reason for the error. + */ + public String getReason() { + return reason.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectOK.java b/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..9333dce6e388613bbb70dce1165ceec3c2594969 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KSConnectOK.java @@ -0,0 +1,103 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.EntityListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.Entity; + +/** + * A message for signalling a successful connection to the kernel. + */ +public class KSConnectOK extends AbstractMessage { + private IntComponent simulatorID; + private IntComponent requestID; + private EntityListComponent world; + private ConfigComponent config; + + /** + * A KSConnectOK message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KSConnectOK(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KSConnectOK message. + * + * @param simulatorID The ID of the simulator that has successfully connected. + * @param requestID The request ID. + * @param allEntities All Entities in the world. + * @param config The Config that the simulator knows about. + */ + public KSConnectOK(int simulatorID, int requestID, Collection<? extends Entity> allEntities, Config config) { + this(); + this.simulatorID.setValue(simulatorID); + this.requestID.setValue(requestID); + this.world.setEntities(allEntities); + this.config.setConfig(config); + } + + private KSConnectOK() { + super(ControlMessageURN.KS_CONNECT_OK); + simulatorID = new IntComponent(ControlMessageComponentURN.SimulatorID); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + world = new EntityListComponent(ControlMessageComponentURN.Entities); + config = new ConfigComponent(ControlMessageComponentURN.SimulatorConfig); + addMessageComponent(requestID); + addMessageComponent(simulatorID); + addMessageComponent(world); + addMessageComponent(config); + } + + public KSConnectOK(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the simulator ID for this message. + * + * @return The simulator ID. + */ + public int getSimulatorID() { + return simulatorID.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the entity list. + * + * @return All entities in the world. + */ + public Collection<Entity> getEntities() { + return world.getEntities(); + } + + /** + * Get the Config. + * + * @return The simulator config. + */ + public Config getConfig() { + return config.getConfig(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KSUpdate.java b/modules/rescuecore2/src/rescuecore2/messages/control/KSUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..878b6565a616648cdb12887f4968b877f4fcf3ce --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KSUpdate.java @@ -0,0 +1,86 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ChangeSetComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.ChangeSet; + +/** + * A broadcast update from the kernel. + */ +public class KSUpdate extends AbstractMessage { + private IntComponent id; + private IntComponent time; + private ChangeSetComponent changes; + + /** + * A KSUpdate message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KSUpdate(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KSUpdate message. + * + * @param id The id of the simulator receiving the update. + * @param time The timestep of the simulation. + * @param changes The changeset. + */ + public KSUpdate(int id, int time, ChangeSet changes) { + this(); + this.id.setValue(id); + this.time.setValue(time); + this.changes.setChangeSet(changes); + } + + private KSUpdate() { + super(ControlMessageURN.KS_UPDATE); + id = new IntComponent(ControlMessageComponentURN.ID); + time = new IntComponent(ControlMessageComponentURN.Time); + changes = new ChangeSetComponent(ControlMessageComponentURN.Changes); + addMessageComponent(id); + addMessageComponent(time); + addMessageComponent(changes); + } + + public KSUpdate(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the id of the component that this message is addressed to. + * + * @return The ID of the target component. + */ + public int getTargetID() { + return id.getValue(); + } + + /** + * Get the time of the simulation. + * + * @return The simulation time. + */ + public int getTime() { + return time.getValue(); + } + + /** + * Get the list of changes. + * + * @return The changes. + */ + public ChangeSet getChangeSet() { + return changes.getChangeSet(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectError.java b/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectError.java new file mode 100644 index 0000000000000000000000000000000000000000..ed78c6c5b913e54957e5e724266b9feed2967815 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectError.java @@ -0,0 +1,71 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for signalling an unsuccessful connection to the kernel. + */ +public class KVConnectError extends AbstractMessage { + private IntComponent requestID; + private StringComponent reason; + + /** + * A KVConnectError message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KVConnectError(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A KVConnectError with specified request ID and reason. + * + * @param requestID The request ID. + * @param message The reason for the error. + */ + public KVConnectError(int requestID, String message) { + this(); + this.requestID.setValue(requestID); + reason.setValue(message); + } + + private KVConnectError() { + super(ControlMessageURN.KV_CONNECT_ERROR); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + reason = new StringComponent(ControlMessageComponentURN.Reason); + addMessageComponent(requestID); + addMessageComponent(reason); + } + + public KVConnectError(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the reason for the error. + * + * @return The reason for the error. + */ + public String getReason() { + return reason.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectOK.java b/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectOK.java new file mode 100644 index 0000000000000000000000000000000000000000..5c2aa277e840d7ce4f35dc0f006de4f0f0de700a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KVConnectOK.java @@ -0,0 +1,103 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import rescuecore2.config.Config; +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ConfigComponent; +import rescuecore2.messages.components.EntityListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.Entity; + +/** + * A message for signalling a successful connection to the kernel. + */ +public class KVConnectOK extends AbstractMessage { + private IntComponent viewerID; + private IntComponent requestID; + private EntityListComponent world; + private ConfigComponent config; + + /** + * A KVConnectOK message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KVConnectOK(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KVConnectOK message. + * + * @param viewerID The viewer ID. + * @param requestID The request ID. + * @param allEntities All Entities in the world. + * @param config The Config that the agent knows about. + */ + public KVConnectOK(int viewerID, int requestID, Collection<? extends Entity> allEntities, Config config) { + this(); + this.viewerID.setValue(viewerID); + this.requestID.setValue(requestID); + this.world.setEntities(allEntities); + this.config.setConfig(config); + } + + private KVConnectOK() { + super(ControlMessageURN.KV_CONNECT_OK); + viewerID = new IntComponent(ControlMessageComponentURN.ViewerID); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + world = new EntityListComponent(ControlMessageComponentURN.Entities); + config = new ConfigComponent(ControlMessageComponentURN.AgentConfig); + addMessageComponent(requestID); + addMessageComponent(viewerID); + addMessageComponent(world); + addMessageComponent(config); + } + + public KVConnectOK(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the viewer ID. + * + * @return The viewer ID. + */ + public int getViewerID() { + return viewerID.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the entity list. + * + * @return All entities in the world. + */ + public Collection<Entity> getEntities() { + return world.getEntities(); + } + + /** + * Get the Config. + * + * @return The viewer config. + */ + public Config getConfig() { + return config.getConfig(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/KVTimestep.java b/modules/rescuecore2/src/rescuecore2/messages/control/KVTimestep.java new file mode 100644 index 0000000000000000000000000000000000000000..ccf8cfcb996d64e16efe5e02291fe36cf7ecf29c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/KVTimestep.java @@ -0,0 +1,105 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.Command; +import rescuecore2.messages.components.ChangeSetComponent; +import rescuecore2.messages.components.CommandListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.ChangeSet; + +/** + * A message containing a timestep summary. This is sent from the kernel to all + * viewers. + */ +public class KVTimestep extends AbstractMessage { + private IntComponent id; + private IntComponent time; + private CommandListComponent commands; + private ChangeSetComponent changes; + + /** + * A KVTimestep message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public KVTimestep(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A populated KVTimestep message. + * + * @param id The id of the viewer receiving the update. + * @param time The timestep of the simulation. + * @param commands All agent Commands. + * @param changes Summary of changes during the timestep. + */ + public KVTimestep(int id, int time, Collection<? extends Command> commands, ChangeSet changes) { + this(); + this.id.setValue(id); + this.time.setValue(time); + this.commands.setCommands(commands); + this.changes.setChangeSet(changes); + } + + private KVTimestep() { + super(ControlMessageURN.KV_TIMESTEP); + id = new IntComponent(ControlMessageComponentURN.ID); + time = new IntComponent(ControlMessageComponentURN.Time); + commands = new CommandListComponent(ControlMessageComponentURN.Commands); + changes = new ChangeSetComponent(ControlMessageComponentURN.Changes); + addMessageComponent(id); + addMessageComponent(time); + addMessageComponent(commands); + addMessageComponent(changes); + } + + public KVTimestep(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the id of the component that this message is addressed to. + * + * @return The ID of the target component. + */ + public int getTargetID() { + return id.getValue(); + } + + /** + * Get the time of the simulation. + * + * @return The simulation time. + */ + public int getTime() { + return time.getValue(); + } + + /** + * Get the list of agent commands. + * + * @return The agent commands. + */ + public List<Command> getCommands() { + return commands.getCommands(); + } + + /** + * Get the list of changes. + * + * @return The changes. + */ + public ChangeSet getChangeSet() { + return changes.getChangeSet(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/SKAcknowledge.java b/modules/rescuecore2/src/rescuecore2/messages/control/SKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..32c75ef83b83233d0a11446de66b731a4496b07c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/SKAcknowledge.java @@ -0,0 +1,70 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for acknowledging a connection to the kernel. + */ +public class SKAcknowledge extends AbstractMessage { + private IntComponent requestID; + private IntComponent simulatorID; + + /** + * An SKAcknowledge message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public SKAcknowledge(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * SKAcknowledge message with specific request ID and simulator ID components. + * + * @param requestID The value of the request ID component. + * @param simulatorID The value of the simulator ID component. + */ + public SKAcknowledge(int requestID, int simulatorID) { + this(); + this.requestID.setValue(requestID); + this.simulatorID.setValue(simulatorID); + } + + private SKAcknowledge() { + super(ControlMessageURN.SK_ACKNOWLEDGE); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + simulatorID = new IntComponent(ControlMessageComponentURN.SimulatorID); + addMessageComponent(requestID); + addMessageComponent(simulatorID); + } + + public SKAcknowledge(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the request ID. + * + * @return The request ID component. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the simulator ID. + * + * @return The simulator ID component. + */ + public int getSimulatorID() { + return simulatorID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/SKConnect.java b/modules/rescuecore2/src/rescuecore2/messages/control/SKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..0c594feded3ef364b0cd908e4d3df9fb1ffe1ac1 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/SKConnect.java @@ -0,0 +1,85 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for connecting a simulator to the kernel. + */ +public class SKConnect extends AbstractMessage { + private IntComponent requestID; + private IntComponent version; + private StringComponent simulatorName; + + /** + * An SKConnect message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public SKConnect(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * An SKConnect with a given version and request ID. + * + * @param requestID The request ID. + * @param version The version number. + * @param name The name of the simulator. + */ + public SKConnect(int requestID, int version, String name) { + this(); + this.requestID.setValue(requestID); + this.version.setValue(version); + this.simulatorName.setValue(name); + } + + private SKConnect() { + super(ControlMessageURN.SK_CONNECT); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + version = new IntComponent(ControlMessageComponentURN.Version); + simulatorName = new StringComponent(ControlMessageComponentURN.Name); + addMessageComponent(requestID); + addMessageComponent(version); + addMessageComponent(simulatorName); + } + + public SKConnect(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the version number of this request. + * + * @return The version number. + */ + public int getVersion() { + return version.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the simulator name. + * + * @return The name of the simulator. + */ + public String getSimulatorName() { + return simulatorName.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/SKUpdate.java b/modules/rescuecore2/src/rescuecore2/messages/control/SKUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..495f087033442ee800fd7003f96987eb850e3941 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/SKUpdate.java @@ -0,0 +1,86 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.ChangeSetComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.ChangeSet; + +/** + * A message for sending updates from a simulator to the kernel. + */ +public class SKUpdate extends AbstractMessage { + private IntComponent id; + private IntComponent time; + private ChangeSetComponent update; + + /** + * An SKUpdate message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public SKUpdate(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * SKUpdate message with a specific ID and data component. + * + * @param id The id of the simulator sending the update. + * @param time The timestep this update refers to. + * @param changes The changeset. + */ + public SKUpdate(int id, int time, ChangeSet changes) { + this(); + this.id.setValue(id); + this.time.setValue(time); + this.update.setChangeSet(changes); + } + + private SKUpdate() { + super(ControlMessageURN.SK_UPDATE); + id = new IntComponent(ControlMessageComponentURN.ID); + time = new IntComponent(ControlMessageComponentURN.Time); + update = new ChangeSetComponent(ControlMessageComponentURN.Changes); + addMessageComponent(id); + addMessageComponent(time); + addMessageComponent(update); + } + + public SKUpdate(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the ID of the simulator that is acknowledging the connection. + * + * @return The simulator ID component. + */ + public int getSimulatorID() { + return id.getValue(); + } + + /** + * Get the list of changes. + * + * @return The ChangeSet. + */ + public ChangeSet getChangeSet() { + return update.getChangeSet(); + } + + /** + * Get the timestep this update is for. + * + * @return The timestep. + */ + public int getTime() { + return time.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/Shutdown.java b/modules/rescuecore2/src/rescuecore2/messages/control/Shutdown.java new file mode 100644 index 0000000000000000000000000000000000000000..5acef9265b16184d631a87a135de4c497fe8aaef --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/Shutdown.java @@ -0,0 +1,35 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message from the kernel indicating that components should shut down. + */ +public class Shutdown extends AbstractMessage { + /** + * Construct a Shutdown message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public Shutdown(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct a Shutdown message. + */ + public Shutdown() { + super(ControlMessageURN.SHUTDOWN); + } + + public Shutdown(MessageProto proto) { + this(); + fromMessageProto(proto); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/VKAcknowledge.java b/modules/rescuecore2/src/rescuecore2/messages/control/VKAcknowledge.java new file mode 100644 index 0000000000000000000000000000000000000000..7b4c7061bb13e01987807c6f1efe40ae7bd40a92 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/VKAcknowledge.java @@ -0,0 +1,70 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for acknowledging a connection to the kernel. + */ +public class VKAcknowledge extends AbstractMessage { + private IntComponent requestID; + private IntComponent viewerID; + + /** + * A VKAcknowledge message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public VKAcknowledge(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * VKAcknowledge message with specific request ID and viewer ID components. + * + * @param requestID The value of the request ID component. + * @param viewerID The value of the viewer ID component. + */ + public VKAcknowledge(int requestID, int viewerID) { + this(); + this.requestID.setValue(requestID); + this.viewerID.setValue(viewerID); + } + + private VKAcknowledge() { + super(ControlMessageURN.VK_ACKNOWLEDGE); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + viewerID = new IntComponent(ControlMessageComponentURN.ViewerID); + addMessageComponent(requestID); + addMessageComponent(viewerID); + } + + public VKAcknowledge(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the request ID. + * + * @return The request ID component. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the viewer ID. + * + * @return The viewer ID component. + */ + public int getViewerID() { + return viewerID.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/control/VKConnect.java b/modules/rescuecore2/src/rescuecore2/messages/control/VKConnect.java new file mode 100644 index 0000000000000000000000000000000000000000..f1524f473752e075dc179f637d0713d5bb3b5d45 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/control/VKConnect.java @@ -0,0 +1,85 @@ +package rescuecore2.messages.control; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.AbstractMessage; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.StringComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message for connecting a viewer to the kernel. + */ +public class VKConnect extends AbstractMessage { + private IntComponent requestID; + private IntComponent version; + private StringComponent viewerName; + + /** + * A VKConnect message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public VKConnect(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * A VKConnect with a given version and request ID. + * + * @param version The version number. + * @param requestID The request ID. + * @param name The name of the simulator. + */ + public VKConnect(int requestID, int version, String name) { + this(); + this.requestID.setValue(requestID); + this.version.setValue(version); + this.viewerName.setValue(name); + } + + private VKConnect() { + super(ControlMessageURN.VK_CONNECT); + requestID = new IntComponent(ControlMessageComponentURN.RequestID); + version = new IntComponent(ControlMessageComponentURN.Version); + viewerName = new StringComponent(ControlMessageComponentURN.Name); + addMessageComponent(requestID); + addMessageComponent(version); + addMessageComponent(viewerName); + } + + public VKConnect(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the version number of this request. + * + * @return The version number. + */ + public int getVersion() { + return version.getValue(); + } + + /** + * Get the request ID. + * + * @return The request ID. + */ + public int getRequestID() { + return requestID.getValue(); + } + + /** + * Get the viewer name. + * + * @return The name of the viewer. + */ + public String getViewerName() { + return viewerName.getValue(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/protobuf/MsgProtoBuf.java b/modules/rescuecore2/src/rescuecore2/messages/protobuf/MsgProtoBuf.java new file mode 100644 index 0000000000000000000000000000000000000000..d42f9dab2327552ad82836ad6d9950454db042c6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/protobuf/MsgProtoBuf.java @@ -0,0 +1,35 @@ +package rescuecore2.messages.protobuf; + +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.EntityProto; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +public class MsgProtoBuf { + + public static Property propertyProto2Property(PropertyProto propertyProto) { + int urn = propertyProto.getUrn(); + Property property = Registry.getCurrentRegistry().createProperty(urn); + if (property != null) + property.fromPropertyProto(propertyProto); + return property; + } + + public static Entity entityProto2Entity(EntityProto entityProto) { + int urn = entityProto.getUrn(); + Entity entity = Registry.getCurrentRegistry().createEntity(urn, new EntityID(entityProto.getEntityID())); + if (entity != null) + entity.fromEntityProto(entityProto); + return entity; + } + + public static Message messageProto2Message(MessageProto messageProto) { + int urn = messageProto.getUrn(); + Message msg = Registry.getCurrentRegistry().createMessage(urn, messageProto); + return msg; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.java b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.java new file mode 100644 index 0000000000000000000000000000000000000000..acd6d309d7dc17b4f9ccbc54a6920eec8c21132a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.java @@ -0,0 +1,7176 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: RCRSLogProto.proto + +package rescuecore2.messages.protobuf; + +public final class RCRSLogProto { + private RCRSLogProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface LogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:LogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>.StartLogProto start = 1;</code> + * @return Whether the start field is set. + */ + boolean hasStart(); + /** + * <code>.StartLogProto start = 1;</code> + * @return The start. + */ + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getStart(); + /** + * <code>.StartLogProto start = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder getStartOrBuilder(); + + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return Whether the initialCondition field is set. + */ + boolean hasInitialCondition(); + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return The initialCondition. + */ + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getInitialCondition(); + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder getInitialConditionOrBuilder(); + + /** + * <code>.CommandLogProto command = 3;</code> + * @return Whether the command field is set. + */ + boolean hasCommand(); + /** + * <code>.CommandLogProto command = 3;</code> + * @return The command. + */ + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getCommand(); + /** + * <code>.CommandLogProto command = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder getCommandOrBuilder(); + + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return Whether the perception field is set. + */ + boolean hasPerception(); + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return The perception. + */ + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getPerception(); + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder getPerceptionOrBuilder(); + + /** + * <code>.ConfigLogProto config = 5;</code> + * @return Whether the config field is set. + */ + boolean hasConfig(); + /** + * <code>.ConfigLogProto config = 5;</code> + * @return The config. + */ + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getConfig(); + /** + * <code>.ConfigLogProto config = 5;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder getConfigOrBuilder(); + + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return Whether the update field is set. + */ + boolean hasUpdate(); + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return The update. + */ + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getUpdate(); + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder getUpdateOrBuilder(); + + /** + * <code>.EndLogProto end = 7;</code> + * @return Whether the end field is set. + */ + boolean hasEnd(); + /** + * <code>.EndLogProto end = 7;</code> + * @return The end. + */ + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getEnd(); + /** + * <code>.EndLogProto end = 7;</code> + */ + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder getEndOrBuilder(); + + public rescuecore2.messages.protobuf.RCRSLogProto.LogProto.LogCase getLogCase(); + } + /** + * Protobuf type {@code LogProto} + */ + public static final class LogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:LogProto) + LogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use LogProto.newBuilder() to construct. + private LogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private LogProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new LogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private LogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder subBuilder = null; + if (logCase_ == 1) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 1; + break; + } + case 18: { + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder subBuilder = null; + if (logCase_ == 2) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 2; + break; + } + case 26: { + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder subBuilder = null; + if (logCase_ == 3) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 3; + break; + } + case 34: { + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder subBuilder = null; + if (logCase_ == 4) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 4; + break; + } + case 42: { + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder subBuilder = null; + if (logCase_ == 5) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 5; + break; + } + case 50: { + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder subBuilder = null; + if (logCase_ == 6) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 6; + break; + } + case 58: { + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder subBuilder = null; + if (logCase_ == 7) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_).toBuilder(); + } + log_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_); + log_ = subBuilder.buildPartial(); + } + logCase_ = 7; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_LogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_LogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.LogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.LogProto.Builder.class); + } + + private int logCase_ = 0; + private java.lang.Object log_; + public enum LogCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + START(1), + INITIALCONDITION(2), + COMMAND(3), + PERCEPTION(4), + CONFIG(5), + UPDATE(6), + END(7), + LOG_NOT_SET(0); + private final int value; + private LogCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static LogCase valueOf(int value) { + return forNumber(value); + } + + public static LogCase forNumber(int value) { + switch (value) { + case 1: return START; + case 2: return INITIALCONDITION; + case 3: return COMMAND; + case 4: return PERCEPTION; + case 5: return CONFIG; + case 6: return UPDATE; + case 7: return END; + case 0: return LOG_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public LogCase + getLogCase() { + return LogCase.forNumber( + logCase_); + } + + public static final int START_FIELD_NUMBER = 1; + /** + * <code>.StartLogProto start = 1;</code> + * @return Whether the start field is set. + */ + @java.lang.Override + public boolean hasStart() { + return logCase_ == 1; + } + /** + * <code>.StartLogProto start = 1;</code> + * @return The start. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getStart() { + if (logCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + /** + * <code>.StartLogProto start = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder getStartOrBuilder() { + if (logCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + + public static final int INITIALCONDITION_FIELD_NUMBER = 2; + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return Whether the initialCondition field is set. + */ + @java.lang.Override + public boolean hasInitialCondition() { + return logCase_ == 2; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return The initialCondition. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getInitialCondition() { + if (logCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder getInitialConditionOrBuilder() { + if (logCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + + public static final int COMMAND_FIELD_NUMBER = 3; + /** + * <code>.CommandLogProto command = 3;</code> + * @return Whether the command field is set. + */ + @java.lang.Override + public boolean hasCommand() { + return logCase_ == 3; + } + /** + * <code>.CommandLogProto command = 3;</code> + * @return The command. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getCommand() { + if (logCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder getCommandOrBuilder() { + if (logCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + + public static final int PERCEPTION_FIELD_NUMBER = 4; + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return Whether the perception field is set. + */ + @java.lang.Override + public boolean hasPerception() { + return logCase_ == 4; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return The perception. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getPerception() { + if (logCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder getPerceptionOrBuilder() { + if (logCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + + public static final int CONFIG_FIELD_NUMBER = 5; + /** + * <code>.ConfigLogProto config = 5;</code> + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return logCase_ == 5; + } + /** + * <code>.ConfigLogProto config = 5;</code> + * @return The config. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getConfig() { + if (logCase_ == 5) { + return (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder getConfigOrBuilder() { + if (logCase_ == 5) { + return (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + + public static final int UPDATE_FIELD_NUMBER = 6; + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return Whether the update field is set. + */ + @java.lang.Override + public boolean hasUpdate() { + return logCase_ == 6; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return The update. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getUpdate() { + if (logCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder getUpdateOrBuilder() { + if (logCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + + public static final int END_FIELD_NUMBER = 7; + /** + * <code>.EndLogProto end = 7;</code> + * @return Whether the end field is set. + */ + @java.lang.Override + public boolean hasEnd() { + return logCase_ == 7; + } + /** + * <code>.EndLogProto end = 7;</code> + * @return The end. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getEnd() { + if (logCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + /** + * <code>.EndLogProto end = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder getEndOrBuilder() { + if (logCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (logCase_ == 1) { + output.writeMessage(1, (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_); + } + if (logCase_ == 2) { + output.writeMessage(2, (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_); + } + if (logCase_ == 3) { + output.writeMessage(3, (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_); + } + if (logCase_ == 4) { + output.writeMessage(4, (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_); + } + if (logCase_ == 5) { + output.writeMessage(5, (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_); + } + if (logCase_ == 6) { + output.writeMessage(6, (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_); + } + if (logCase_ == 7) { + output.writeMessage(7, (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (logCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_); + } + if (logCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_); + } + if (logCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_); + } + if (logCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_); + } + if (logCase_ == 5) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_); + } + if (logCase_ == 6) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_); + } + if (logCase_ == 7) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.LogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.LogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.LogProto) obj; + + if (!getLogCase().equals(other.getLogCase())) return false; + switch (logCase_) { + case 1: + if (!getStart() + .equals(other.getStart())) return false; + break; + case 2: + if (!getInitialCondition() + .equals(other.getInitialCondition())) return false; + break; + case 3: + if (!getCommand() + .equals(other.getCommand())) return false; + break; + case 4: + if (!getPerception() + .equals(other.getPerception())) return false; + break; + case 5: + if (!getConfig() + .equals(other.getConfig())) return false; + break; + case 6: + if (!getUpdate() + .equals(other.getUpdate())) return false; + break; + case 7: + if (!getEnd() + .equals(other.getEnd())) return false; + break; + case 0: + default: + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (logCase_) { + case 1: + hash = (37 * hash) + START_FIELD_NUMBER; + hash = (53 * hash) + getStart().hashCode(); + break; + case 2: + hash = (37 * hash) + INITIALCONDITION_FIELD_NUMBER; + hash = (53 * hash) + getInitialCondition().hashCode(); + break; + case 3: + hash = (37 * hash) + COMMAND_FIELD_NUMBER; + hash = (53 * hash) + getCommand().hashCode(); + break; + case 4: + hash = (37 * hash) + PERCEPTION_FIELD_NUMBER; + hash = (53 * hash) + getPerception().hashCode(); + break; + case 5: + hash = (37 * hash) + CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getConfig().hashCode(); + break; + case 6: + hash = (37 * hash) + UPDATE_FIELD_NUMBER; + hash = (53 * hash) + getUpdate().hashCode(); + break; + case 7: + hash = (37 * hash) + END_FIELD_NUMBER; + hash = (53 * hash) + getEnd().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.LogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code LogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:LogProto) + rescuecore2.messages.protobuf.RCRSLogProto.LogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_LogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_LogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.LogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.LogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.LogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + logCase_ = 0; + log_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_LogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.LogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.LogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.LogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.LogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.LogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.LogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.LogProto(this); + if (logCase_ == 1) { + if (startBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = startBuilder_.build(); + } + } + if (logCase_ == 2) { + if (initialConditionBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = initialConditionBuilder_.build(); + } + } + if (logCase_ == 3) { + if (commandBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = commandBuilder_.build(); + } + } + if (logCase_ == 4) { + if (perceptionBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = perceptionBuilder_.build(); + } + } + if (logCase_ == 5) { + if (configBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = configBuilder_.build(); + } + } + if (logCase_ == 6) { + if (updateBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = updateBuilder_.build(); + } + } + if (logCase_ == 7) { + if (endBuilder_ == null) { + result.log_ = log_; + } else { + result.log_ = endBuilder_.build(); + } + } + result.logCase_ = logCase_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.LogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.LogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.LogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.LogProto.getDefaultInstance()) return this; + switch (other.getLogCase()) { + case START: { + mergeStart(other.getStart()); + break; + } + case INITIALCONDITION: { + mergeInitialCondition(other.getInitialCondition()); + break; + } + case COMMAND: { + mergeCommand(other.getCommand()); + break; + } + case PERCEPTION: { + mergePerception(other.getPerception()); + break; + } + case CONFIG: { + mergeConfig(other.getConfig()); + break; + } + case UPDATE: { + mergeUpdate(other.getUpdate()); + break; + } + case END: { + mergeEnd(other.getEnd()); + break; + } + case LOG_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.LogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.LogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int logCase_ = 0; + private java.lang.Object log_; + public LogCase + getLogCase() { + return LogCase.forNumber( + logCase_); + } + + public Builder clearLog() { + logCase_ = 0; + log_ = null; + onChanged(); + return this; + } + + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder> startBuilder_; + /** + * <code>.StartLogProto start = 1;</code> + * @return Whether the start field is set. + */ + @java.lang.Override + public boolean hasStart() { + return logCase_ == 1; + } + /** + * <code>.StartLogProto start = 1;</code> + * @return The start. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getStart() { + if (startBuilder_ == null) { + if (logCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } else { + if (logCase_ == 1) { + return startBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + } + /** + * <code>.StartLogProto start = 1;</code> + */ + public Builder setStart(rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto value) { + if (startBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + startBuilder_.setMessage(value); + } + logCase_ = 1; + return this; + } + /** + * <code>.StartLogProto start = 1;</code> + */ + public Builder setStart( + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder builderForValue) { + if (startBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + startBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 1; + return this; + } + /** + * <code>.StartLogProto start = 1;</code> + */ + public Builder mergeStart(rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto value) { + if (startBuilder_ == null) { + if (logCase_ == 1 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 1) { + startBuilder_.mergeFrom(value); + } + startBuilder_.setMessage(value); + } + logCase_ = 1; + return this; + } + /** + * <code>.StartLogProto start = 1;</code> + */ + public Builder clearStart() { + if (startBuilder_ == null) { + if (logCase_ == 1) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 1) { + logCase_ = 0; + log_ = null; + } + startBuilder_.clear(); + } + return this; + } + /** + * <code>.StartLogProto start = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder getStartBuilder() { + return getStartFieldBuilder().getBuilder(); + } + /** + * <code>.StartLogProto start = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder getStartOrBuilder() { + if ((logCase_ == 1) && (startBuilder_ != null)) { + return startBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + } + /** + * <code>.StartLogProto start = 1;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder> + getStartFieldBuilder() { + if (startBuilder_ == null) { + if (!(logCase_ == 1)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + startBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 1; + onChanged();; + return startBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder> initialConditionBuilder_; + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return Whether the initialCondition field is set. + */ + @java.lang.Override + public boolean hasInitialCondition() { + return logCase_ == 2; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + * @return The initialCondition. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getInitialCondition() { + if (initialConditionBuilder_ == null) { + if (logCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } else { + if (logCase_ == 2) { + return initialConditionBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + public Builder setInitialCondition(rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto value) { + if (initialConditionBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + initialConditionBuilder_.setMessage(value); + } + logCase_ = 2; + return this; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + public Builder setInitialCondition( + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder builderForValue) { + if (initialConditionBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + initialConditionBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 2; + return this; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + public Builder mergeInitialCondition(rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto value) { + if (initialConditionBuilder_ == null) { + if (logCase_ == 2 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 2) { + initialConditionBuilder_.mergeFrom(value); + } + initialConditionBuilder_.setMessage(value); + } + logCase_ = 2; + return this; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + public Builder clearInitialCondition() { + if (initialConditionBuilder_ == null) { + if (logCase_ == 2) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 2) { + logCase_ = 0; + log_ = null; + } + initialConditionBuilder_.clear(); + } + return this; + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder getInitialConditionBuilder() { + return getInitialConditionFieldBuilder().getBuilder(); + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder getInitialConditionOrBuilder() { + if ((logCase_ == 2) && (initialConditionBuilder_ != null)) { + return initialConditionBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + } + /** + * <code>.InitialConditionsLogProto initialCondition = 2;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder> + getInitialConditionFieldBuilder() { + if (initialConditionBuilder_ == null) { + if (!(logCase_ == 2)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + initialConditionBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 2; + onChanged();; + return initialConditionBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder> commandBuilder_; + /** + * <code>.CommandLogProto command = 3;</code> + * @return Whether the command field is set. + */ + @java.lang.Override + public boolean hasCommand() { + return logCase_ == 3; + } + /** + * <code>.CommandLogProto command = 3;</code> + * @return The command. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getCommand() { + if (commandBuilder_ == null) { + if (logCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } else { + if (logCase_ == 3) { + return commandBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + public Builder setCommand(rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto value) { + if (commandBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + commandBuilder_.setMessage(value); + } + logCase_ = 3; + return this; + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + public Builder setCommand( + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder builderForValue) { + if (commandBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + commandBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 3; + return this; + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + public Builder mergeCommand(rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto value) { + if (commandBuilder_ == null) { + if (logCase_ == 3 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 3) { + commandBuilder_.mergeFrom(value); + } + commandBuilder_.setMessage(value); + } + logCase_ = 3; + return this; + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + public Builder clearCommand() { + if (commandBuilder_ == null) { + if (logCase_ == 3) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 3) { + logCase_ = 0; + log_ = null; + } + commandBuilder_.clear(); + } + return this; + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder getCommandBuilder() { + return getCommandFieldBuilder().getBuilder(); + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder getCommandOrBuilder() { + if ((logCase_ == 3) && (commandBuilder_ != null)) { + return commandBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + } + /** + * <code>.CommandLogProto command = 3;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder> + getCommandFieldBuilder() { + if (commandBuilder_ == null) { + if (!(logCase_ == 3)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + commandBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 3; + onChanged();; + return commandBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder> perceptionBuilder_; + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return Whether the perception field is set. + */ + @java.lang.Override + public boolean hasPerception() { + return logCase_ == 4; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + * @return The perception. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getPerception() { + if (perceptionBuilder_ == null) { + if (logCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } else { + if (logCase_ == 4) { + return perceptionBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + public Builder setPerception(rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto value) { + if (perceptionBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + perceptionBuilder_.setMessage(value); + } + logCase_ = 4; + return this; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + public Builder setPerception( + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder builderForValue) { + if (perceptionBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + perceptionBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 4; + return this; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + public Builder mergePerception(rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto value) { + if (perceptionBuilder_ == null) { + if (logCase_ == 4 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 4) { + perceptionBuilder_.mergeFrom(value); + } + perceptionBuilder_.setMessage(value); + } + logCase_ = 4; + return this; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + public Builder clearPerception() { + if (perceptionBuilder_ == null) { + if (logCase_ == 4) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 4) { + logCase_ = 0; + log_ = null; + } + perceptionBuilder_.clear(); + } + return this; + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder getPerceptionBuilder() { + return getPerceptionFieldBuilder().getBuilder(); + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder getPerceptionOrBuilder() { + if ((logCase_ == 4) && (perceptionBuilder_ != null)) { + return perceptionBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + } + /** + * <code>.PerceptionLogProto perception = 4;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder> + getPerceptionFieldBuilder() { + if (perceptionBuilder_ == null) { + if (!(logCase_ == 4)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + perceptionBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 4; + onChanged();; + return perceptionBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder> configBuilder_; + /** + * <code>.ConfigLogProto config = 5;</code> + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return logCase_ == 5; + } + /** + * <code>.ConfigLogProto config = 5;</code> + * @return The config. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getConfig() { + if (configBuilder_ == null) { + if (logCase_ == 5) { + return (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } else { + if (logCase_ == 5) { + return configBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + public Builder setConfig(rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto value) { + if (configBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + configBuilder_.setMessage(value); + } + logCase_ = 5; + return this; + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + public Builder setConfig( + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder builderForValue) { + if (configBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + configBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 5; + return this; + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + public Builder mergeConfig(rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto value) { + if (configBuilder_ == null) { + if (logCase_ == 5 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 5) { + configBuilder_.mergeFrom(value); + } + configBuilder_.setMessage(value); + } + logCase_ = 5; + return this; + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + public Builder clearConfig() { + if (configBuilder_ == null) { + if (logCase_ == 5) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 5) { + logCase_ = 0; + log_ = null; + } + configBuilder_.clear(); + } + return this; + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder getConfigBuilder() { + return getConfigFieldBuilder().getBuilder(); + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder getConfigOrBuilder() { + if ((logCase_ == 5) && (configBuilder_ != null)) { + return configBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 5) { + return (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + } + /** + * <code>.ConfigLogProto config = 5;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder> + getConfigFieldBuilder() { + if (configBuilder_ == null) { + if (!(logCase_ == 5)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + configBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 5; + onChanged();; + return configBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder> updateBuilder_; + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return Whether the update field is set. + */ + @java.lang.Override + public boolean hasUpdate() { + return logCase_ == 6; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + * @return The update. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getUpdate() { + if (updateBuilder_ == null) { + if (logCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } else { + if (logCase_ == 6) { + return updateBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + public Builder setUpdate(rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto value) { + if (updateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + updateBuilder_.setMessage(value); + } + logCase_ = 6; + return this; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + public Builder setUpdate( + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder builderForValue) { + if (updateBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + updateBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 6; + return this; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + public Builder mergeUpdate(rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto value) { + if (updateBuilder_ == null) { + if (logCase_ == 6 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 6) { + updateBuilder_.mergeFrom(value); + } + updateBuilder_.setMessage(value); + } + logCase_ = 6; + return this; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + public Builder clearUpdate() { + if (updateBuilder_ == null) { + if (logCase_ == 6) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 6) { + logCase_ = 0; + log_ = null; + } + updateBuilder_.clear(); + } + return this; + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder getUpdateBuilder() { + return getUpdateFieldBuilder().getBuilder(); + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder getUpdateOrBuilder() { + if ((logCase_ == 6) && (updateBuilder_ != null)) { + return updateBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + } + /** + * <code>.UpdatesLogProto update = 6;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder> + getUpdateFieldBuilder() { + if (updateBuilder_ == null) { + if (!(logCase_ == 6)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + updateBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 6; + onChanged();; + return updateBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder> endBuilder_; + /** + * <code>.EndLogProto end = 7;</code> + * @return Whether the end field is set. + */ + @java.lang.Override + public boolean hasEnd() { + return logCase_ == 7; + } + /** + * <code>.EndLogProto end = 7;</code> + * @return The end. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getEnd() { + if (endBuilder_ == null) { + if (logCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } else { + if (logCase_ == 7) { + return endBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + } + /** + * <code>.EndLogProto end = 7;</code> + */ + public Builder setEnd(rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto value) { + if (endBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + log_ = value; + onChanged(); + } else { + endBuilder_.setMessage(value); + } + logCase_ = 7; + return this; + } + /** + * <code>.EndLogProto end = 7;</code> + */ + public Builder setEnd( + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder builderForValue) { + if (endBuilder_ == null) { + log_ = builderForValue.build(); + onChanged(); + } else { + endBuilder_.setMessage(builderForValue.build()); + } + logCase_ = 7; + return this; + } + /** + * <code>.EndLogProto end = 7;</code> + */ + public Builder mergeEnd(rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto value) { + if (endBuilder_ == null) { + if (logCase_ == 7 && + log_ != rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance()) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.newBuilder((rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_) + .mergeFrom(value).buildPartial(); + } else { + log_ = value; + } + onChanged(); + } else { + if (logCase_ == 7) { + endBuilder_.mergeFrom(value); + } + endBuilder_.setMessage(value); + } + logCase_ = 7; + return this; + } + /** + * <code>.EndLogProto end = 7;</code> + */ + public Builder clearEnd() { + if (endBuilder_ == null) { + if (logCase_ == 7) { + logCase_ = 0; + log_ = null; + onChanged(); + } + } else { + if (logCase_ == 7) { + logCase_ = 0; + log_ = null; + } + endBuilder_.clear(); + } + return this; + } + /** + * <code>.EndLogProto end = 7;</code> + */ + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder getEndBuilder() { + return getEndFieldBuilder().getBuilder(); + } + /** + * <code>.EndLogProto end = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder getEndOrBuilder() { + if ((logCase_ == 7) && (endBuilder_ != null)) { + return endBuilder_.getMessageOrBuilder(); + } else { + if (logCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_; + } + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + } + /** + * <code>.EndLogProto end = 7;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder> + getEndFieldBuilder() { + if (endBuilder_ == null) { + if (!(logCase_ == 7)) { + log_ = rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + endBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) log_, + getParentForChildren(), + isClean()); + log_ = null; + } + logCase_ = 7; + onChanged();; + return endBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:LogProto) + } + + // @@protoc_insertion_point(class_scope:LogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.LogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.LogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.LogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<LogProto> + PARSER = new com.google.protobuf.AbstractParser<LogProto>() { + @java.lang.Override + public LogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new LogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<LogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<LogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.LogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface StartLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:StartLogProto) + com.google.protobuf.MessageOrBuilder { + } + /** + * Protobuf type {@code StartLogProto} + */ + public static final class StartLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:StartLogProto) + StartLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use StartLogProto.newBuilder() to construct. + private StartLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private StartLogProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new StartLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private StartLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_StartLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_StartLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder.class); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) obj; + + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code StartLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:StartLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_StartLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_StartLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_StartLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto(this); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto.getDefaultInstance()) return this; + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:StartLogProto) + } + + // @@protoc_insertion_point(class_scope:StartLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<StartLogProto> + PARSER = new com.google.protobuf.AbstractParser<StartLogProto>() { + @java.lang.Override + public StartLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StartLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<StartLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<StartLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.StartLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface InitialConditionsLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:InitialConditionsLogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> + getEntitiesList(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + int getEntitiesCount(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index); + } + /** + * Protobuf type {@code InitialConditionsLogProto} + */ + public static final class InitialConditionsLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:InitialConditionsLogProto) + InitialConditionsLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use InitialConditionsLogProto.newBuilder() to construct. + private InitialConditionsLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private InitialConditionsLogProto() { + entities_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new InitialConditionsLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private InitialConditionsLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + entities_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EntityProto>(); + mutable_bitField0_ |= 0x00000001; + } + entities_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EntityProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + entities_ = java.util.Collections.unmodifiableList(entities_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_InitialConditionsLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_InitialConditionsLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder.class); + } + + public static final int ENTITIES_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> entities_; + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> getEntitiesList() { + return entities_; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList() { + return entities_; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public int getEntitiesCount() { + return entities_.size(); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index) { + return entities_.get(index); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index) { + return entities_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < entities_.size(); i++) { + output.writeMessage(1, entities_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < entities_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, entities_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) obj; + + if (!getEntitiesList() + .equals(other.getEntitiesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getEntitiesCount() > 0) { + hash = (37 * hash) + ENTITIES_FIELD_NUMBER; + hash = (53 * hash) + getEntitiesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code InitialConditionsLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:InitialConditionsLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_InitialConditionsLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_InitialConditionsLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getEntitiesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (entitiesBuilder_ == null) { + entities_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + entitiesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_InitialConditionsLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto(this); + int from_bitField0_ = bitField0_; + if (entitiesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + entities_ = java.util.Collections.unmodifiableList(entities_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.entities_ = entities_; + } else { + result.entities_ = entitiesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto.getDefaultInstance()) return this; + if (entitiesBuilder_ == null) { + if (!other.entities_.isEmpty()) { + if (entities_.isEmpty()) { + entities_ = other.entities_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureEntitiesIsMutable(); + entities_.addAll(other.entities_); + } + onChanged(); + } + } else { + if (!other.entities_.isEmpty()) { + if (entitiesBuilder_.isEmpty()) { + entitiesBuilder_.dispose(); + entitiesBuilder_ = null; + entities_ = other.entities_; + bitField0_ = (bitField0_ & ~0x00000001); + entitiesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getEntitiesFieldBuilder() : null; + } else { + entitiesBuilder_.addAllMessages(other.entities_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> entities_ = + java.util.Collections.emptyList(); + private void ensureEntitiesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + entities_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EntityProto>(entities_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> entitiesBuilder_; + + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> getEntitiesList() { + if (entitiesBuilder_ == null) { + return java.util.Collections.unmodifiableList(entities_); + } else { + return entitiesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public int getEntitiesCount() { + if (entitiesBuilder_ == null) { + return entities_.size(); + } else { + return entitiesBuilder_.getCount(); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index) { + if (entitiesBuilder_ == null) { + return entities_.get(index); + } else { + return entitiesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder setEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.set(index, value); + onChanged(); + } else { + entitiesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder setEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.set(index, builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities(rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.add(value); + onChanged(); + } else { + entitiesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.add(index, value); + onChanged(); + } else { + entitiesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.add(builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.add(index, builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addAllEntities( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProto> values) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, entities_); + onChanged(); + } else { + entitiesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder clearEntities() { + if (entitiesBuilder_ == null) { + entities_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + entitiesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder removeEntities(int index) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.remove(index); + onChanged(); + } else { + entitiesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder getEntitiesBuilder( + int index) { + return getEntitiesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index) { + if (entitiesBuilder_ == null) { + return entities_.get(index); } else { + return entitiesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList() { + if (entitiesBuilder_ != null) { + return entitiesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(entities_); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder addEntitiesBuilder() { + return getEntitiesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder addEntitiesBuilder( + int index) { + return getEntitiesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder> + getEntitiesBuilderList() { + return getEntitiesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesFieldBuilder() { + if (entitiesBuilder_ == null) { + entitiesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder>( + entities_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + entities_ = null; + } + return entitiesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:InitialConditionsLogProto) + } + + // @@protoc_insertion_point(class_scope:InitialConditionsLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<InitialConditionsLogProto> + PARSER = new com.google.protobuf.AbstractParser<InitialConditionsLogProto>() { + @java.lang.Override + public InitialConditionsLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new InitialConditionsLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<InitialConditionsLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<InitialConditionsLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.InitialConditionsLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface CommandLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:CommandLogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + int getTime(); + + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> + getCommandsList(); + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index); + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + int getCommandsCount(); + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList(); + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index); + } + /** + * Protobuf type {@code CommandLogProto} + */ + public static final class CommandLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:CommandLogProto) + CommandLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use CommandLogProto.newBuilder() to construct. + private CommandLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private CommandLogProto() { + commands_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new CommandLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private CommandLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + time_ = input.readInt32(); + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + commands_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(); + mutable_bitField0_ |= 0x00000001; + } + commands_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.MessageProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + commands_ = java.util.Collections.unmodifiableList(commands_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_CommandLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_CommandLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder.class); + } + + public static final int TIME_FIELD_NUMBER = 1; + private int time_; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + + public static final int COMMANDS_FIELD_NUMBER = 2; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> commands_; + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommandsList() { + return commands_; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList() { + return commands_; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + @java.lang.Override + public int getCommandsCount() { + return commands_.size(); + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index) { + return commands_.get(index); + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index) { + return commands_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (time_ != 0) { + output.writeInt32(1, time_); + } + for (int i = 0; i < commands_.size(); i++) { + output.writeMessage(2, commands_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (time_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, time_); + } + for (int i = 0; i < commands_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, commands_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) obj; + + if (getTime() + != other.getTime()) return false; + if (!getCommandsList() + .equals(other.getCommandsList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TIME_FIELD_NUMBER; + hash = (53 * hash) + getTime(); + if (getCommandsCount() > 0) { + hash = (37 * hash) + COMMANDS_FIELD_NUMBER; + hash = (53 * hash) + getCommandsList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code CommandLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:CommandLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_CommandLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_CommandLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getCommandsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + time_ = 0; + + if (commandsBuilder_ == null) { + commands_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + commandsBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_CommandLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto(this); + int from_bitField0_ = bitField0_; + result.time_ = time_; + if (commandsBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + commands_ = java.util.Collections.unmodifiableList(commands_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.commands_ = commands_; + } else { + result.commands_ = commandsBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto.getDefaultInstance()) return this; + if (other.getTime() != 0) { + setTime(other.getTime()); + } + if (commandsBuilder_ == null) { + if (!other.commands_.isEmpty()) { + if (commands_.isEmpty()) { + commands_ = other.commands_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureCommandsIsMutable(); + commands_.addAll(other.commands_); + } + onChanged(); + } + } else { + if (!other.commands_.isEmpty()) { + if (commandsBuilder_.isEmpty()) { + commandsBuilder_.dispose(); + commandsBuilder_ = null; + commands_ = other.commands_; + bitField0_ = (bitField0_ & ~0x00000001); + commandsBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getCommandsFieldBuilder() : null; + } else { + commandsBuilder_.addAllMessages(other.commands_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int time_ ; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + /** + * <code>int32 time = 1;</code> + * @param value The time to set. + * @return This builder for chaining. + */ + public Builder setTime(int value) { + + time_ = value; + onChanged(); + return this; + } + /** + * <code>int32 time = 1;</code> + * @return This builder for chaining. + */ + public Builder clearTime() { + + time_ = 0; + onChanged(); + return this; + } + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> commands_ = + java.util.Collections.emptyList(); + private void ensureCommandsIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + commands_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(commands_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> commandsBuilder_; + + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommandsList() { + if (commandsBuilder_ == null) { + return java.util.Collections.unmodifiableList(commands_); + } else { + return commandsBuilder_.getMessageList(); + } + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public int getCommandsCount() { + if (commandsBuilder_ == null) { + return commands_.size(); + } else { + return commandsBuilder_.getCount(); + } + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index) { + if (commandsBuilder_ == null) { + return commands_.get(index); + } else { + return commandsBuilder_.getMessage(index); + } + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder setCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.set(index, value); + onChanged(); + } else { + commandsBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder setCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.set(index, builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder addCommands(rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.add(value); + onChanged(); + } else { + commandsBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder addCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.add(index, value); + onChanged(); + } else { + commandsBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder addCommands( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.add(builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder addCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.add(index, builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder addAllCommands( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProto> values) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, commands_); + onChanged(); + } else { + commandsBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder clearCommands() { + if (commandsBuilder_ == null) { + commands_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + commandsBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public Builder removeCommands(int index) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.remove(index); + onChanged(); + } else { + commandsBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder getCommandsBuilder( + int index) { + return getCommandsFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index) { + if (commandsBuilder_ == null) { + return commands_.get(index); } else { + return commandsBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList() { + if (commandsBuilder_ != null) { + return commandsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(commands_); + } + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommandsBuilder() { + return getCommandsFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommandsBuilder( + int index) { + return getCommandsFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto commands = 2;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder> + getCommandsBuilderList() { + return getCommandsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsFieldBuilder() { + if (commandsBuilder_ == null) { + commandsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder>( + commands_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + commands_ = null; + } + return commandsBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:CommandLogProto) + } + + // @@protoc_insertion_point(class_scope:CommandLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<CommandLogProto> + PARSER = new com.google.protobuf.AbstractParser<CommandLogProto>() { + @java.lang.Override + public CommandLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CommandLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<CommandLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<CommandLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.CommandLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface PerceptionLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:PerceptionLogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + int getTime(); + + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + int getEntityID(); + + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return Whether the visible field is set. + */ + boolean hasVisible(); + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return The visible. + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getVisible(); + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getVisibleOrBuilder(); + + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> + getCommunicationsList(); + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommunications(int index); + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + int getCommunicationsCount(); + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommunicationsOrBuilderList(); + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommunicationsOrBuilder( + int index); + } + /** + * Protobuf type {@code PerceptionLogProto} + */ + public static final class PerceptionLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:PerceptionLogProto) + PerceptionLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use PerceptionLogProto.newBuilder() to construct. + private PerceptionLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private PerceptionLogProto() { + communications_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new PerceptionLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PerceptionLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + time_ = input.readInt32(); + break; + } + case 16: { + + entityID_ = input.readInt32(); + break; + } + case 26: { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder subBuilder = null; + if (visible_ != null) { + subBuilder = visible_.toBuilder(); + } + visible_ = input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(visible_); + visible_ = subBuilder.buildPartial(); + } + + break; + } + case 34: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + communications_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(); + mutable_bitField0_ |= 0x00000001; + } + communications_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.MessageProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + communications_ = java.util.Collections.unmodifiableList(communications_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_PerceptionLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_PerceptionLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder.class); + } + + public static final int TIME_FIELD_NUMBER = 1; + private int time_; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + + public static final int ENTITYID_FIELD_NUMBER = 2; + private int entityID_; + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + + public static final int VISIBLE_FIELD_NUMBER = 3; + private rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto visible_; + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return Whether the visible field is set. + */ + @java.lang.Override + public boolean hasVisible() { + return visible_ != null; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return The visible. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getVisible() { + return visible_ == null ? rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : visible_; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getVisibleOrBuilder() { + return getVisible(); + } + + public static final int COMMUNICATIONS_FIELD_NUMBER = 4; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> communications_; + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommunicationsList() { + return communications_; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommunicationsOrBuilderList() { + return communications_; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + @java.lang.Override + public int getCommunicationsCount() { + return communications_.size(); + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommunications(int index) { + return communications_.get(index); + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommunicationsOrBuilder( + int index) { + return communications_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (time_ != 0) { + output.writeInt32(1, time_); + } + if (entityID_ != 0) { + output.writeInt32(2, entityID_); + } + if (visible_ != null) { + output.writeMessage(3, getVisible()); + } + for (int i = 0; i < communications_.size(); i++) { + output.writeMessage(4, communications_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (time_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, time_); + } + if (entityID_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, entityID_); + } + if (visible_ != null) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getVisible()); + } + for (int i = 0; i < communications_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, communications_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) obj; + + if (getTime() + != other.getTime()) return false; + if (getEntityID() + != other.getEntityID()) return false; + if (hasVisible() != other.hasVisible()) return false; + if (hasVisible()) { + if (!getVisible() + .equals(other.getVisible())) return false; + } + if (!getCommunicationsList() + .equals(other.getCommunicationsList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TIME_FIELD_NUMBER; + hash = (53 * hash) + getTime(); + hash = (37 * hash) + ENTITYID_FIELD_NUMBER; + hash = (53 * hash) + getEntityID(); + if (hasVisible()) { + hash = (37 * hash) + VISIBLE_FIELD_NUMBER; + hash = (53 * hash) + getVisible().hashCode(); + } + if (getCommunicationsCount() > 0) { + hash = (37 * hash) + COMMUNICATIONS_FIELD_NUMBER; + hash = (53 * hash) + getCommunicationsList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code PerceptionLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:PerceptionLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_PerceptionLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_PerceptionLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getCommunicationsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + time_ = 0; + + entityID_ = 0; + + if (visibleBuilder_ == null) { + visible_ = null; + } else { + visible_ = null; + visibleBuilder_ = null; + } + if (communicationsBuilder_ == null) { + communications_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + communicationsBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_PerceptionLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto(this); + int from_bitField0_ = bitField0_; + result.time_ = time_; + result.entityID_ = entityID_; + if (visibleBuilder_ == null) { + result.visible_ = visible_; + } else { + result.visible_ = visibleBuilder_.build(); + } + if (communicationsBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + communications_ = java.util.Collections.unmodifiableList(communications_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.communications_ = communications_; + } else { + result.communications_ = communicationsBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto.getDefaultInstance()) return this; + if (other.getTime() != 0) { + setTime(other.getTime()); + } + if (other.getEntityID() != 0) { + setEntityID(other.getEntityID()); + } + if (other.hasVisible()) { + mergeVisible(other.getVisible()); + } + if (communicationsBuilder_ == null) { + if (!other.communications_.isEmpty()) { + if (communications_.isEmpty()) { + communications_ = other.communications_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureCommunicationsIsMutable(); + communications_.addAll(other.communications_); + } + onChanged(); + } + } else { + if (!other.communications_.isEmpty()) { + if (communicationsBuilder_.isEmpty()) { + communicationsBuilder_.dispose(); + communicationsBuilder_ = null; + communications_ = other.communications_; + bitField0_ = (bitField0_ & ~0x00000001); + communicationsBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getCommunicationsFieldBuilder() : null; + } else { + communicationsBuilder_.addAllMessages(other.communications_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int time_ ; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + /** + * <code>int32 time = 1;</code> + * @param value The time to set. + * @return This builder for chaining. + */ + public Builder setTime(int value) { + + time_ = value; + onChanged(); + return this; + } + /** + * <code>int32 time = 1;</code> + * @return This builder for chaining. + */ + public Builder clearTime() { + + time_ = 0; + onChanged(); + return this; + } + + private int entityID_ ; + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + /** + * <code>int32 entityID = 2;</code> + * @param value The entityID to set. + * @return This builder for chaining. + */ + public Builder setEntityID(int value) { + + entityID_ = value; + onChanged(); + return this; + } + /** + * <code>int32 entityID = 2;</code> + * @return This builder for chaining. + */ + public Builder clearEntityID() { + + entityID_ = 0; + onChanged(); + return this; + } + + private rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto visible_; + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> visibleBuilder_; + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return Whether the visible field is set. + */ + public boolean hasVisible() { + return visibleBuilder_ != null || visible_ != null; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + * @return The visible. + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getVisible() { + if (visibleBuilder_ == null) { + return visible_ == null ? rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : visible_; + } else { + return visibleBuilder_.getMessage(); + } + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public Builder setVisible(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (visibleBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + visible_ = value; + onChanged(); + } else { + visibleBuilder_.setMessage(value); + } + + return this; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public Builder setVisible( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder builderForValue) { + if (visibleBuilder_ == null) { + visible_ = builderForValue.build(); + onChanged(); + } else { + visibleBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public Builder mergeVisible(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (visibleBuilder_ == null) { + if (visible_ != null) { + visible_ = + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.newBuilder(visible_).mergeFrom(value).buildPartial(); + } else { + visible_ = value; + } + onChanged(); + } else { + visibleBuilder_.mergeFrom(value); + } + + return this; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public Builder clearVisible() { + if (visibleBuilder_ == null) { + visible_ = null; + onChanged(); + } else { + visible_ = null; + visibleBuilder_ = null; + } + + return this; + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder getVisibleBuilder() { + + onChanged(); + return getVisibleFieldBuilder().getBuilder(); + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getVisibleOrBuilder() { + if (visibleBuilder_ != null) { + return visibleBuilder_.getMessageOrBuilder(); + } else { + return visible_ == null ? + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : visible_; + } + } + /** + * <code>.ChangeSetProto visible = 3;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> + getVisibleFieldBuilder() { + if (visibleBuilder_ == null) { + visibleBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder>( + getVisible(), + getParentForChildren(), + isClean()); + visible_ = null; + } + return visibleBuilder_; + } + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> communications_ = + java.util.Collections.emptyList(); + private void ensureCommunicationsIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + communications_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(communications_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> communicationsBuilder_; + + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommunicationsList() { + if (communicationsBuilder_ == null) { + return java.util.Collections.unmodifiableList(communications_); + } else { + return communicationsBuilder_.getMessageList(); + } + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public int getCommunicationsCount() { + if (communicationsBuilder_ == null) { + return communications_.size(); + } else { + return communicationsBuilder_.getCount(); + } + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommunications(int index) { + if (communicationsBuilder_ == null) { + return communications_.get(index); + } else { + return communicationsBuilder_.getMessage(index); + } + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder setCommunications( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (communicationsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommunicationsIsMutable(); + communications_.set(index, value); + onChanged(); + } else { + communicationsBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder setCommunications( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (communicationsBuilder_ == null) { + ensureCommunicationsIsMutable(); + communications_.set(index, builderForValue.build()); + onChanged(); + } else { + communicationsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder addCommunications(rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (communicationsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommunicationsIsMutable(); + communications_.add(value); + onChanged(); + } else { + communicationsBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder addCommunications( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (communicationsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommunicationsIsMutable(); + communications_.add(index, value); + onChanged(); + } else { + communicationsBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder addCommunications( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (communicationsBuilder_ == null) { + ensureCommunicationsIsMutable(); + communications_.add(builderForValue.build()); + onChanged(); + } else { + communicationsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder addCommunications( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (communicationsBuilder_ == null) { + ensureCommunicationsIsMutable(); + communications_.add(index, builderForValue.build()); + onChanged(); + } else { + communicationsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder addAllCommunications( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProto> values) { + if (communicationsBuilder_ == null) { + ensureCommunicationsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, communications_); + onChanged(); + } else { + communicationsBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder clearCommunications() { + if (communicationsBuilder_ == null) { + communications_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + communicationsBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public Builder removeCommunications(int index) { + if (communicationsBuilder_ == null) { + ensureCommunicationsIsMutable(); + communications_.remove(index); + onChanged(); + } else { + communicationsBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder getCommunicationsBuilder( + int index) { + return getCommunicationsFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommunicationsOrBuilder( + int index) { + if (communicationsBuilder_ == null) { + return communications_.get(index); } else { + return communicationsBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommunicationsOrBuilderList() { + if (communicationsBuilder_ != null) { + return communicationsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(communications_); + } + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommunicationsBuilder() { + return getCommunicationsFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommunicationsBuilder( + int index) { + return getCommunicationsFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto communications = 4;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder> + getCommunicationsBuilderList() { + return getCommunicationsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommunicationsFieldBuilder() { + if (communicationsBuilder_ == null) { + communicationsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder>( + communications_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + communications_ = null; + } + return communicationsBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:PerceptionLogProto) + } + + // @@protoc_insertion_point(class_scope:PerceptionLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<PerceptionLogProto> + PARSER = new com.google.protobuf.AbstractParser<PerceptionLogProto>() { + @java.lang.Override + public PerceptionLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PerceptionLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<PerceptionLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<PerceptionLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.PerceptionLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface ConfigLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:ConfigLogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>.ConfigProto config = 1;</code> + * @return Whether the config field is set. + */ + boolean hasConfig(); + /** + * <code>.ConfigProto config = 1;</code> + * @return The config. + */ + rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig(); + /** + * <code>.ConfigProto config = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder(); + } + /** + * Protobuf type {@code ConfigLogProto} + */ + public static final class ConfigLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:ConfigLogProto) + ConfigLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use ConfigLogProto.newBuilder() to construct. + private ConfigLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private ConfigLogProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ConfigLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ConfigLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder subBuilder = null; + if (config_ != null) { + subBuilder = config_.toBuilder(); + } + config_ = input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ConfigProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(config_); + config_ = subBuilder.buildPartial(); + } + + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_ConfigLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_ConfigLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder.class); + } + + public static final int CONFIG_FIELD_NUMBER = 1; + private rescuecore2.messages.protobuf.RCRSProto.ConfigProto config_; + /** + * <code>.ConfigProto config = 1;</code> + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return config_ != null; + } + /** + * <code>.ConfigProto config = 1;</code> + * @return The config. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig() { + return config_ == null ? rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance() : config_; + } + /** + * <code>.ConfigProto config = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder() { + return getConfig(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (config_ != null) { + output.writeMessage(1, getConfig()); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (config_ != null) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, getConfig()); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) obj; + + if (hasConfig() != other.hasConfig()) return false; + if (hasConfig()) { + if (!getConfig() + .equals(other.getConfig())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasConfig()) { + hash = (37 * hash) + CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getConfig().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ConfigLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:ConfigLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_ConfigLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_ConfigLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (configBuilder_ == null) { + config_ = null; + } else { + config_ = null; + configBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_ConfigLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto(this); + if (configBuilder_ == null) { + result.config_ = config_; + } else { + result.config_ = configBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto.getDefaultInstance()) return this; + if (other.hasConfig()) { + mergeConfig(other.getConfig()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private rescuecore2.messages.protobuf.RCRSProto.ConfigProto config_; + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder> configBuilder_; + /** + * <code>.ConfigProto config = 1;</code> + * @return Whether the config field is set. + */ + public boolean hasConfig() { + return configBuilder_ != null || config_ != null; + } + /** + * <code>.ConfigProto config = 1;</code> + * @return The config. + */ + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig() { + if (configBuilder_ == null) { + return config_ == null ? rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance() : config_; + } else { + return configBuilder_.getMessage(); + } + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public Builder setConfig(rescuecore2.messages.protobuf.RCRSProto.ConfigProto value) { + if (configBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + config_ = value; + onChanged(); + } else { + configBuilder_.setMessage(value); + } + + return this; + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public Builder setConfig( + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder builderForValue) { + if (configBuilder_ == null) { + config_ = builderForValue.build(); + onChanged(); + } else { + configBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public Builder mergeConfig(rescuecore2.messages.protobuf.RCRSProto.ConfigProto value) { + if (configBuilder_ == null) { + if (config_ != null) { + config_ = + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.newBuilder(config_).mergeFrom(value).buildPartial(); + } else { + config_ = value; + } + onChanged(); + } else { + configBuilder_.mergeFrom(value); + } + + return this; + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public Builder clearConfig() { + if (configBuilder_ == null) { + config_ = null; + onChanged(); + } else { + config_ = null; + configBuilder_ = null; + } + + return this; + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder getConfigBuilder() { + + onChanged(); + return getConfigFieldBuilder().getBuilder(); + } + /** + * <code>.ConfigProto config = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder() { + if (configBuilder_ != null) { + return configBuilder_.getMessageOrBuilder(); + } else { + return config_ == null ? + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance() : config_; + } + } + /** + * <code>.ConfigProto config = 1;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder> + getConfigFieldBuilder() { + if (configBuilder_ == null) { + configBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder>( + getConfig(), + getParentForChildren(), + isClean()); + config_ = null; + } + return configBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:ConfigLogProto) + } + + // @@protoc_insertion_point(class_scope:ConfigLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ConfigLogProto> + PARSER = new com.google.protobuf.AbstractParser<ConfigLogProto>() { + @java.lang.Override + public ConfigLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ConfigLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<ConfigLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ConfigLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.ConfigLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface UpdatesLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:UpdatesLogProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + int getTime(); + + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return Whether the changes field is set. + */ + boolean hasChanges(); + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return The changes. + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChanges(); + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangesOrBuilder(); + } + /** + * Protobuf type {@code UpdatesLogProto} + */ + public static final class UpdatesLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:UpdatesLogProto) + UpdatesLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use UpdatesLogProto.newBuilder() to construct. + private UpdatesLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private UpdatesLogProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new UpdatesLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private UpdatesLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + time_ = input.readInt32(); + break; + } + case 18: { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder subBuilder = null; + if (changes_ != null) { + subBuilder = changes_.toBuilder(); + } + changes_ = input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(changes_); + changes_ = subBuilder.buildPartial(); + } + + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_UpdatesLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_UpdatesLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder.class); + } + + public static final int TIME_FIELD_NUMBER = 1; + private int time_; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + + public static final int CHANGES_FIELD_NUMBER = 2; + private rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto changes_; + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return Whether the changes field is set. + */ + @java.lang.Override + public boolean hasChanges() { + return changes_ != null; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return The changes. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChanges() { + return changes_ == null ? rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : changes_; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangesOrBuilder() { + return getChanges(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (time_ != 0) { + output.writeInt32(1, time_); + } + if (changes_ != null) { + output.writeMessage(2, getChanges()); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (time_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, time_); + } + if (changes_ != null) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getChanges()); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) obj; + + if (getTime() + != other.getTime()) return false; + if (hasChanges() != other.hasChanges()) return false; + if (hasChanges()) { + if (!getChanges() + .equals(other.getChanges())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TIME_FIELD_NUMBER; + hash = (53 * hash) + getTime(); + if (hasChanges()) { + hash = (37 * hash) + CHANGES_FIELD_NUMBER; + hash = (53 * hash) + getChanges().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code UpdatesLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:UpdatesLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_UpdatesLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_UpdatesLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + time_ = 0; + + if (changesBuilder_ == null) { + changes_ = null; + } else { + changes_ = null; + changesBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_UpdatesLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto(this); + result.time_ = time_; + if (changesBuilder_ == null) { + result.changes_ = changes_; + } else { + result.changes_ = changesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto.getDefaultInstance()) return this; + if (other.getTime() != 0) { + setTime(other.getTime()); + } + if (other.hasChanges()) { + mergeChanges(other.getChanges()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int time_ ; + /** + * <code>int32 time = 1;</code> + * @return The time. + */ + @java.lang.Override + public int getTime() { + return time_; + } + /** + * <code>int32 time = 1;</code> + * @param value The time to set. + * @return This builder for chaining. + */ + public Builder setTime(int value) { + + time_ = value; + onChanged(); + return this; + } + /** + * <code>int32 time = 1;</code> + * @return This builder for chaining. + */ + public Builder clearTime() { + + time_ = 0; + onChanged(); + return this; + } + + private rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto changes_; + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> changesBuilder_; + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return Whether the changes field is set. + */ + public boolean hasChanges() { + return changesBuilder_ != null || changes_ != null; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + * @return The changes. + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChanges() { + if (changesBuilder_ == null) { + return changes_ == null ? rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : changes_; + } else { + return changesBuilder_.getMessage(); + } + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public Builder setChanges(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (changesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + changes_ = value; + onChanged(); + } else { + changesBuilder_.setMessage(value); + } + + return this; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public Builder setChanges( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder builderForValue) { + if (changesBuilder_ == null) { + changes_ = builderForValue.build(); + onChanged(); + } else { + changesBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public Builder mergeChanges(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (changesBuilder_ == null) { + if (changes_ != null) { + changes_ = + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.newBuilder(changes_).mergeFrom(value).buildPartial(); + } else { + changes_ = value; + } + onChanged(); + } else { + changesBuilder_.mergeFrom(value); + } + + return this; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public Builder clearChanges() { + if (changesBuilder_ == null) { + changes_ = null; + onChanged(); + } else { + changes_ = null; + changesBuilder_ = null; + } + + return this; + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder getChangesBuilder() { + + onChanged(); + return getChangesFieldBuilder().getBuilder(); + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangesOrBuilder() { + if (changesBuilder_ != null) { + return changesBuilder_.getMessageOrBuilder(); + } else { + return changes_ == null ? + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance() : changes_; + } + } + /** + * <code>.ChangeSetProto changes = 2;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> + getChangesFieldBuilder() { + if (changesBuilder_ == null) { + changesBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder>( + getChanges(), + getParentForChildren(), + isClean()); + changes_ = null; + } + return changesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:UpdatesLogProto) + } + + // @@protoc_insertion_point(class_scope:UpdatesLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<UpdatesLogProto> + PARSER = new com.google.protobuf.AbstractParser<UpdatesLogProto>() { + @java.lang.Override + public UpdatesLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new UpdatesLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<UpdatesLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<UpdatesLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.UpdatesLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface EndLogProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:EndLogProto) + com.google.protobuf.MessageOrBuilder { + } + /** + * Protobuf type {@code EndLogProto} + */ + public static final class EndLogProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:EndLogProto) + EndLogProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EndLogProto.newBuilder() to construct. + private EndLogProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EndLogProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EndLogProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EndLogProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_EndLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_EndLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder.class); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto other = (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) obj; + + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code EndLogProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:EndLogProto) + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_EndLogProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_EndLogProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.class, rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.internal_static_EndLogProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto build() { + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto buildPartial() { + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto result = new rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto(this); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto other) { + if (other == rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto.getDefaultInstance()) return this; + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:EndLogProto) + } + + // @@protoc_insertion_point(class_scope:EndLogProto) + private static final rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto(); + } + + public static rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EndLogProto> + PARSER = new com.google.protobuf.AbstractParser<EndLogProto>() { + @java.lang.Override + public EndLogProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EndLogProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EndLogProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EndLogProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSLogProto.EndLogProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_LogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_LogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_StartLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_StartLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_InitialConditionsLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_InitialConditionsLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_CommandLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_CommandLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_PerceptionLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_PerceptionLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ConfigLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ConfigLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_UpdatesLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_UpdatesLogProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_EndLogProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_EndLogProto_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\022RCRSLogProto.proto\032\017RCRSProto.proto\"\236\002" + + "\n\010LogProto\022\037\n\005start\030\001 \001(\0132\016.StartLogProt" + + "oH\000\0226\n\020initialCondition\030\002 \001(\0132\032.InitialC" + + "onditionsLogProtoH\000\022#\n\007command\030\003 \001(\0132\020.C" + + "ommandLogProtoH\000\022)\n\nperception\030\004 \001(\0132\023.P" + + "erceptionLogProtoH\000\022!\n\006config\030\005 \001(\0132\017.Co" + + "nfigLogProtoH\000\022\"\n\006update\030\006 \001(\0132\020.Updates" + + "LogProtoH\000\022\033\n\003end\030\007 \001(\0132\014.EndLogProtoH\000B" + + "\005\n\003log\"\017\n\rStartLogProto\";\n\031InitialCondit" + + "ionsLogProto\022\036\n\010entities\030\001 \003(\0132\014.EntityP" + + "roto\"@\n\017CommandLogProto\022\014\n\004time\030\001 \001(\005\022\037\n" + + "\010commands\030\002 \003(\0132\r.MessageProto\"}\n\022Percep" + + "tionLogProto\022\014\n\004time\030\001 \001(\005\022\020\n\010entityID\030\002" + + " \001(\005\022 \n\007visible\030\003 \001(\0132\017.ChangeSetProto\022%" + + "\n\016communications\030\004 \003(\0132\r.MessageProto\".\n" + + "\016ConfigLogProto\022\034\n\006config\030\001 \001(\0132\014.Config" + + "Proto\"A\n\017UpdatesLogProto\022\014\n\004time\030\001 \001(\005\022 " + + "\n\007changes\030\002 \001(\0132\017.ChangeSetProto\"\r\n\013EndL" + + "ogProtoB-\n\035rescuecore2.messages.protobuf" + + "B\014RCRSLogProtob\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + rescuecore2.messages.protobuf.RCRSProto.getDescriptor(), + }); + internal_static_LogProto_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_LogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_LogProto_descriptor, + new java.lang.String[] { "Start", "InitialCondition", "Command", "Perception", "Config", "Update", "End", "Log", }); + internal_static_StartLogProto_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_StartLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_StartLogProto_descriptor, + new java.lang.String[] { }); + internal_static_InitialConditionsLogProto_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_InitialConditionsLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_InitialConditionsLogProto_descriptor, + new java.lang.String[] { "Entities", }); + internal_static_CommandLogProto_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_CommandLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_CommandLogProto_descriptor, + new java.lang.String[] { "Time", "Commands", }); + internal_static_PerceptionLogProto_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_PerceptionLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_PerceptionLogProto_descriptor, + new java.lang.String[] { "Time", "EntityID", "Visible", "Communications", }); + internal_static_ConfigLogProto_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_ConfigLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ConfigLogProto_descriptor, + new java.lang.String[] { "Config", }); + internal_static_UpdatesLogProto_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_UpdatesLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_UpdatesLogProto_descriptor, + new java.lang.String[] { "Time", "Changes", }); + internal_static_EndLogProto_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_EndLogProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_EndLogProto_descriptor, + new java.lang.String[] { }); + rescuecore2.messages.protobuf.RCRSProto.getDescriptor(); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.proto b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.proto new file mode 100644 index 0000000000000000000000000000000000000000..e1e87c7344e18df1d219226fda2f4b4031b8a648 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSLogProto.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +import "RCRSProto.proto"; + +option java_package = "rescuecore2.messages.protobuf"; +option java_outer_classname = "RCRSLogProto"; + +message LogProto { + oneof log { + StartLogProto start = 1; + InitialConditionsLogProto initialCondition = 2; + CommandLogProto command = 3; + PerceptionLogProto perception = 4; + ConfigLogProto config = 5; + UpdatesLogProto update = 6; + EndLogProto end = 7; + } +}; + +message StartLogProto { +}; + +message InitialConditionsLogProto { + repeated EntityProto entities = 1; +}; + +message CommandLogProto { + int32 time = 1; + repeated MessageProto commands = 2; +}; + +message PerceptionLogProto { + int32 time = 1; + int32 entityID = 2; + ChangeSetProto visible = 3; + repeated MessageProto communications = 4; +}; + +message ConfigLogProto { + ConfigProto config = 1; +} + +message UpdatesLogProto { + int32 time = 1; + ChangeSetProto changes = 2; +}; + +message EndLogProto { +}; \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.java b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.java new file mode 100644 index 0000000000000000000000000000000000000000..56680367efe4d5d52b75779d935c5120fe0a0027 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.java @@ -0,0 +1,17988 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: RCRSProto.proto + +package rescuecore2.messages.protobuf; + +public final class RCRSProto { + private RCRSProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface MessageProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:MessageProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + int getUrn(); + + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + int getComponentsCount(); + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + boolean containsComponents( + int key); + /** + * Use {@link #getComponentsMap()} instead. + */ + @java.lang.Deprecated + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + getComponents(); + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + getComponentsMap(); + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrDefault( + int key, + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto defaultValue); + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrThrow( + int key); + } + /** + * Protobuf type {@code MessageProto} + */ + public static final class MessageProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:MessageProto) + MessageProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use MessageProto.newBuilder() to construct. + private MessageProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private MessageProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new MessageProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private MessageProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + urn_ = input.readInt32(); + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + components_ = com.google.protobuf.MapField.newMapField( + ComponentsDefaultEntryHolder.defaultEntry); + mutable_bitField0_ |= 0x00000001; + } + com.google.protobuf.MapEntry<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + components__ = input.readMessage( + ComponentsDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + components_.getMutableMap().put( + components__.getKey(), components__.getValue()); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapField internalGetMapField( + int number) { + switch (number) { + case 2: + return internalGetComponents(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder.class); + } + + public static final int URN_FIELD_NUMBER = 1; + private int urn_; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + + public static final int COMPONENTS_FIELD_NUMBER = 2; + private static final class ComponentsDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> defaultEntry = + com.google.protobuf.MapEntry + .<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto>newDefaultInstance( + rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_ComponentsEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.INT32, + 0, + com.google.protobuf.WireFormat.FieldType.MESSAGE, + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.getDefaultInstance()); + } + private com.google.protobuf.MapField< + java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> components_; + private com.google.protobuf.MapField<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + internalGetComponents() { + if (components_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ComponentsDefaultEntryHolder.defaultEntry); + } + return components_; + } + + public int getComponentsCount() { + return internalGetComponents().getMap().size(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + @java.lang.Override + public boolean containsComponents( + int key) { + + return internalGetComponents().getMap().containsKey(key); + } + /** + * Use {@link #getComponentsMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> getComponents() { + return getComponentsMap(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> getComponentsMap() { + return internalGetComponents().getMap(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrDefault( + int key, + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto defaultValue) { + + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> map = + internalGetComponents().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrThrow( + int key) { + + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> map = + internalGetComponents().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (urn_ != 0) { + output.writeInt32(1, urn_); + } + com.google.protobuf.GeneratedMessageV3 + .serializeIntegerMapTo( + output, + internalGetComponents(), + ComponentsDefaultEntryHolder.defaultEntry, + 2); + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (urn_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, urn_); + } + for (java.util.Map.Entry<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> entry + : internalGetComponents().getMap().entrySet()) { + com.google.protobuf.MapEntry<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + components__ = ComponentsDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, components__); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.MessageProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.MessageProto other = (rescuecore2.messages.protobuf.RCRSProto.MessageProto) obj; + + if (getUrn() + != other.getUrn()) return false; + if (!internalGetComponents().equals( + other.internalGetComponents())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URN_FIELD_NUMBER; + hash = (53 * hash) + getUrn(); + if (!internalGetComponents().getMap().isEmpty()) { + hash = (37 * hash) + COMPONENTS_FIELD_NUMBER; + hash = (53 * hash) + internalGetComponents().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.MessageProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code MessageProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:MessageProto) + rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapField internalGetMapField( + int number) { + switch (number) { + case 2: + return internalGetComponents(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapField internalGetMutableMapField( + int number) { + switch (number) { + case 2: + return internalGetMutableComponents(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.MessageProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + urn_ = 0; + + internalGetMutableComponents().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto build() { + rescuecore2.messages.protobuf.RCRSProto.MessageProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.MessageProto result = new rescuecore2.messages.protobuf.RCRSProto.MessageProto(this); + int from_bitField0_ = bitField0_; + result.urn_ = urn_; + result.components_ = internalGetComponents(); + result.components_.makeImmutable(); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.MessageProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.MessageProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.MessageProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()) return this; + if (other.getUrn() != 0) { + setUrn(other.getUrn()); + } + internalGetMutableComponents().mergeFrom( + other.internalGetComponents()); + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.MessageProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.MessageProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int urn_ ; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + /** + * <code>int32 urn = 1;</code> + * @param value The urn to set. + * @return This builder for chaining. + */ + public Builder setUrn(int value) { + + urn_ = value; + onChanged(); + return this; + } + /** + * <code>int32 urn = 1;</code> + * @return This builder for chaining. + */ + public Builder clearUrn() { + + urn_ = 0; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> components_; + private com.google.protobuf.MapField<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + internalGetComponents() { + if (components_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ComponentsDefaultEntryHolder.defaultEntry); + } + return components_; + } + private com.google.protobuf.MapField<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + internalGetMutableComponents() { + onChanged();; + if (components_ == null) { + components_ = com.google.protobuf.MapField.newMapField( + ComponentsDefaultEntryHolder.defaultEntry); + } + if (!components_.isMutable()) { + components_ = components_.copy(); + } + return components_; + } + + public int getComponentsCount() { + return internalGetComponents().getMap().size(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + @java.lang.Override + public boolean containsComponents( + int key) { + + return internalGetComponents().getMap().containsKey(key); + } + /** + * Use {@link #getComponentsMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> getComponents() { + return getComponentsMap(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> getComponentsMap() { + return internalGetComponents().getMap(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrDefault( + int key, + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto defaultValue) { + + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> map = + internalGetComponents().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + @java.lang.Override + + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getComponentsOrThrow( + int key) { + + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> map = + internalGetComponents().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + public Builder clearComponents() { + internalGetMutableComponents().getMutableMap() + .clear(); + return this; + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + public Builder removeComponents( + int key) { + + internalGetMutableComponents().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> + getMutableComponents() { + return internalGetMutableComponents().getMutableMap(); + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + public Builder putComponents( + int key, + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto value) { + + if (value == null) { throw new java.lang.NullPointerException(); } + internalGetMutableComponents().getMutableMap() + .put(key, value); + return this; + } + /** + * <code>map<int32, .MessageComponentProto> components = 2;</code> + */ + + public Builder putAllComponents( + java.util.Map<java.lang.Integer, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto> values) { + internalGetMutableComponents().getMutableMap() + .putAll(values); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:MessageProto) + } + + // @@protoc_insertion_point(class_scope:MessageProto) + private static final rescuecore2.messages.protobuf.RCRSProto.MessageProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.MessageProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<MessageProto> + PARSER = new com.google.protobuf.AbstractParser<MessageProto>() { + @java.lang.Override + public MessageProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new MessageProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<MessageProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<MessageProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface MessageListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:MessageListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> + getCommandsList(); + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index); + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + int getCommandsCount(); + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList(); + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index); + } + /** + * Protobuf type {@code MessageListProto} + */ + public static final class MessageListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:MessageListProto) + MessageListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use MessageListProto.newBuilder() to construct. + private MessageListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private MessageListProto() { + commands_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new MessageListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private MessageListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + commands_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(); + mutable_bitField0_ |= 0x00000001; + } + commands_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.MessageProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + commands_ = java.util.Collections.unmodifiableList(commands_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageListProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder.class); + } + + public static final int COMMANDS_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> commands_; + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommandsList() { + return commands_; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList() { + return commands_; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + @java.lang.Override + public int getCommandsCount() { + return commands_.size(); + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index) { + return commands_.get(index); + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index) { + return commands_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < commands_.size(); i++) { + output.writeMessage(1, commands_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < commands_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, commands_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.MessageListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.MessageListProto other = (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) obj; + + if (!getCommandsList() + .equals(other.getCommandsList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getCommandsCount() > 0) { + hash = (37 * hash) + COMMANDS_FIELD_NUMBER; + hash = (53 * hash) + getCommandsList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.MessageListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code MessageListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:MessageListProto) + rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageListProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.MessageListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getCommandsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (commandsBuilder_ == null) { + commands_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + commandsBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto build() { + rescuecore2.messages.protobuf.RCRSProto.MessageListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.MessageListProto result = new rescuecore2.messages.protobuf.RCRSProto.MessageListProto(this); + int from_bitField0_ = bitField0_; + if (commandsBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + commands_ = java.util.Collections.unmodifiableList(commands_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.commands_ = commands_; + } else { + result.commands_ = commandsBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.MessageListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.MessageListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.MessageListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance()) return this; + if (commandsBuilder_ == null) { + if (!other.commands_.isEmpty()) { + if (commands_.isEmpty()) { + commands_ = other.commands_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureCommandsIsMutable(); + commands_.addAll(other.commands_); + } + onChanged(); + } + } else { + if (!other.commands_.isEmpty()) { + if (commandsBuilder_.isEmpty()) { + commandsBuilder_.dispose(); + commandsBuilder_ = null; + commands_ = other.commands_; + bitField0_ = (bitField0_ & ~0x00000001); + commandsBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getCommandsFieldBuilder() : null; + } else { + commandsBuilder_.addAllMessages(other.commands_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.MessageListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> commands_ = + java.util.Collections.emptyList(); + private void ensureCommandsIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + commands_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.MessageProto>(commands_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> commandsBuilder_; + + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto> getCommandsList() { + if (commandsBuilder_ == null) { + return java.util.Collections.unmodifiableList(commands_); + } else { + return commandsBuilder_.getMessageList(); + } + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public int getCommandsCount() { + if (commandsBuilder_ == null) { + return commands_.size(); + } else { + return commandsBuilder_.getCount(); + } + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto getCommands(int index) { + if (commandsBuilder_ == null) { + return commands_.get(index); + } else { + return commandsBuilder_.getMessage(index); + } + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder setCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.set(index, value); + onChanged(); + } else { + commandsBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder setCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.set(index, builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder addCommands(rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.add(value); + onChanged(); + } else { + commandsBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder addCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto value) { + if (commandsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureCommandsIsMutable(); + commands_.add(index, value); + onChanged(); + } else { + commandsBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder addCommands( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.add(builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder addCommands( + int index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder builderForValue) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.add(index, builderForValue.build()); + onChanged(); + } else { + commandsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder addAllCommands( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProto> values) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, commands_); + onChanged(); + } else { + commandsBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder clearCommands() { + if (commandsBuilder_ == null) { + commands_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + commandsBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public Builder removeCommands(int index) { + if (commandsBuilder_ == null) { + ensureCommandsIsMutable(); + commands_.remove(index); + onChanged(); + } else { + commandsBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder getCommandsBuilder( + int index) { + return getCommandsFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder getCommandsOrBuilder( + int index) { + if (commandsBuilder_ == null) { + return commands_.get(index); } else { + return commandsBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsOrBuilderList() { + if (commandsBuilder_ != null) { + return commandsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(commands_); + } + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommandsBuilder() { + return getCommandsFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder addCommandsBuilder( + int index) { + return getCommandsFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.MessageProto.getDefaultInstance()); + } + /** + * <code>repeated .MessageProto commands = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder> + getCommandsBuilderList() { + return getCommandsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder> + getCommandsFieldBuilder() { + if (commandsBuilder_ == null) { + commandsBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageProto, rescuecore2.messages.protobuf.RCRSProto.MessageProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageProtoOrBuilder>( + commands_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + commands_ = null; + } + return commandsBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:MessageListProto) + } + + // @@protoc_insertion_point(class_scope:MessageListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.MessageListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.MessageListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<MessageListProto> + PARSER = new com.google.protobuf.AbstractParser<MessageListProto>() { + @java.lang.Override + public MessageListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new MessageListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<MessageListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<MessageListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface MessageComponentProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:MessageComponentProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return Whether the changeSet field is set. + */ + boolean hasChangeSet(); + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return The changeSet. + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChangeSet(); + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangeSetOrBuilder(); + + /** + * <code>.MessageListProto commandList = 2;</code> + * @return Whether the commandList field is set. + */ + boolean hasCommandList(); + /** + * <code>.MessageListProto commandList = 2;</code> + * @return The commandList. + */ + rescuecore2.messages.protobuf.RCRSProto.MessageListProto getCommandList(); + /** + * <code>.MessageListProto commandList = 2;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder getCommandListOrBuilder(); + + /** + * <code>.ConfigProto config = 3;</code> + * @return Whether the config field is set. + */ + boolean hasConfig(); + /** + * <code>.ConfigProto config = 3;</code> + * @return The config. + */ + rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig(); + /** + * <code>.ConfigProto config = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder(); + + /** + * <code>.EntityProto entity = 4;</code> + * @return Whether the entity field is set. + */ + boolean hasEntity(); + /** + * <code>.EntityProto entity = 4;</code> + * @return The entity. + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntity(); + /** + * <code>.EntityProto entity = 4;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntityOrBuilder(); + + /** + * <code>int32 entityID = 5;</code> + * @return Whether the entityID field is set. + */ + boolean hasEntityID(); + /** + * <code>int32 entityID = 5;</code> + * @return The entityID. + */ + int getEntityID(); + + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return Whether the entityIDList field is set. + */ + boolean hasEntityIDList(); + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return The entityIDList. + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProto getEntityIDList(); + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getEntityIDListOrBuilder(); + + /** + * <code>.EntityListProto entityList = 7;</code> + * @return Whether the entityList field is set. + */ + boolean hasEntityList(); + /** + * <code>.EntityListProto entityList = 7;</code> + * @return The entityList. + */ + rescuecore2.messages.protobuf.RCRSProto.EntityListProto getEntityList(); + /** + * <code>.EntityListProto entityList = 7;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder getEntityListOrBuilder(); + + /** + * <code>.FloatListProto floatList = 8;</code> + * @return Whether the floatList field is set. + */ + boolean hasFloatList(); + /** + * <code>.FloatListProto floatList = 8;</code> + * @return The floatList. + */ + rescuecore2.messages.protobuf.RCRSProto.FloatListProto getFloatList(); + /** + * <code>.FloatListProto floatList = 8;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder getFloatListOrBuilder(); + + /** + * <code>int32 intValue = 9;</code> + * @return Whether the intValue field is set. + */ + boolean hasIntValue(); + /** + * <code>int32 intValue = 9;</code> + * @return The intValue. + */ + int getIntValue(); + + /** + * <code>.IntListProto intList = 10;</code> + * @return Whether the intList field is set. + */ + boolean hasIntList(); + /** + * <code>.IntListProto intList = 10;</code> + * @return The intList. + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList(); + /** + * <code>.IntListProto intList = 10;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder(); + + /** + * <code>bytes rawData = 11;</code> + * @return Whether the rawData field is set. + */ + boolean hasRawData(); + /** + * <code>bytes rawData = 11;</code> + * @return The rawData. + */ + com.google.protobuf.ByteString getRawData(); + + /** + * <code>string stringValue = 12;</code> + * @return Whether the stringValue field is set. + */ + boolean hasStringValue(); + /** + * <code>string stringValue = 12;</code> + * @return The stringValue. + */ + java.lang.String getStringValue(); + /** + * <code>string stringValue = 12;</code> + * @return The bytes for stringValue. + */ + com.google.protobuf.ByteString + getStringValueBytes(); + + /** + * <code>.StrListProto stringList = 13;</code> + * @return Whether the stringList field is set. + */ + boolean hasStringList(); + /** + * <code>.StrListProto stringList = 13;</code> + * @return The stringList. + */ + rescuecore2.messages.protobuf.RCRSProto.StrListProto getStringList(); + /** + * <code>.StrListProto stringList = 13;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder getStringListOrBuilder(); + + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.ComponentCase getComponentCase(); + } + /** + * Protobuf type {@code MessageComponentProto} + */ + public static final class MessageComponentProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:MessageComponentProto) + MessageComponentProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use MessageComponentProto.newBuilder() to construct. + private MessageComponentProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private MessageComponentProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new MessageComponentProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private MessageComponentProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder subBuilder = null; + if (componentCase_ == 1) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 1; + break; + } + case 18: { + rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder subBuilder = null; + if (componentCase_ == 2) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.MessageListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 2; + break; + } + case 26: { + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder subBuilder = null; + if (componentCase_ == 3) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ConfigProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 3; + break; + } + case 34: { + rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder subBuilder = null; + if (componentCase_ == 4) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EntityProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 4; + break; + } + case 40: { + componentCase_ = 5; + component_ = input.readInt32(); + break; + } + case 50: { + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder subBuilder = null; + if (componentCase_ == 6) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 6; + break; + } + case 58: { + rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder subBuilder = null; + if (componentCase_ == 7) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EntityListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 7; + break; + } + case 66: { + rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder subBuilder = null; + if (componentCase_ == 8) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.FloatListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 8; + break; + } + case 72: { + componentCase_ = 9; + component_ = input.readInt32(); + break; + } + case 82: { + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder subBuilder = null; + if (componentCase_ == 10) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 10; + break; + } + case 90: { + componentCase_ = 11; + component_ = input.readBytes(); + break; + } + case 98: { + java.lang.String s = input.readStringRequireUtf8(); + componentCase_ = 12; + component_ = s; + break; + } + case 106: { + rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder subBuilder = null; + if (componentCase_ == 13) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_).toBuilder(); + } + component_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.StrListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_); + component_ = subBuilder.buildPartial(); + } + componentCase_ = 13; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageComponentProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageComponentProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.Builder.class); + } + + private int componentCase_ = 0; + private java.lang.Object component_; + public enum ComponentCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + CHANGESET(1), + COMMANDLIST(2), + CONFIG(3), + ENTITY(4), + ENTITYID(5), + ENTITYIDLIST(6), + ENTITYLIST(7), + FLOATLIST(8), + INTVALUE(9), + INTLIST(10), + RAWDATA(11), + STRINGVALUE(12), + STRINGLIST(13), + COMPONENT_NOT_SET(0); + private final int value; + private ComponentCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static ComponentCase valueOf(int value) { + return forNumber(value); + } + + public static ComponentCase forNumber(int value) { + switch (value) { + case 1: return CHANGESET; + case 2: return COMMANDLIST; + case 3: return CONFIG; + case 4: return ENTITY; + case 5: return ENTITYID; + case 6: return ENTITYIDLIST; + case 7: return ENTITYLIST; + case 8: return FLOATLIST; + case 9: return INTVALUE; + case 10: return INTLIST; + case 11: return RAWDATA; + case 12: return STRINGVALUE; + case 13: return STRINGLIST; + case 0: return COMPONENT_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public ComponentCase + getComponentCase() { + return ComponentCase.forNumber( + componentCase_); + } + + public static final int CHANGESET_FIELD_NUMBER = 1; + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return Whether the changeSet field is set. + */ + @java.lang.Override + public boolean hasChangeSet() { + return componentCase_ == 1; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return The changeSet. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChangeSet() { + if (componentCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangeSetOrBuilder() { + if (componentCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + + public static final int COMMANDLIST_FIELD_NUMBER = 2; + /** + * <code>.MessageListProto commandList = 2;</code> + * @return Whether the commandList field is set. + */ + @java.lang.Override + public boolean hasCommandList() { + return componentCase_ == 2; + } + /** + * <code>.MessageListProto commandList = 2;</code> + * @return The commandList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto getCommandList() { + if (componentCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder getCommandListOrBuilder() { + if (componentCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + + public static final int CONFIG_FIELD_NUMBER = 3; + /** + * <code>.ConfigProto config = 3;</code> + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return componentCase_ == 3; + } + /** + * <code>.ConfigProto config = 3;</code> + * @return The config. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig() { + if (componentCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + /** + * <code>.ConfigProto config = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder() { + if (componentCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + + public static final int ENTITY_FIELD_NUMBER = 4; + /** + * <code>.EntityProto entity = 4;</code> + * @return Whether the entity field is set. + */ + @java.lang.Override + public boolean hasEntity() { + return componentCase_ == 4; + } + /** + * <code>.EntityProto entity = 4;</code> + * @return The entity. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntity() { + if (componentCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + /** + * <code>.EntityProto entity = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntityOrBuilder() { + if (componentCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + + public static final int ENTITYID_FIELD_NUMBER = 5; + /** + * <code>int32 entityID = 5;</code> + * @return Whether the entityID field is set. + */ + @java.lang.Override + public boolean hasEntityID() { + return componentCase_ == 5; + } + /** + * <code>int32 entityID = 5;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + if (componentCase_ == 5) { + return (java.lang.Integer) component_; + } + return 0; + } + + public static final int ENTITYIDLIST_FIELD_NUMBER = 6; + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return Whether the entityIDList field is set. + */ + @java.lang.Override + public boolean hasEntityIDList() { + return componentCase_ == 6; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return The entityIDList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getEntityIDList() { + if (componentCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getEntityIDListOrBuilder() { + if (componentCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + + public static final int ENTITYLIST_FIELD_NUMBER = 7; + /** + * <code>.EntityListProto entityList = 7;</code> + * @return Whether the entityList field is set. + */ + @java.lang.Override + public boolean hasEntityList() { + return componentCase_ == 7; + } + /** + * <code>.EntityListProto entityList = 7;</code> + * @return The entityList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto getEntityList() { + if (componentCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder getEntityListOrBuilder() { + if (componentCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + + public static final int FLOATLIST_FIELD_NUMBER = 8; + /** + * <code>.FloatListProto floatList = 8;</code> + * @return Whether the floatList field is set. + */ + @java.lang.Override + public boolean hasFloatList() { + return componentCase_ == 8; + } + /** + * <code>.FloatListProto floatList = 8;</code> + * @return The floatList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto getFloatList() { + if (componentCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder getFloatListOrBuilder() { + if (componentCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + + public static final int INTVALUE_FIELD_NUMBER = 9; + /** + * <code>int32 intValue = 9;</code> + * @return Whether the intValue field is set. + */ + @java.lang.Override + public boolean hasIntValue() { + return componentCase_ == 9; + } + /** + * <code>int32 intValue = 9;</code> + * @return The intValue. + */ + @java.lang.Override + public int getIntValue() { + if (componentCase_ == 9) { + return (java.lang.Integer) component_; + } + return 0; + } + + public static final int INTLIST_FIELD_NUMBER = 10; + /** + * <code>.IntListProto intList = 10;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return componentCase_ == 10; + } + /** + * <code>.IntListProto intList = 10;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (componentCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + /** + * <code>.IntListProto intList = 10;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if (componentCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + + public static final int RAWDATA_FIELD_NUMBER = 11; + /** + * <code>bytes rawData = 11;</code> + * @return Whether the rawData field is set. + */ + @java.lang.Override + public boolean hasRawData() { + return componentCase_ == 11; + } + /** + * <code>bytes rawData = 11;</code> + * @return The rawData. + */ + @java.lang.Override + public com.google.protobuf.ByteString getRawData() { + if (componentCase_ == 11) { + return (com.google.protobuf.ByteString) component_; + } + return com.google.protobuf.ByteString.EMPTY; + } + + public static final int STRINGVALUE_FIELD_NUMBER = 12; + /** + * <code>string stringValue = 12;</code> + * @return Whether the stringValue field is set. + */ + public boolean hasStringValue() { + return componentCase_ == 12; + } + /** + * <code>string stringValue = 12;</code> + * @return The stringValue. + */ + public java.lang.String getStringValue() { + java.lang.Object ref = ""; + if (componentCase_ == 12) { + ref = component_; + } + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (componentCase_ == 12) { + component_ = s; + } + return s; + } + } + /** + * <code>string stringValue = 12;</code> + * @return The bytes for stringValue. + */ + public com.google.protobuf.ByteString + getStringValueBytes() { + java.lang.Object ref = ""; + if (componentCase_ == 12) { + ref = component_; + } + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (componentCase_ == 12) { + component_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int STRINGLIST_FIELD_NUMBER = 13; + /** + * <code>.StrListProto stringList = 13;</code> + * @return Whether the stringList field is set. + */ + @java.lang.Override + public boolean hasStringList() { + return componentCase_ == 13; + } + /** + * <code>.StrListProto stringList = 13;</code> + * @return The stringList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto getStringList() { + if (componentCase_ == 13) { + return (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder getStringListOrBuilder() { + if (componentCase_ == 13) { + return (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (componentCase_ == 1) { + output.writeMessage(1, (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_); + } + if (componentCase_ == 2) { + output.writeMessage(2, (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_); + } + if (componentCase_ == 3) { + output.writeMessage(3, (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_); + } + if (componentCase_ == 4) { + output.writeMessage(4, (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_); + } + if (componentCase_ == 5) { + output.writeInt32( + 5, (int)((java.lang.Integer) component_)); + } + if (componentCase_ == 6) { + output.writeMessage(6, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + } + if (componentCase_ == 7) { + output.writeMessage(7, (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_); + } + if (componentCase_ == 8) { + output.writeMessage(8, (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_); + } + if (componentCase_ == 9) { + output.writeInt32( + 9, (int)((java.lang.Integer) component_)); + } + if (componentCase_ == 10) { + output.writeMessage(10, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + } + if (componentCase_ == 11) { + output.writeBytes( + 11, (com.google.protobuf.ByteString) component_); + } + if (componentCase_ == 12) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 12, component_); + } + if (componentCase_ == 13) { + output.writeMessage(13, (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (componentCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_); + } + if (componentCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_); + } + if (componentCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_); + } + if (componentCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_); + } + if (componentCase_ == 5) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size( + 5, (int)((java.lang.Integer) component_)); + } + if (componentCase_ == 6) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + } + if (componentCase_ == 7) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_); + } + if (componentCase_ == 8) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_); + } + if (componentCase_ == 9) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size( + 9, (int)((java.lang.Integer) component_)); + } + if (componentCase_ == 10) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(10, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_); + } + if (componentCase_ == 11) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize( + 11, (com.google.protobuf.ByteString) component_); + } + if (componentCase_ == 12) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(12, component_); + } + if (componentCase_ == 13) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(13, (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto other = (rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto) obj; + + if (!getComponentCase().equals(other.getComponentCase())) return false; + switch (componentCase_) { + case 1: + if (!getChangeSet() + .equals(other.getChangeSet())) return false; + break; + case 2: + if (!getCommandList() + .equals(other.getCommandList())) return false; + break; + case 3: + if (!getConfig() + .equals(other.getConfig())) return false; + break; + case 4: + if (!getEntity() + .equals(other.getEntity())) return false; + break; + case 5: + if (getEntityID() + != other.getEntityID()) return false; + break; + case 6: + if (!getEntityIDList() + .equals(other.getEntityIDList())) return false; + break; + case 7: + if (!getEntityList() + .equals(other.getEntityList())) return false; + break; + case 8: + if (!getFloatList() + .equals(other.getFloatList())) return false; + break; + case 9: + if (getIntValue() + != other.getIntValue()) return false; + break; + case 10: + if (!getIntList() + .equals(other.getIntList())) return false; + break; + case 11: + if (!getRawData() + .equals(other.getRawData())) return false; + break; + case 12: + if (!getStringValue() + .equals(other.getStringValue())) return false; + break; + case 13: + if (!getStringList() + .equals(other.getStringList())) return false; + break; + case 0: + default: + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (componentCase_) { + case 1: + hash = (37 * hash) + CHANGESET_FIELD_NUMBER; + hash = (53 * hash) + getChangeSet().hashCode(); + break; + case 2: + hash = (37 * hash) + COMMANDLIST_FIELD_NUMBER; + hash = (53 * hash) + getCommandList().hashCode(); + break; + case 3: + hash = (37 * hash) + CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getConfig().hashCode(); + break; + case 4: + hash = (37 * hash) + ENTITY_FIELD_NUMBER; + hash = (53 * hash) + getEntity().hashCode(); + break; + case 5: + hash = (37 * hash) + ENTITYID_FIELD_NUMBER; + hash = (53 * hash) + getEntityID(); + break; + case 6: + hash = (37 * hash) + ENTITYIDLIST_FIELD_NUMBER; + hash = (53 * hash) + getEntityIDList().hashCode(); + break; + case 7: + hash = (37 * hash) + ENTITYLIST_FIELD_NUMBER; + hash = (53 * hash) + getEntityList().hashCode(); + break; + case 8: + hash = (37 * hash) + FLOATLIST_FIELD_NUMBER; + hash = (53 * hash) + getFloatList().hashCode(); + break; + case 9: + hash = (37 * hash) + INTVALUE_FIELD_NUMBER; + hash = (53 * hash) + getIntValue(); + break; + case 10: + hash = (37 * hash) + INTLIST_FIELD_NUMBER; + hash = (53 * hash) + getIntList().hashCode(); + break; + case 11: + hash = (37 * hash) + RAWDATA_FIELD_NUMBER; + hash = (53 * hash) + getRawData().hashCode(); + break; + case 12: + hash = (37 * hash) + STRINGVALUE_FIELD_NUMBER; + hash = (53 * hash) + getStringValue().hashCode(); + break; + case 13: + hash = (37 * hash) + STRINGLIST_FIELD_NUMBER; + hash = (53 * hash) + getStringList().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code MessageComponentProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:MessageComponentProto) + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageComponentProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageComponentProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.class, rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + componentCase_ = 0; + component_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_MessageComponentProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto build() { + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto result = new rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto(this); + if (componentCase_ == 1) { + if (changeSetBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = changeSetBuilder_.build(); + } + } + if (componentCase_ == 2) { + if (commandListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = commandListBuilder_.build(); + } + } + if (componentCase_ == 3) { + if (configBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = configBuilder_.build(); + } + } + if (componentCase_ == 4) { + if (entityBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = entityBuilder_.build(); + } + } + if (componentCase_ == 5) { + result.component_ = component_; + } + if (componentCase_ == 6) { + if (entityIDListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = entityIDListBuilder_.build(); + } + } + if (componentCase_ == 7) { + if (entityListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = entityListBuilder_.build(); + } + } + if (componentCase_ == 8) { + if (floatListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = floatListBuilder_.build(); + } + } + if (componentCase_ == 9) { + result.component_ = component_; + } + if (componentCase_ == 10) { + if (intListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = intListBuilder_.build(); + } + } + if (componentCase_ == 11) { + result.component_ = component_; + } + if (componentCase_ == 12) { + result.component_ = component_; + } + if (componentCase_ == 13) { + if (stringListBuilder_ == null) { + result.component_ = component_; + } else { + result.component_ = stringListBuilder_.build(); + } + } + result.componentCase_ = componentCase_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto.getDefaultInstance()) return this; + switch (other.getComponentCase()) { + case CHANGESET: { + mergeChangeSet(other.getChangeSet()); + break; + } + case COMMANDLIST: { + mergeCommandList(other.getCommandList()); + break; + } + case CONFIG: { + mergeConfig(other.getConfig()); + break; + } + case ENTITY: { + mergeEntity(other.getEntity()); + break; + } + case ENTITYID: { + setEntityID(other.getEntityID()); + break; + } + case ENTITYIDLIST: { + mergeEntityIDList(other.getEntityIDList()); + break; + } + case ENTITYLIST: { + mergeEntityList(other.getEntityList()); + break; + } + case FLOATLIST: { + mergeFloatList(other.getFloatList()); + break; + } + case INTVALUE: { + setIntValue(other.getIntValue()); + break; + } + case INTLIST: { + mergeIntList(other.getIntList()); + break; + } + case RAWDATA: { + setRawData(other.getRawData()); + break; + } + case STRINGVALUE: { + componentCase_ = 12; + component_ = other.component_; + onChanged(); + break; + } + case STRINGLIST: { + mergeStringList(other.getStringList()); + break; + } + case COMPONENT_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int componentCase_ = 0; + private java.lang.Object component_; + public ComponentCase + getComponentCase() { + return ComponentCase.forNumber( + componentCase_); + } + + public Builder clearComponent() { + componentCase_ = 0; + component_ = null; + onChanged(); + return this; + } + + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> changeSetBuilder_; + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return Whether the changeSet field is set. + */ + @java.lang.Override + public boolean hasChangeSet() { + return componentCase_ == 1; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + * @return The changeSet. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getChangeSet() { + if (changeSetBuilder_ == null) { + if (componentCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } else { + if (componentCase_ == 1) { + return changeSetBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + public Builder setChangeSet(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (changeSetBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + changeSetBuilder_.setMessage(value); + } + componentCase_ = 1; + return this; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + public Builder setChangeSet( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder builderForValue) { + if (changeSetBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + changeSetBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 1; + return this; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + public Builder mergeChangeSet(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto value) { + if (changeSetBuilder_ == null) { + if (componentCase_ == 1 && + component_ != rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 1) { + changeSetBuilder_.mergeFrom(value); + } + changeSetBuilder_.setMessage(value); + } + componentCase_ = 1; + return this; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + public Builder clearChangeSet() { + if (changeSetBuilder_ == null) { + if (componentCase_ == 1) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 1) { + componentCase_ = 0; + component_ = null; + } + changeSetBuilder_.clear(); + } + return this; + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder getChangeSetBuilder() { + return getChangeSetFieldBuilder().getBuilder(); + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder getChangeSetOrBuilder() { + if ((componentCase_ == 1) && (changeSetBuilder_ != null)) { + return changeSetBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 1) { + return (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + } + /** + * <code>.ChangeSetProto changeSet = 1;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder> + getChangeSetFieldBuilder() { + if (changeSetBuilder_ == null) { + if (!(componentCase_ == 1)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + changeSetBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 1; + onChanged();; + return changeSetBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageListProto, rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder> commandListBuilder_; + /** + * <code>.MessageListProto commandList = 2;</code> + * @return Whether the commandList field is set. + */ + @java.lang.Override + public boolean hasCommandList() { + return componentCase_ == 2; + } + /** + * <code>.MessageListProto commandList = 2;</code> + * @return The commandList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto getCommandList() { + if (commandListBuilder_ == null) { + if (componentCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } else { + if (componentCase_ == 2) { + return commandListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + public Builder setCommandList(rescuecore2.messages.protobuf.RCRSProto.MessageListProto value) { + if (commandListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + commandListBuilder_.setMessage(value); + } + componentCase_ = 2; + return this; + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + public Builder setCommandList( + rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder builderForValue) { + if (commandListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + commandListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 2; + return this; + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + public Builder mergeCommandList(rescuecore2.messages.protobuf.RCRSProto.MessageListProto value) { + if (commandListBuilder_ == null) { + if (componentCase_ == 2 && + component_ != rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.MessageListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 2) { + commandListBuilder_.mergeFrom(value); + } + commandListBuilder_.setMessage(value); + } + componentCase_ = 2; + return this; + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + public Builder clearCommandList() { + if (commandListBuilder_ == null) { + if (componentCase_ == 2) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 2) { + componentCase_ = 0; + component_ = null; + } + commandListBuilder_.clear(); + } + return this; + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder getCommandListBuilder() { + return getCommandListFieldBuilder().getBuilder(); + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder getCommandListOrBuilder() { + if ((componentCase_ == 2) && (commandListBuilder_ != null)) { + return commandListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 2) { + return (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + } + /** + * <code>.MessageListProto commandList = 2;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageListProto, rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder> + getCommandListFieldBuilder() { + if (commandListBuilder_ == null) { + if (!(componentCase_ == 2)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.MessageListProto.getDefaultInstance(); + } + commandListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.MessageListProto, rescuecore2.messages.protobuf.RCRSProto.MessageListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.MessageListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.MessageListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 2; + onChanged();; + return commandListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder> configBuilder_; + /** + * <code>.ConfigProto config = 3;</code> + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return componentCase_ == 3; + } + /** + * <code>.ConfigProto config = 3;</code> + * @return The config. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getConfig() { + if (configBuilder_ == null) { + if (componentCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } else { + if (componentCase_ == 3) { + return configBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + } + /** + * <code>.ConfigProto config = 3;</code> + */ + public Builder setConfig(rescuecore2.messages.protobuf.RCRSProto.ConfigProto value) { + if (configBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + configBuilder_.setMessage(value); + } + componentCase_ = 3; + return this; + } + /** + * <code>.ConfigProto config = 3;</code> + */ + public Builder setConfig( + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder builderForValue) { + if (configBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + configBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 3; + return this; + } + /** + * <code>.ConfigProto config = 3;</code> + */ + public Builder mergeConfig(rescuecore2.messages.protobuf.RCRSProto.ConfigProto value) { + if (configBuilder_ == null) { + if (componentCase_ == 3 && + component_ != rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.ConfigProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 3) { + configBuilder_.mergeFrom(value); + } + configBuilder_.setMessage(value); + } + componentCase_ = 3; + return this; + } + /** + * <code>.ConfigProto config = 3;</code> + */ + public Builder clearConfig() { + if (configBuilder_ == null) { + if (componentCase_ == 3) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 3) { + componentCase_ = 0; + component_ = null; + } + configBuilder_.clear(); + } + return this; + } + /** + * <code>.ConfigProto config = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder getConfigBuilder() { + return getConfigFieldBuilder().getBuilder(); + } + /** + * <code>.ConfigProto config = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder getConfigOrBuilder() { + if ((componentCase_ == 3) && (configBuilder_ != null)) { + return configBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 3) { + return (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + } + /** + * <code>.ConfigProto config = 3;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder> + getConfigFieldBuilder() { + if (configBuilder_ == null) { + if (!(componentCase_ == 3)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + configBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ConfigProto, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 3; + onChanged();; + return configBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> entityBuilder_; + /** + * <code>.EntityProto entity = 4;</code> + * @return Whether the entity field is set. + */ + @java.lang.Override + public boolean hasEntity() { + return componentCase_ == 4; + } + /** + * <code>.EntityProto entity = 4;</code> + * @return The entity. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntity() { + if (entityBuilder_ == null) { + if (componentCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } else { + if (componentCase_ == 4) { + return entityBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + } + /** + * <code>.EntityProto entity = 4;</code> + */ + public Builder setEntity(rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + entityBuilder_.setMessage(value); + } + componentCase_ = 4; + return this; + } + /** + * <code>.EntityProto entity = 4;</code> + */ + public Builder setEntity( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entityBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + entityBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 4; + return this; + } + /** + * <code>.EntityProto entity = 4;</code> + */ + public Builder mergeEntity(rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entityBuilder_ == null) { + if (componentCase_ == 4 && + component_ != rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.EntityProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 4) { + entityBuilder_.mergeFrom(value); + } + entityBuilder_.setMessage(value); + } + componentCase_ = 4; + return this; + } + /** + * <code>.EntityProto entity = 4;</code> + */ + public Builder clearEntity() { + if (entityBuilder_ == null) { + if (componentCase_ == 4) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 4) { + componentCase_ = 0; + component_ = null; + } + entityBuilder_.clear(); + } + return this; + } + /** + * <code>.EntityProto entity = 4;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder getEntityBuilder() { + return getEntityFieldBuilder().getBuilder(); + } + /** + * <code>.EntityProto entity = 4;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntityOrBuilder() { + if ((componentCase_ == 4) && (entityBuilder_ != null)) { + return entityBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 4) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + } + /** + * <code>.EntityProto entity = 4;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntityFieldBuilder() { + if (entityBuilder_ == null) { + if (!(componentCase_ == 4)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + entityBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.EntityProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 4; + onChanged();; + return entityBuilder_; + } + + /** + * <code>int32 entityID = 5;</code> + * @return Whether the entityID field is set. + */ + public boolean hasEntityID() { + return componentCase_ == 5; + } + /** + * <code>int32 entityID = 5;</code> + * @return The entityID. + */ + public int getEntityID() { + if (componentCase_ == 5) { + return (java.lang.Integer) component_; + } + return 0; + } + /** + * <code>int32 entityID = 5;</code> + * @param value The entityID to set. + * @return This builder for chaining. + */ + public Builder setEntityID(int value) { + componentCase_ = 5; + component_ = value; + onChanged(); + return this; + } + /** + * <code>int32 entityID = 5;</code> + * @return This builder for chaining. + */ + public Builder clearEntityID() { + if (componentCase_ == 5) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + return this; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> entityIDListBuilder_; + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return Whether the entityIDList field is set. + */ + @java.lang.Override + public boolean hasEntityIDList() { + return componentCase_ == 6; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + * @return The entityIDList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getEntityIDList() { + if (entityIDListBuilder_ == null) { + if (componentCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } else { + if (componentCase_ == 6) { + return entityIDListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + public Builder setEntityIDList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (entityIDListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + entityIDListBuilder_.setMessage(value); + } + componentCase_ = 6; + return this; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + public Builder setEntityIDList( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (entityIDListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + entityIDListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 6; + return this; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + public Builder mergeEntityIDList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (entityIDListBuilder_ == null) { + if (componentCase_ == 6 && + component_ != rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 6) { + entityIDListBuilder_.mergeFrom(value); + } + entityIDListBuilder_.setMessage(value); + } + componentCase_ = 6; + return this; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + public Builder clearEntityIDList() { + if (entityIDListBuilder_ == null) { + if (componentCase_ == 6) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 6) { + componentCase_ = 0; + component_ = null; + } + entityIDListBuilder_.clear(); + } + return this; + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder getEntityIDListBuilder() { + return getEntityIDListFieldBuilder().getBuilder(); + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getEntityIDListOrBuilder() { + if ((componentCase_ == 6) && (entityIDListBuilder_ != null)) { + return entityIDListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto entityIDList = 6;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getEntityIDListFieldBuilder() { + if (entityIDListBuilder_ == null) { + if (!(componentCase_ == 6)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + entityIDListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 6; + onChanged();; + return entityIDListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityListProto, rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder> entityListBuilder_; + /** + * <code>.EntityListProto entityList = 7;</code> + * @return Whether the entityList field is set. + */ + @java.lang.Override + public boolean hasEntityList() { + return componentCase_ == 7; + } + /** + * <code>.EntityListProto entityList = 7;</code> + * @return The entityList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto getEntityList() { + if (entityListBuilder_ == null) { + if (componentCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } else { + if (componentCase_ == 7) { + return entityListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + public Builder setEntityList(rescuecore2.messages.protobuf.RCRSProto.EntityListProto value) { + if (entityListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + entityListBuilder_.setMessage(value); + } + componentCase_ = 7; + return this; + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + public Builder setEntityList( + rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder builderForValue) { + if (entityListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + entityListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 7; + return this; + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + public Builder mergeEntityList(rescuecore2.messages.protobuf.RCRSProto.EntityListProto value) { + if (entityListBuilder_ == null) { + if (componentCase_ == 7 && + component_ != rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.EntityListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 7) { + entityListBuilder_.mergeFrom(value); + } + entityListBuilder_.setMessage(value); + } + componentCase_ = 7; + return this; + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + public Builder clearEntityList() { + if (entityListBuilder_ == null) { + if (componentCase_ == 7) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 7) { + componentCase_ = 0; + component_ = null; + } + entityListBuilder_.clear(); + } + return this; + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder getEntityListBuilder() { + return getEntityListFieldBuilder().getBuilder(); + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder getEntityListOrBuilder() { + if ((componentCase_ == 7) && (entityListBuilder_ != null)) { + return entityListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + } + /** + * <code>.EntityListProto entityList = 7;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityListProto, rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder> + getEntityListFieldBuilder() { + if (entityListBuilder_ == null) { + if (!(componentCase_ == 7)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + entityListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityListProto, rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 7; + onChanged();; + return entityListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.FloatListProto, rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder> floatListBuilder_; + /** + * <code>.FloatListProto floatList = 8;</code> + * @return Whether the floatList field is set. + */ + @java.lang.Override + public boolean hasFloatList() { + return componentCase_ == 8; + } + /** + * <code>.FloatListProto floatList = 8;</code> + * @return The floatList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto getFloatList() { + if (floatListBuilder_ == null) { + if (componentCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } else { + if (componentCase_ == 8) { + return floatListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + public Builder setFloatList(rescuecore2.messages.protobuf.RCRSProto.FloatListProto value) { + if (floatListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + floatListBuilder_.setMessage(value); + } + componentCase_ = 8; + return this; + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + public Builder setFloatList( + rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder builderForValue) { + if (floatListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + floatListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 8; + return this; + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + public Builder mergeFloatList(rescuecore2.messages.protobuf.RCRSProto.FloatListProto value) { + if (floatListBuilder_ == null) { + if (componentCase_ == 8 && + component_ != rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.FloatListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 8) { + floatListBuilder_.mergeFrom(value); + } + floatListBuilder_.setMessage(value); + } + componentCase_ = 8; + return this; + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + public Builder clearFloatList() { + if (floatListBuilder_ == null) { + if (componentCase_ == 8) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 8) { + componentCase_ = 0; + component_ = null; + } + floatListBuilder_.clear(); + } + return this; + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder getFloatListBuilder() { + return getFloatListFieldBuilder().getBuilder(); + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder getFloatListOrBuilder() { + if ((componentCase_ == 8) && (floatListBuilder_ != null)) { + return floatListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + } + /** + * <code>.FloatListProto floatList = 8;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.FloatListProto, rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder> + getFloatListFieldBuilder() { + if (floatListBuilder_ == null) { + if (!(componentCase_ == 8)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + floatListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.FloatListProto, rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 8; + onChanged();; + return floatListBuilder_; + } + + /** + * <code>int32 intValue = 9;</code> + * @return Whether the intValue field is set. + */ + public boolean hasIntValue() { + return componentCase_ == 9; + } + /** + * <code>int32 intValue = 9;</code> + * @return The intValue. + */ + public int getIntValue() { + if (componentCase_ == 9) { + return (java.lang.Integer) component_; + } + return 0; + } + /** + * <code>int32 intValue = 9;</code> + * @param value The intValue to set. + * @return This builder for chaining. + */ + public Builder setIntValue(int value) { + componentCase_ = 9; + component_ = value; + onChanged(); + return this; + } + /** + * <code>int32 intValue = 9;</code> + * @return This builder for chaining. + */ + public Builder clearIntValue() { + if (componentCase_ == 9) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + return this; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> intListBuilder_; + /** + * <code>.IntListProto intList = 10;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return componentCase_ == 10; + } + /** + * <code>.IntListProto intList = 10;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (intListBuilder_ == null) { + if (componentCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } else { + if (componentCase_ == 10) { + return intListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 10;</code> + */ + public Builder setIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + intListBuilder_.setMessage(value); + } + componentCase_ = 10; + return this; + } + /** + * <code>.IntListProto intList = 10;</code> + */ + public Builder setIntList( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (intListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + intListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 10; + return this; + } + /** + * <code>.IntListProto intList = 10;</code> + */ + public Builder mergeIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (componentCase_ == 10 && + component_ != rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 10) { + intListBuilder_.mergeFrom(value); + } + intListBuilder_.setMessage(value); + } + componentCase_ = 10; + return this; + } + /** + * <code>.IntListProto intList = 10;</code> + */ + public Builder clearIntList() { + if (intListBuilder_ == null) { + if (componentCase_ == 10) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 10) { + componentCase_ = 0; + component_ = null; + } + intListBuilder_.clear(); + } + return this; + } + /** + * <code>.IntListProto intList = 10;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder getIntListBuilder() { + return getIntListFieldBuilder().getBuilder(); + } + /** + * <code>.IntListProto intList = 10;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if ((componentCase_ == 10) && (intListBuilder_ != null)) { + return intListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 10;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getIntListFieldBuilder() { + if (intListBuilder_ == null) { + if (!(componentCase_ == 10)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + intListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 10; + onChanged();; + return intListBuilder_; + } + + /** + * <code>bytes rawData = 11;</code> + * @return Whether the rawData field is set. + */ + public boolean hasRawData() { + return componentCase_ == 11; + } + /** + * <code>bytes rawData = 11;</code> + * @return The rawData. + */ + public com.google.protobuf.ByteString getRawData() { + if (componentCase_ == 11) { + return (com.google.protobuf.ByteString) component_; + } + return com.google.protobuf.ByteString.EMPTY; + } + /** + * <code>bytes rawData = 11;</code> + * @param value The rawData to set. + * @return This builder for chaining. + */ + public Builder setRawData(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + componentCase_ = 11; + component_ = value; + onChanged(); + return this; + } + /** + * <code>bytes rawData = 11;</code> + * @return This builder for chaining. + */ + public Builder clearRawData() { + if (componentCase_ == 11) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + return this; + } + + /** + * <code>string stringValue = 12;</code> + * @return Whether the stringValue field is set. + */ + @java.lang.Override + public boolean hasStringValue() { + return componentCase_ == 12; + } + /** + * <code>string stringValue = 12;</code> + * @return The stringValue. + */ + @java.lang.Override + public java.lang.String getStringValue() { + java.lang.Object ref = ""; + if (componentCase_ == 12) { + ref = component_; + } + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (componentCase_ == 12) { + component_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * <code>string stringValue = 12;</code> + * @return The bytes for stringValue. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getStringValueBytes() { + java.lang.Object ref = ""; + if (componentCase_ == 12) { + ref = component_; + } + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (componentCase_ == 12) { + component_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * <code>string stringValue = 12;</code> + * @param value The stringValue to set. + * @return This builder for chaining. + */ + public Builder setStringValue( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + componentCase_ = 12; + component_ = value; + onChanged(); + return this; + } + /** + * <code>string stringValue = 12;</code> + * @return This builder for chaining. + */ + public Builder clearStringValue() { + if (componentCase_ == 12) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + return this; + } + /** + * <code>string stringValue = 12;</code> + * @param value The bytes for stringValue to set. + * @return This builder for chaining. + */ + public Builder setStringValueBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + componentCase_ = 12; + component_ = value; + onChanged(); + return this; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.StrListProto, rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder> stringListBuilder_; + /** + * <code>.StrListProto stringList = 13;</code> + * @return Whether the stringList field is set. + */ + @java.lang.Override + public boolean hasStringList() { + return componentCase_ == 13; + } + /** + * <code>.StrListProto stringList = 13;</code> + * @return The stringList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto getStringList() { + if (stringListBuilder_ == null) { + if (componentCase_ == 13) { + return (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } else { + if (componentCase_ == 13) { + return stringListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + public Builder setStringList(rescuecore2.messages.protobuf.RCRSProto.StrListProto value) { + if (stringListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + component_ = value; + onChanged(); + } else { + stringListBuilder_.setMessage(value); + } + componentCase_ = 13; + return this; + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + public Builder setStringList( + rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder builderForValue) { + if (stringListBuilder_ == null) { + component_ = builderForValue.build(); + onChanged(); + } else { + stringListBuilder_.setMessage(builderForValue.build()); + } + componentCase_ = 13; + return this; + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + public Builder mergeStringList(rescuecore2.messages.protobuf.RCRSProto.StrListProto value) { + if (stringListBuilder_ == null) { + if (componentCase_ == 13 && + component_ != rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance()) { + component_ = rescuecore2.messages.protobuf.RCRSProto.StrListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_) + .mergeFrom(value).buildPartial(); + } else { + component_ = value; + } + onChanged(); + } else { + if (componentCase_ == 13) { + stringListBuilder_.mergeFrom(value); + } + stringListBuilder_.setMessage(value); + } + componentCase_ = 13; + return this; + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + public Builder clearStringList() { + if (stringListBuilder_ == null) { + if (componentCase_ == 13) { + componentCase_ = 0; + component_ = null; + onChanged(); + } + } else { + if (componentCase_ == 13) { + componentCase_ = 0; + component_ = null; + } + stringListBuilder_.clear(); + } + return this; + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder getStringListBuilder() { + return getStringListFieldBuilder().getBuilder(); + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder getStringListOrBuilder() { + if ((componentCase_ == 13) && (stringListBuilder_ != null)) { + return stringListBuilder_.getMessageOrBuilder(); + } else { + if (componentCase_ == 13) { + return (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_; + } + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + } + /** + * <code>.StrListProto stringList = 13;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.StrListProto, rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder> + getStringListFieldBuilder() { + if (stringListBuilder_ == null) { + if (!(componentCase_ == 13)) { + component_ = rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + stringListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.StrListProto, rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.StrListProto) component_, + getParentForChildren(), + isClean()); + component_ = null; + } + componentCase_ = 13; + onChanged();; + return stringListBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:MessageComponentProto) + } + + // @@protoc_insertion_point(class_scope:MessageComponentProto) + private static final rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<MessageComponentProto> + PARSER = new com.google.protobuf.AbstractParser<MessageComponentProto>() { + @java.lang.Override + public MessageComponentProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new MessageComponentProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<MessageComponentProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<MessageComponentProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.MessageComponentProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface StrListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:StrListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated string values = 1;</code> + * @return A list containing the values. + */ + java.util.List<java.lang.String> + getValuesList(); + /** + * <code>repeated string values = 1;</code> + * @return The count of values. + */ + int getValuesCount(); + /** + * <code>repeated string values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + java.lang.String getValues(int index); + /** + * <code>repeated string values = 1;</code> + * @param index The index of the value to return. + * @return The bytes of the values at the given index. + */ + com.google.protobuf.ByteString + getValuesBytes(int index); + } + /** + * Protobuf type {@code StrListProto} + */ + public static final class StrListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:StrListProto) + StrListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use StrListProto.newBuilder() to construct. + private StrListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private StrListProto() { + values_ = com.google.protobuf.LazyStringArrayList.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new StrListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private StrListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = new com.google.protobuf.LazyStringArrayList(); + mutable_bitField0_ |= 0x00000001; + } + values_.add(s); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = values_.getUnmodifiableView(); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_StrListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_StrListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.StrListProto.class, rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder.class); + } + + public static final int VALUES_FIELD_NUMBER = 1; + private com.google.protobuf.LazyStringList values_; + /** + * <code>repeated string values = 1;</code> + * @return A list containing the values. + */ + public com.google.protobuf.ProtocolStringList + getValuesList() { + return values_; + } + /** + * <code>repeated string values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated string values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public java.lang.String getValues(int index) { + return values_.get(index); + } + /** + * <code>repeated string values = 1;</code> + * @param index The index of the value to return. + * @return The bytes of the values at the given index. + */ + public com.google.protobuf.ByteString + getValuesBytes(int index) { + return values_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < values_.size(); i++) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, values_.getRaw(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < values_.size(); i++) { + dataSize += computeStringSizeNoTag(values_.getRaw(i)); + } + size += dataSize; + size += 1 * getValuesList().size(); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.StrListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.StrListProto other = (rescuecore2.messages.protobuf.RCRSProto.StrListProto) obj; + + if (!getValuesList() + .equals(other.getValuesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getValuesCount() > 0) { + hash = (37 * hash) + VALUES_FIELD_NUMBER; + hash = (53 * hash) + getValuesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.StrListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code StrListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:StrListProto) + rescuecore2.messages.protobuf.RCRSProto.StrListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_StrListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_StrListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.StrListProto.class, rescuecore2.messages.protobuf.RCRSProto.StrListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.StrListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + values_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_StrListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto build() { + rescuecore2.messages.protobuf.RCRSProto.StrListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.StrListProto result = new rescuecore2.messages.protobuf.RCRSProto.StrListProto(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) != 0)) { + values_ = values_.getUnmodifiableView(); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.values_ = values_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.StrListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.StrListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.StrListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.StrListProto.getDefaultInstance()) return this; + if (!other.values_.isEmpty()) { + if (values_.isEmpty()) { + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureValuesIsMutable(); + values_.addAll(other.values_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.StrListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.StrListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private com.google.protobuf.LazyStringList values_ = com.google.protobuf.LazyStringArrayList.EMPTY; + private void ensureValuesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + values_ = new com.google.protobuf.LazyStringArrayList(values_); + bitField0_ |= 0x00000001; + } + } + /** + * <code>repeated string values = 1;</code> + * @return A list containing the values. + */ + public com.google.protobuf.ProtocolStringList + getValuesList() { + return values_.getUnmodifiableView(); + } + /** + * <code>repeated string values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated string values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public java.lang.String getValues(int index) { + return values_.get(index); + } + /** + * <code>repeated string values = 1;</code> + * @param index The index of the value to return. + * @return The bytes of the values at the given index. + */ + public com.google.protobuf.ByteString + getValuesBytes(int index) { + return values_.getByteString(index); + } + /** + * <code>repeated string values = 1;</code> + * @param index The index to set the value at. + * @param value The values to set. + * @return This builder for chaining. + */ + public Builder setValues( + int index, java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.set(index, value); + onChanged(); + return this; + } + /** + * <code>repeated string values = 1;</code> + * @param value The values to add. + * @return This builder for chaining. + */ + public Builder addValues( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.add(value); + onChanged(); + return this; + } + /** + * <code>repeated string values = 1;</code> + * @param values The values to add. + * @return This builder for chaining. + */ + public Builder addAllValues( + java.lang.Iterable<java.lang.String> values) { + ensureValuesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, values_); + onChanged(); + return this; + } + /** + * <code>repeated string values = 1;</code> + * @return This builder for chaining. + */ + public Builder clearValues() { + values_ = com.google.protobuf.LazyStringArrayList.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * <code>repeated string values = 1;</code> + * @param value The bytes of the values to add. + * @return This builder for chaining. + */ + public Builder addValuesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + ensureValuesIsMutable(); + values_.add(value); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:StrListProto) + } + + // @@protoc_insertion_point(class_scope:StrListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.StrListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.StrListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.StrListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<StrListProto> + PARSER = new com.google.protobuf.AbstractParser<StrListProto>() { + @java.lang.Override + public StrListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StrListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<StrListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<StrListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.StrListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface IntListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:IntListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated int32 values = 1;</code> + * @return A list containing the values. + */ + java.util.List<java.lang.Integer> getValuesList(); + /** + * <code>repeated int32 values = 1;</code> + * @return The count of values. + */ + int getValuesCount(); + /** + * <code>repeated int32 values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + int getValues(int index); + } + /** + * Protobuf type {@code IntListProto} + */ + public static final class IntListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:IntListProto) + IntListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use IntListProto.newBuilder() to construct. + private IntListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private IntListProto() { + values_ = emptyIntList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new IntListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IntListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = newIntList(); + mutable_bitField0_ |= 0x00000001; + } + values_.addInt(input.readInt32()); + break; + } + case 10: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000001) != 0) && input.getBytesUntilLimit() > 0) { + values_ = newIntList(); + mutable_bitField0_ |= 0x00000001; + } + while (input.getBytesUntilLimit() > 0) { + values_.addInt(input.readInt32()); + } + input.popLimit(limit); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + values_.makeImmutable(); // C + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.class, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder.class); + } + + public static final int VALUES_FIELD_NUMBER = 1; + private com.google.protobuf.Internal.IntList values_; + /** + * <code>repeated int32 values = 1;</code> + * @return A list containing the values. + */ + @java.lang.Override + public java.util.List<java.lang.Integer> + getValuesList() { + return values_; + } + /** + * <code>repeated int32 values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated int32 values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public int getValues(int index) { + return values_.getInt(index); + } + private int valuesMemoizedSerializedSize = -1; + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (getValuesList().size() > 0) { + output.writeUInt32NoTag(10); + output.writeUInt32NoTag(valuesMemoizedSerializedSize); + } + for (int i = 0; i < values_.size(); i++) { + output.writeInt32NoTag(values_.getInt(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < values_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(values_.getInt(i)); + } + size += dataSize; + if (!getValuesList().isEmpty()) { + size += 1; + size += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(dataSize); + } + valuesMemoizedSerializedSize = dataSize; + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.IntListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.IntListProto other = (rescuecore2.messages.protobuf.RCRSProto.IntListProto) obj; + + if (!getValuesList() + .equals(other.getValuesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getValuesCount() > 0) { + hash = (37 * hash) + VALUES_FIELD_NUMBER; + hash = (53 * hash) + getValuesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.IntListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code IntListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:IntListProto) + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.class, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.IntListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + values_ = emptyIntList(); + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto build() { + rescuecore2.messages.protobuf.RCRSProto.IntListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.IntListProto result = new rescuecore2.messages.protobuf.RCRSProto.IntListProto(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) != 0)) { + values_.makeImmutable(); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.values_ = values_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.IntListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.IntListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()) return this; + if (!other.values_.isEmpty()) { + if (values_.isEmpty()) { + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureValuesIsMutable(); + values_.addAll(other.values_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.IntListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.IntListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private com.google.protobuf.Internal.IntList values_ = emptyIntList(); + private void ensureValuesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + values_ = mutableCopy(values_); + bitField0_ |= 0x00000001; + } + } + /** + * <code>repeated int32 values = 1;</code> + * @return A list containing the values. + */ + public java.util.List<java.lang.Integer> + getValuesList() { + return ((bitField0_ & 0x00000001) != 0) ? + java.util.Collections.unmodifiableList(values_) : values_; + } + /** + * <code>repeated int32 values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated int32 values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public int getValues(int index) { + return values_.getInt(index); + } + /** + * <code>repeated int32 values = 1;</code> + * @param index The index to set the value at. + * @param value The values to set. + * @return This builder for chaining. + */ + public Builder setValues( + int index, int value) { + ensureValuesIsMutable(); + values_.setInt(index, value); + onChanged(); + return this; + } + /** + * <code>repeated int32 values = 1;</code> + * @param value The values to add. + * @return This builder for chaining. + */ + public Builder addValues(int value) { + ensureValuesIsMutable(); + values_.addInt(value); + onChanged(); + return this; + } + /** + * <code>repeated int32 values = 1;</code> + * @param values The values to add. + * @return This builder for chaining. + */ + public Builder addAllValues( + java.lang.Iterable<? extends java.lang.Integer> values) { + ensureValuesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, values_); + onChanged(); + return this; + } + /** + * <code>repeated int32 values = 1;</code> + * @return This builder for chaining. + */ + public Builder clearValues() { + values_ = emptyIntList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:IntListProto) + } + + // @@protoc_insertion_point(class_scope:IntListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.IntListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.IntListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.IntListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<IntListProto> + PARSER = new com.google.protobuf.AbstractParser<IntListProto>() { + @java.lang.Override + public IntListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IntListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<IntListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<IntListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface FloatListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:FloatListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated float values = 1;</code> + * @return A list containing the values. + */ + java.util.List<java.lang.Float> getValuesList(); + /** + * <code>repeated float values = 1;</code> + * @return The count of values. + */ + int getValuesCount(); + /** + * <code>repeated float values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + float getValues(int index); + } + /** + * Protobuf type {@code FloatListProto} + */ + public static final class FloatListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:FloatListProto) + FloatListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use FloatListProto.newBuilder() to construct. + private FloatListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private FloatListProto() { + values_ = emptyFloatList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new FloatListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private FloatListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 13: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = newFloatList(); + mutable_bitField0_ |= 0x00000001; + } + values_.addFloat(input.readFloat()); + break; + } + case 10: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000001) != 0) && input.getBytesUntilLimit() > 0) { + values_ = newFloatList(); + mutable_bitField0_ |= 0x00000001; + } + while (input.getBytesUntilLimit() > 0) { + values_.addFloat(input.readFloat()); + } + input.popLimit(limit); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + values_.makeImmutable(); // C + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_FloatListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_FloatListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.FloatListProto.class, rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder.class); + } + + public static final int VALUES_FIELD_NUMBER = 1; + private com.google.protobuf.Internal.FloatList values_; + /** + * <code>repeated float values = 1;</code> + * @return A list containing the values. + */ + @java.lang.Override + public java.util.List<java.lang.Float> + getValuesList() { + return values_; + } + /** + * <code>repeated float values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated float values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public float getValues(int index) { + return values_.getFloat(index); + } + private int valuesMemoizedSerializedSize = -1; + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (getValuesList().size() > 0) { + output.writeUInt32NoTag(10); + output.writeUInt32NoTag(valuesMemoizedSerializedSize); + } + for (int i = 0; i < values_.size(); i++) { + output.writeFloatNoTag(values_.getFloat(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + dataSize = 4 * getValuesList().size(); + size += dataSize; + if (!getValuesList().isEmpty()) { + size += 1; + size += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(dataSize); + } + valuesMemoizedSerializedSize = dataSize; + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.FloatListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.FloatListProto other = (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) obj; + + if (!getValuesList() + .equals(other.getValuesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getValuesCount() > 0) { + hash = (37 * hash) + VALUES_FIELD_NUMBER; + hash = (53 * hash) + getValuesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.FloatListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code FloatListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:FloatListProto) + rescuecore2.messages.protobuf.RCRSProto.FloatListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_FloatListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_FloatListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.FloatListProto.class, rescuecore2.messages.protobuf.RCRSProto.FloatListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.FloatListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + values_ = emptyFloatList(); + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_FloatListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto build() { + rescuecore2.messages.protobuf.RCRSProto.FloatListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.FloatListProto result = new rescuecore2.messages.protobuf.RCRSProto.FloatListProto(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) != 0)) { + values_.makeImmutable(); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.values_ = values_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.FloatListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.FloatListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.FloatListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.FloatListProto.getDefaultInstance()) return this; + if (!other.values_.isEmpty()) { + if (values_.isEmpty()) { + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureValuesIsMutable(); + values_.addAll(other.values_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.FloatListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.FloatListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private com.google.protobuf.Internal.FloatList values_ = emptyFloatList(); + private void ensureValuesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + values_ = mutableCopy(values_); + bitField0_ |= 0x00000001; + } + } + /** + * <code>repeated float values = 1;</code> + * @return A list containing the values. + */ + public java.util.List<java.lang.Float> + getValuesList() { + return ((bitField0_ & 0x00000001) != 0) ? + java.util.Collections.unmodifiableList(values_) : values_; + } + /** + * <code>repeated float values = 1;</code> + * @return The count of values. + */ + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated float values = 1;</code> + * @param index The index of the element to return. + * @return The values at the given index. + */ + public float getValues(int index) { + return values_.getFloat(index); + } + /** + * <code>repeated float values = 1;</code> + * @param index The index to set the value at. + * @param value The values to set. + * @return This builder for chaining. + */ + public Builder setValues( + int index, float value) { + ensureValuesIsMutable(); + values_.setFloat(index, value); + onChanged(); + return this; + } + /** + * <code>repeated float values = 1;</code> + * @param value The values to add. + * @return This builder for chaining. + */ + public Builder addValues(float value) { + ensureValuesIsMutable(); + values_.addFloat(value); + onChanged(); + return this; + } + /** + * <code>repeated float values = 1;</code> + * @param values The values to add. + * @return This builder for chaining. + */ + public Builder addAllValues( + java.lang.Iterable<? extends java.lang.Float> values) { + ensureValuesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, values_); + onChanged(); + return this; + } + /** + * <code>repeated float values = 1;</code> + * @return This builder for chaining. + */ + public Builder clearValues() { + values_ = emptyFloatList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:FloatListProto) + } + + // @@protoc_insertion_point(class_scope:FloatListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.FloatListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.FloatListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.FloatListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<FloatListProto> + PARSER = new com.google.protobuf.AbstractParser<FloatListProto>() { + @java.lang.Override + public FloatListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new FloatListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<FloatListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<FloatListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.FloatListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface IntMatrixProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:IntMatrixProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .IntListProto values = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto> + getValuesList(); + /** + * <code>repeated .IntListProto values = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProto getValues(int index); + /** + * <code>repeated .IntListProto values = 1;</code> + */ + int getValuesCount(); + /** + * <code>repeated .IntListProto values = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getValuesOrBuilderList(); + /** + * <code>repeated .IntListProto values = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getValuesOrBuilder( + int index); + } + /** + * Protobuf type {@code IntMatrixProto} + */ + public static final class IntMatrixProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:IntMatrixProto) + IntMatrixProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use IntMatrixProto.newBuilder() to construct. + private IntMatrixProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private IntMatrixProto() { + values_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new IntMatrixProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private IntMatrixProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.IntListProto>(); + mutable_bitField0_ |= 0x00000001; + } + values_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntListProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + values_ = java.util.Collections.unmodifiableList(values_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntMatrixProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntMatrixProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.class, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder.class); + } + + public static final int VALUES_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto> values_; + /** + * <code>repeated .IntListProto values = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto> getValuesList() { + return values_; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getValuesOrBuilderList() { + return values_; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + @java.lang.Override + public int getValuesCount() { + return values_.size(); + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getValues(int index) { + return values_.get(index); + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getValuesOrBuilder( + int index) { + return values_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < values_.size(); i++) { + output.writeMessage(1, values_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < values_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, values_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto other = (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) obj; + + if (!getValuesList() + .equals(other.getValuesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getValuesCount() > 0) { + hash = (37 * hash) + VALUES_FIELD_NUMBER; + hash = (53 * hash) + getValuesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code IntMatrixProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:IntMatrixProto) + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntMatrixProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntMatrixProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.class, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getValuesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (valuesBuilder_ == null) { + values_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + valuesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_IntMatrixProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto build() { + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto result = new rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto(this); + int from_bitField0_ = bitField0_; + if (valuesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + values_ = java.util.Collections.unmodifiableList(values_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.values_ = values_; + } else { + result.values_ = valuesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance()) return this; + if (valuesBuilder_ == null) { + if (!other.values_.isEmpty()) { + if (values_.isEmpty()) { + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureValuesIsMutable(); + values_.addAll(other.values_); + } + onChanged(); + } + } else { + if (!other.values_.isEmpty()) { + if (valuesBuilder_.isEmpty()) { + valuesBuilder_.dispose(); + valuesBuilder_ = null; + values_ = other.values_; + bitField0_ = (bitField0_ & ~0x00000001); + valuesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getValuesFieldBuilder() : null; + } else { + valuesBuilder_.addAllMessages(other.values_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto> values_ = + java.util.Collections.emptyList(); + private void ensureValuesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + values_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.IntListProto>(values_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> valuesBuilder_; + + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto> getValuesList() { + if (valuesBuilder_ == null) { + return java.util.Collections.unmodifiableList(values_); + } else { + return valuesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public int getValuesCount() { + if (valuesBuilder_ == null) { + return values_.size(); + } else { + return valuesBuilder_.getCount(); + } + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getValues(int index) { + if (valuesBuilder_ == null) { + return values_.get(index); + } else { + return valuesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder setValues( + int index, rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.set(index, value); + onChanged(); + } else { + valuesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder setValues( + int index, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.set(index, builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder addValues(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.add(value); + onChanged(); + } else { + valuesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder addValues( + int index, rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (valuesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureValuesIsMutable(); + values_.add(index, value); + onChanged(); + } else { + valuesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder addValues( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.add(builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder addValues( + int index, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.add(index, builderForValue.build()); + onChanged(); + } else { + valuesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder addAllValues( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.IntListProto> values) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, values_); + onChanged(); + } else { + valuesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder clearValues() { + if (valuesBuilder_ == null) { + values_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + valuesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public Builder removeValues(int index) { + if (valuesBuilder_ == null) { + ensureValuesIsMutable(); + values_.remove(index); + onChanged(); + } else { + valuesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder getValuesBuilder( + int index) { + return getValuesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getValuesOrBuilder( + int index) { + if (valuesBuilder_ == null) { + return values_.get(index); } else { + return valuesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getValuesOrBuilderList() { + if (valuesBuilder_ != null) { + return valuesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(values_); + } + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder addValuesBuilder() { + return getValuesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()); + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder addValuesBuilder( + int index) { + return getValuesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()); + } + /** + * <code>repeated .IntListProto values = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder> + getValuesBuilderList() { + return getValuesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getValuesFieldBuilder() { + if (valuesBuilder_ == null) { + valuesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder>( + values_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + values_ = null; + } + return valuesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:IntMatrixProto) + } + + // @@protoc_insertion_point(class_scope:IntMatrixProto) + private static final rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<IntMatrixProto> + PARSER = new com.google.protobuf.AbstractParser<IntMatrixProto>() { + @java.lang.Override + public IntMatrixProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new IntMatrixProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<IntMatrixProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<IntMatrixProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface ValueProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:ValueProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>bool defined = 1;</code> + * @return The defined. + */ + boolean getDefined(); + + /** + * <code>int32 intValue = 2;</code> + * @return Whether the intValue field is set. + */ + boolean hasIntValue(); + /** + * <code>int32 intValue = 2;</code> + * @return The intValue. + */ + int getIntValue(); + + /** + * <code>bool boolValue = 3;</code> + * @return Whether the boolValue field is set. + */ + boolean hasBoolValue(); + /** + * <code>bool boolValue = 3;</code> + * @return The boolValue. + */ + boolean getBoolValue(); + + /** + * <code>double doubleValue = 4;</code> + * @return Whether the doubleValue field is set. + */ + boolean hasDoubleValue(); + /** + * <code>double doubleValue = 4;</code> + * @return The doubleValue. + */ + double getDoubleValue(); + + /** + * <code>bytes byteList = 5;</code> + * @return Whether the byteList field is set. + */ + boolean hasByteList(); + /** + * <code>bytes byteList = 5;</code> + * @return The byteList. + */ + com.google.protobuf.ByteString getByteList(); + + /** + * <code>.IntListProto intList = 6;</code> + * @return Whether the intList field is set. + */ + boolean hasIntList(); + /** + * <code>.IntListProto intList = 6;</code> + * @return The intList. + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList(); + /** + * <code>.IntListProto intList = 6;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder(); + + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return Whether the intMatrix field is set. + */ + boolean hasIntMatrix(); + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return The intMatrix. + */ + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix(); + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder(); + + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return Whether the edgeList field is set. + */ + boolean hasEdgeList(); + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return The edgeList. + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList(); + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder(); + + /** + * <code>.Point2DProto point2D = 9;</code> + * @return Whether the point2D field is set. + */ + boolean hasPoint2D(); + /** + * <code>.Point2DProto point2D = 9;</code> + * @return The point2D. + */ + rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D(); + /** + * <code>.Point2DProto point2D = 9;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder(); + + public rescuecore2.messages.protobuf.RCRSProto.ValueProto.ValueCase getValueCase(); + } + /** + * Protobuf type {@code ValueProto} + */ + public static final class ValueProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:ValueProto) + ValueProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use ValueProto.newBuilder() to construct. + private ValueProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private ValueProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ValueProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ValueProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + defined_ = input.readBool(); + break; + } + case 16: { + valueCase_ = 2; + value_ = input.readInt32(); + break; + } + case 24: { + valueCase_ = 3; + value_ = input.readBool(); + break; + } + case 33: { + valueCase_ = 4; + value_ = input.readDouble(); + break; + } + case 42: { + valueCase_ = 5; + value_ = input.readBytes(); + break; + } + case 50: { + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder subBuilder = null; + if (valueCase_ == 6) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 6; + break; + } + case 58: { + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder subBuilder = null; + if (valueCase_ == 7) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 7; + break; + } + case 66: { + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder subBuilder = null; + if (valueCase_ == 8) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 8; + break; + } + case 74: { + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder subBuilder = null; + if (valueCase_ == 9) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.Point2DProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 9; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ValueProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ValueProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ValueProto.class, rescuecore2.messages.protobuf.RCRSProto.ValueProto.Builder.class); + } + + private int valueCase_ = 0; + private java.lang.Object value_; + public enum ValueCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + INTVALUE(2), + BOOLVALUE(3), + DOUBLEVALUE(4), + BYTELIST(5), + INTLIST(6), + INTMATRIX(7), + EDGELIST(8), + POINT2D(9), + VALUE_NOT_SET(0); + private final int value; + private ValueCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static ValueCase valueOf(int value) { + return forNumber(value); + } + + public static ValueCase forNumber(int value) { + switch (value) { + case 2: return INTVALUE; + case 3: return BOOLVALUE; + case 4: return DOUBLEVALUE; + case 5: return BYTELIST; + case 6: return INTLIST; + case 7: return INTMATRIX; + case 8: return EDGELIST; + case 9: return POINT2D; + case 0: return VALUE_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public ValueCase + getValueCase() { + return ValueCase.forNumber( + valueCase_); + } + + public static final int DEFINED_FIELD_NUMBER = 1; + private boolean defined_; + /** + * <code>bool defined = 1;</code> + * @return The defined. + */ + @java.lang.Override + public boolean getDefined() { + return defined_; + } + + public static final int INTVALUE_FIELD_NUMBER = 2; + /** + * <code>int32 intValue = 2;</code> + * @return Whether the intValue field is set. + */ + @java.lang.Override + public boolean hasIntValue() { + return valueCase_ == 2; + } + /** + * <code>int32 intValue = 2;</code> + * @return The intValue. + */ + @java.lang.Override + public int getIntValue() { + if (valueCase_ == 2) { + return (java.lang.Integer) value_; + } + return 0; + } + + public static final int BOOLVALUE_FIELD_NUMBER = 3; + /** + * <code>bool boolValue = 3;</code> + * @return Whether the boolValue field is set. + */ + @java.lang.Override + public boolean hasBoolValue() { + return valueCase_ == 3; + } + /** + * <code>bool boolValue = 3;</code> + * @return The boolValue. + */ + @java.lang.Override + public boolean getBoolValue() { + if (valueCase_ == 3) { + return (java.lang.Boolean) value_; + } + return false; + } + + public static final int DOUBLEVALUE_FIELD_NUMBER = 4; + /** + * <code>double doubleValue = 4;</code> + * @return Whether the doubleValue field is set. + */ + @java.lang.Override + public boolean hasDoubleValue() { + return valueCase_ == 4; + } + /** + * <code>double doubleValue = 4;</code> + * @return The doubleValue. + */ + @java.lang.Override + public double getDoubleValue() { + if (valueCase_ == 4) { + return (java.lang.Double) value_; + } + return 0D; + } + + public static final int BYTELIST_FIELD_NUMBER = 5; + /** + * <code>bytes byteList = 5;</code> + * @return Whether the byteList field is set. + */ + @java.lang.Override + public boolean hasByteList() { + return valueCase_ == 5; + } + /** + * <code>bytes byteList = 5;</code> + * @return The byteList. + */ + @java.lang.Override + public com.google.protobuf.ByteString getByteList() { + if (valueCase_ == 5) { + return (com.google.protobuf.ByteString) value_; + } + return com.google.protobuf.ByteString.EMPTY; + } + + public static final int INTLIST_FIELD_NUMBER = 6; + /** + * <code>.IntListProto intList = 6;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return valueCase_ == 6; + } + /** + * <code>.IntListProto intList = 6;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (valueCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + /** + * <code>.IntListProto intList = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if (valueCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + + public static final int INTMATRIX_FIELD_NUMBER = 7; + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return Whether the intMatrix field is set. + */ + @java.lang.Override + public boolean hasIntMatrix() { + return valueCase_ == 7; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return The intMatrix. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix() { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder() { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + + public static final int EDGELIST_FIELD_NUMBER = 8; + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return Whether the edgeList field is set. + */ + @java.lang.Override + public boolean hasEdgeList() { + return valueCase_ == 8; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return The edgeList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList() { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder() { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + + public static final int POINT2D_FIELD_NUMBER = 9; + /** + * <code>.Point2DProto point2D = 9;</code> + * @return Whether the point2D field is set. + */ + @java.lang.Override + public boolean hasPoint2D() { + return valueCase_ == 9; + } + /** + * <code>.Point2DProto point2D = 9;</code> + * @return The point2D. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D() { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder() { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (defined_ != false) { + output.writeBool(1, defined_); + } + if (valueCase_ == 2) { + output.writeInt32( + 2, (int)((java.lang.Integer) value_)); + } + if (valueCase_ == 3) { + output.writeBool( + 3, (boolean)((java.lang.Boolean) value_)); + } + if (valueCase_ == 4) { + output.writeDouble( + 4, (double)((java.lang.Double) value_)); + } + if (valueCase_ == 5) { + output.writeBytes( + 5, (com.google.protobuf.ByteString) value_); + } + if (valueCase_ == 6) { + output.writeMessage(6, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + } + if (valueCase_ == 7) { + output.writeMessage(7, (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + } + if (valueCase_ == 8) { + output.writeMessage(8, (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + } + if (valueCase_ == 9) { + output.writeMessage(9, (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (defined_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(1, defined_); + } + if (valueCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size( + 2, (int)((java.lang.Integer) value_)); + } + if (valueCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize( + 3, (boolean)((java.lang.Boolean) value_)); + } + if (valueCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize( + 4, (double)((java.lang.Double) value_)); + } + if (valueCase_ == 5) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize( + 5, (com.google.protobuf.ByteString) value_); + } + if (valueCase_ == 6) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + } + if (valueCase_ == 7) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + } + if (valueCase_ == 8) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + } + if (valueCase_ == 9) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.ValueProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.ValueProto other = (rescuecore2.messages.protobuf.RCRSProto.ValueProto) obj; + + if (getDefined() + != other.getDefined()) return false; + if (!getValueCase().equals(other.getValueCase())) return false; + switch (valueCase_) { + case 2: + if (getIntValue() + != other.getIntValue()) return false; + break; + case 3: + if (getBoolValue() + != other.getBoolValue()) return false; + break; + case 4: + if (java.lang.Double.doubleToLongBits(getDoubleValue()) + != java.lang.Double.doubleToLongBits( + other.getDoubleValue())) return false; + break; + case 5: + if (!getByteList() + .equals(other.getByteList())) return false; + break; + case 6: + if (!getIntList() + .equals(other.getIntList())) return false; + break; + case 7: + if (!getIntMatrix() + .equals(other.getIntMatrix())) return false; + break; + case 8: + if (!getEdgeList() + .equals(other.getEdgeList())) return false; + break; + case 9: + if (!getPoint2D() + .equals(other.getPoint2D())) return false; + break; + case 0: + default: + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DEFINED_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getDefined()); + switch (valueCase_) { + case 2: + hash = (37 * hash) + INTVALUE_FIELD_NUMBER; + hash = (53 * hash) + getIntValue(); + break; + case 3: + hash = (37 * hash) + BOOLVALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getBoolValue()); + break; + case 4: + hash = (37 * hash) + DOUBLEVALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getDoubleValue())); + break; + case 5: + hash = (37 * hash) + BYTELIST_FIELD_NUMBER; + hash = (53 * hash) + getByteList().hashCode(); + break; + case 6: + hash = (37 * hash) + INTLIST_FIELD_NUMBER; + hash = (53 * hash) + getIntList().hashCode(); + break; + case 7: + hash = (37 * hash) + INTMATRIX_FIELD_NUMBER; + hash = (53 * hash) + getIntMatrix().hashCode(); + break; + case 8: + hash = (37 * hash) + EDGELIST_FIELD_NUMBER; + hash = (53 * hash) + getEdgeList().hashCode(); + break; + case 9: + hash = (37 * hash) + POINT2D_FIELD_NUMBER; + hash = (53 * hash) + getPoint2D().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.ValueProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ValueProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:ValueProto) + rescuecore2.messages.protobuf.RCRSProto.ValueProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ValueProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ValueProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ValueProto.class, rescuecore2.messages.protobuf.RCRSProto.ValueProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.ValueProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + defined_ = false; + + valueCase_ = 0; + value_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ValueProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ValueProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.ValueProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ValueProto build() { + rescuecore2.messages.protobuf.RCRSProto.ValueProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ValueProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.ValueProto result = new rescuecore2.messages.protobuf.RCRSProto.ValueProto(this); + result.defined_ = defined_; + if (valueCase_ == 2) { + result.value_ = value_; + } + if (valueCase_ == 3) { + result.value_ = value_; + } + if (valueCase_ == 4) { + result.value_ = value_; + } + if (valueCase_ == 5) { + result.value_ = value_; + } + if (valueCase_ == 6) { + if (intListBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = intListBuilder_.build(); + } + } + if (valueCase_ == 7) { + if (intMatrixBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = intMatrixBuilder_.build(); + } + } + if (valueCase_ == 8) { + if (edgeListBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = edgeListBuilder_.build(); + } + } + if (valueCase_ == 9) { + if (point2DBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = point2DBuilder_.build(); + } + } + result.valueCase_ = valueCase_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.ValueProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ValueProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.ValueProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.ValueProto.getDefaultInstance()) return this; + if (other.getDefined() != false) { + setDefined(other.getDefined()); + } + switch (other.getValueCase()) { + case INTVALUE: { + setIntValue(other.getIntValue()); + break; + } + case BOOLVALUE: { + setBoolValue(other.getBoolValue()); + break; + } + case DOUBLEVALUE: { + setDoubleValue(other.getDoubleValue()); + break; + } + case BYTELIST: { + setByteList(other.getByteList()); + break; + } + case INTLIST: { + mergeIntList(other.getIntList()); + break; + } + case INTMATRIX: { + mergeIntMatrix(other.getIntMatrix()); + break; + } + case EDGELIST: { + mergeEdgeList(other.getEdgeList()); + break; + } + case POINT2D: { + mergePoint2D(other.getPoint2D()); + break; + } + case VALUE_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.ValueProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.ValueProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int valueCase_ = 0; + private java.lang.Object value_; + public ValueCase + getValueCase() { + return ValueCase.forNumber( + valueCase_); + } + + public Builder clearValue() { + valueCase_ = 0; + value_ = null; + onChanged(); + return this; + } + + + private boolean defined_ ; + /** + * <code>bool defined = 1;</code> + * @return The defined. + */ + @java.lang.Override + public boolean getDefined() { + return defined_; + } + /** + * <code>bool defined = 1;</code> + * @param value The defined to set. + * @return This builder for chaining. + */ + public Builder setDefined(boolean value) { + + defined_ = value; + onChanged(); + return this; + } + /** + * <code>bool defined = 1;</code> + * @return This builder for chaining. + */ + public Builder clearDefined() { + + defined_ = false; + onChanged(); + return this; + } + + /** + * <code>int32 intValue = 2;</code> + * @return Whether the intValue field is set. + */ + public boolean hasIntValue() { + return valueCase_ == 2; + } + /** + * <code>int32 intValue = 2;</code> + * @return The intValue. + */ + public int getIntValue() { + if (valueCase_ == 2) { + return (java.lang.Integer) value_; + } + return 0; + } + /** + * <code>int32 intValue = 2;</code> + * @param value The intValue to set. + * @return This builder for chaining. + */ + public Builder setIntValue(int value) { + valueCase_ = 2; + value_ = value; + onChanged(); + return this; + } + /** + * <code>int32 intValue = 2;</code> + * @return This builder for chaining. + */ + public Builder clearIntValue() { + if (valueCase_ == 2) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>bool boolValue = 3;</code> + * @return Whether the boolValue field is set. + */ + public boolean hasBoolValue() { + return valueCase_ == 3; + } + /** + * <code>bool boolValue = 3;</code> + * @return The boolValue. + */ + public boolean getBoolValue() { + if (valueCase_ == 3) { + return (java.lang.Boolean) value_; + } + return false; + } + /** + * <code>bool boolValue = 3;</code> + * @param value The boolValue to set. + * @return This builder for chaining. + */ + public Builder setBoolValue(boolean value) { + valueCase_ = 3; + value_ = value; + onChanged(); + return this; + } + /** + * <code>bool boolValue = 3;</code> + * @return This builder for chaining. + */ + public Builder clearBoolValue() { + if (valueCase_ == 3) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>double doubleValue = 4;</code> + * @return Whether the doubleValue field is set. + */ + public boolean hasDoubleValue() { + return valueCase_ == 4; + } + /** + * <code>double doubleValue = 4;</code> + * @return The doubleValue. + */ + public double getDoubleValue() { + if (valueCase_ == 4) { + return (java.lang.Double) value_; + } + return 0D; + } + /** + * <code>double doubleValue = 4;</code> + * @param value The doubleValue to set. + * @return This builder for chaining. + */ + public Builder setDoubleValue(double value) { + valueCase_ = 4; + value_ = value; + onChanged(); + return this; + } + /** + * <code>double doubleValue = 4;</code> + * @return This builder for chaining. + */ + public Builder clearDoubleValue() { + if (valueCase_ == 4) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>bytes byteList = 5;</code> + * @return Whether the byteList field is set. + */ + public boolean hasByteList() { + return valueCase_ == 5; + } + /** + * <code>bytes byteList = 5;</code> + * @return The byteList. + */ + public com.google.protobuf.ByteString getByteList() { + if (valueCase_ == 5) { + return (com.google.protobuf.ByteString) value_; + } + return com.google.protobuf.ByteString.EMPTY; + } + /** + * <code>bytes byteList = 5;</code> + * @param value The byteList to set. + * @return This builder for chaining. + */ + public Builder setByteList(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + valueCase_ = 5; + value_ = value; + onChanged(); + return this; + } + /** + * <code>bytes byteList = 5;</code> + * @return This builder for chaining. + */ + public Builder clearByteList() { + if (valueCase_ == 5) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> intListBuilder_; + /** + * <code>.IntListProto intList = 6;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return valueCase_ == 6; + } + /** + * <code>.IntListProto intList = 6;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (intListBuilder_ == null) { + if (valueCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } else { + if (valueCase_ == 6) { + return intListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 6;</code> + */ + public Builder setIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + intListBuilder_.setMessage(value); + } + valueCase_ = 6; + return this; + } + /** + * <code>.IntListProto intList = 6;</code> + */ + public Builder setIntList( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (intListBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + intListBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 6; + return this; + } + /** + * <code>.IntListProto intList = 6;</code> + */ + public Builder mergeIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (valueCase_ == 6 && + value_ != rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 6) { + intListBuilder_.mergeFrom(value); + } + intListBuilder_.setMessage(value); + } + valueCase_ = 6; + return this; + } + /** + * <code>.IntListProto intList = 6;</code> + */ + public Builder clearIntList() { + if (intListBuilder_ == null) { + if (valueCase_ == 6) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 6) { + valueCase_ = 0; + value_ = null; + } + intListBuilder_.clear(); + } + return this; + } + /** + * <code>.IntListProto intList = 6;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder getIntListBuilder() { + return getIntListFieldBuilder().getBuilder(); + } + /** + * <code>.IntListProto intList = 6;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if ((valueCase_ == 6) && (intListBuilder_ != null)) { + return intListBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 6) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 6;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getIntListFieldBuilder() { + if (intListBuilder_ == null) { + if (!(valueCase_ == 6)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + intListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 6; + onChanged();; + return intListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder> intMatrixBuilder_; + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return Whether the intMatrix field is set. + */ + @java.lang.Override + public boolean hasIntMatrix() { + return valueCase_ == 7; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + * @return The intMatrix. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix() { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } else { + if (valueCase_ == 7) { + return intMatrixBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + public Builder setIntMatrix(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto value) { + if (intMatrixBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + intMatrixBuilder_.setMessage(value); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + public Builder setIntMatrix( + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder builderForValue) { + if (intMatrixBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + intMatrixBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + public Builder mergeIntMatrix(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto value) { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 7 && + value_ != rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 7) { + intMatrixBuilder_.mergeFrom(value); + } + intMatrixBuilder_.setMessage(value); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + public Builder clearIntMatrix() { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 7) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 7) { + valueCase_ = 0; + value_ = null; + } + intMatrixBuilder_.clear(); + } + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder getIntMatrixBuilder() { + return getIntMatrixFieldBuilder().getBuilder(); + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder() { + if ((valueCase_ == 7) && (intMatrixBuilder_ != null)) { + return intMatrixBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + } + /** + * <code>.IntMatrixProto intMatrix = 7;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder> + getIntMatrixFieldBuilder() { + if (intMatrixBuilder_ == null) { + if (!(valueCase_ == 7)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + intMatrixBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 7; + onChanged();; + return intMatrixBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder> edgeListBuilder_; + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return Whether the edgeList field is set. + */ + @java.lang.Override + public boolean hasEdgeList() { + return valueCase_ == 8; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + * @return The edgeList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList() { + if (edgeListBuilder_ == null) { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } else { + if (valueCase_ == 8) { + return edgeListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + public Builder setEdgeList(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto value) { + if (edgeListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + edgeListBuilder_.setMessage(value); + } + valueCase_ = 8; + return this; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + public Builder setEdgeList( + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder builderForValue) { + if (edgeListBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + edgeListBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 8; + return this; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + public Builder mergeEdgeList(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto value) { + if (edgeListBuilder_ == null) { + if (valueCase_ == 8 && + value_ != rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 8) { + edgeListBuilder_.mergeFrom(value); + } + edgeListBuilder_.setMessage(value); + } + valueCase_ = 8; + return this; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + public Builder clearEdgeList() { + if (edgeListBuilder_ == null) { + if (valueCase_ == 8) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 8) { + valueCase_ = 0; + value_ = null; + } + edgeListBuilder_.clear(); + } + return this; + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder getEdgeListBuilder() { + return getEdgeListFieldBuilder().getBuilder(); + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder() { + if ((valueCase_ == 8) && (edgeListBuilder_ != null)) { + return edgeListBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + } + /** + * <code>.EdgeListProto edgeList = 8;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder> + getEdgeListFieldBuilder() { + if (edgeListBuilder_ == null) { + if (!(valueCase_ == 8)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + edgeListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 8; + onChanged();; + return edgeListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder> point2DBuilder_; + /** + * <code>.Point2DProto point2D = 9;</code> + * @return Whether the point2D field is set. + */ + @java.lang.Override + public boolean hasPoint2D() { + return valueCase_ == 9; + } + /** + * <code>.Point2DProto point2D = 9;</code> + * @return The point2D. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D() { + if (point2DBuilder_ == null) { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } else { + if (valueCase_ == 9) { + return point2DBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + public Builder setPoint2D(rescuecore2.messages.protobuf.RCRSProto.Point2DProto value) { + if (point2DBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + point2DBuilder_.setMessage(value); + } + valueCase_ = 9; + return this; + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + public Builder setPoint2D( + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder builderForValue) { + if (point2DBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + point2DBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 9; + return this; + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + public Builder mergePoint2D(rescuecore2.messages.protobuf.RCRSProto.Point2DProto value) { + if (point2DBuilder_ == null) { + if (valueCase_ == 9 && + value_ != rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.Point2DProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 9) { + point2DBuilder_.mergeFrom(value); + } + point2DBuilder_.setMessage(value); + } + valueCase_ = 9; + return this; + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + public Builder clearPoint2D() { + if (point2DBuilder_ == null) { + if (valueCase_ == 9) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 9) { + valueCase_ = 0; + value_ = null; + } + point2DBuilder_.clear(); + } + return this; + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder getPoint2DBuilder() { + return getPoint2DFieldBuilder().getBuilder(); + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder() { + if ((valueCase_ == 9) && (point2DBuilder_ != null)) { + return point2DBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + } + /** + * <code>.Point2DProto point2D = 9;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder> + getPoint2DFieldBuilder() { + if (point2DBuilder_ == null) { + if (!(valueCase_ == 9)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + point2DBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 9; + onChanged();; + return point2DBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:ValueProto) + } + + // @@protoc_insertion_point(class_scope:ValueProto) + private static final rescuecore2.messages.protobuf.RCRSProto.ValueProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.ValueProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.ValueProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ValueProto> + PARSER = new com.google.protobuf.AbstractParser<ValueProto>() { + @java.lang.Override + public ValueProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ValueProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<ValueProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ValueProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ValueProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface PropertyProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:PropertyProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + int getUrn(); + + /** + * <code>bool defined = 2;</code> + * @return The defined. + */ + boolean getDefined(); + + /** + * <code>int32 intValue = 3;</code> + * @return Whether the intValue field is set. + */ + boolean hasIntValue(); + /** + * <code>int32 intValue = 3;</code> + * @return The intValue. + */ + int getIntValue(); + + /** + * <code>bool boolValue = 4;</code> + * @return Whether the boolValue field is set. + */ + boolean hasBoolValue(); + /** + * <code>bool boolValue = 4;</code> + * @return The boolValue. + */ + boolean getBoolValue(); + + /** + * <code>double doubleValue = 5;</code> + * @return Whether the doubleValue field is set. + */ + boolean hasDoubleValue(); + /** + * <code>double doubleValue = 5;</code> + * @return The doubleValue. + */ + double getDoubleValue(); + + /** + * <code>bytes byteList = 6;</code> + * @return Whether the byteList field is set. + */ + boolean hasByteList(); + /** + * <code>bytes byteList = 6;</code> + * @return The byteList. + */ + com.google.protobuf.ByteString getByteList(); + + /** + * <code>.IntListProto intList = 7;</code> + * @return Whether the intList field is set. + */ + boolean hasIntList(); + /** + * <code>.IntListProto intList = 7;</code> + * @return The intList. + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList(); + /** + * <code>.IntListProto intList = 7;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder(); + + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return Whether the intMatrix field is set. + */ + boolean hasIntMatrix(); + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return The intMatrix. + */ + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix(); + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder(); + + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return Whether the edgeList field is set. + */ + boolean hasEdgeList(); + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return The edgeList. + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList(); + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder(); + + /** + * <code>.Point2DProto point2D = 10;</code> + * @return Whether the point2D field is set. + */ + boolean hasPoint2D(); + /** + * <code>.Point2DProto point2D = 10;</code> + * @return The point2D. + */ + rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D(); + /** + * <code>.Point2DProto point2D = 10;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder(); + + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.ValueCase getValueCase(); + } + /** + * Protobuf type {@code PropertyProto} + */ + public static final class PropertyProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:PropertyProto) + PropertyProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use PropertyProto.newBuilder() to construct. + private PropertyProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private PropertyProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new PropertyProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private PropertyProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + urn_ = input.readInt32(); + break; + } + case 16: { + + defined_ = input.readBool(); + break; + } + case 24: { + valueCase_ = 3; + value_ = input.readInt32(); + break; + } + case 32: { + valueCase_ = 4; + value_ = input.readBool(); + break; + } + case 41: { + valueCase_ = 5; + value_ = input.readDouble(); + break; + } + case 50: { + valueCase_ = 6; + value_ = input.readBytes(); + break; + } + case 58: { + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder subBuilder = null; + if (valueCase_ == 7) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 7; + break; + } + case 66: { + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder subBuilder = null; + if (valueCase_ == 8) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 8; + break; + } + case 74: { + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder subBuilder = null; + if (valueCase_ == 9) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 9; + break; + } + case 82: { + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder subBuilder = null; + if (valueCase_ == 10) { + subBuilder = ((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_).toBuilder(); + } + value_ = + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.Point2DProto.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + value_ = subBuilder.buildPartial(); + } + valueCase_ = 10; + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_PropertyProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_PropertyProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.class, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder.class); + } + + private int valueCase_ = 0; + private java.lang.Object value_; + public enum ValueCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + INTVALUE(3), + BOOLVALUE(4), + DOUBLEVALUE(5), + BYTELIST(6), + INTLIST(7), + INTMATRIX(8), + EDGELIST(9), + POINT2D(10), + VALUE_NOT_SET(0); + private final int value; + private ValueCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static ValueCase valueOf(int value) { + return forNumber(value); + } + + public static ValueCase forNumber(int value) { + switch (value) { + case 3: return INTVALUE; + case 4: return BOOLVALUE; + case 5: return DOUBLEVALUE; + case 6: return BYTELIST; + case 7: return INTLIST; + case 8: return INTMATRIX; + case 9: return EDGELIST; + case 10: return POINT2D; + case 0: return VALUE_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public ValueCase + getValueCase() { + return ValueCase.forNumber( + valueCase_); + } + + public static final int URN_FIELD_NUMBER = 1; + private int urn_; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + + public static final int DEFINED_FIELD_NUMBER = 2; + private boolean defined_; + /** + * <code>bool defined = 2;</code> + * @return The defined. + */ + @java.lang.Override + public boolean getDefined() { + return defined_; + } + + public static final int INTVALUE_FIELD_NUMBER = 3; + /** + * <code>int32 intValue = 3;</code> + * @return Whether the intValue field is set. + */ + @java.lang.Override + public boolean hasIntValue() { + return valueCase_ == 3; + } + /** + * <code>int32 intValue = 3;</code> + * @return The intValue. + */ + @java.lang.Override + public int getIntValue() { + if (valueCase_ == 3) { + return (java.lang.Integer) value_; + } + return 0; + } + + public static final int BOOLVALUE_FIELD_NUMBER = 4; + /** + * <code>bool boolValue = 4;</code> + * @return Whether the boolValue field is set. + */ + @java.lang.Override + public boolean hasBoolValue() { + return valueCase_ == 4; + } + /** + * <code>bool boolValue = 4;</code> + * @return The boolValue. + */ + @java.lang.Override + public boolean getBoolValue() { + if (valueCase_ == 4) { + return (java.lang.Boolean) value_; + } + return false; + } + + public static final int DOUBLEVALUE_FIELD_NUMBER = 5; + /** + * <code>double doubleValue = 5;</code> + * @return Whether the doubleValue field is set. + */ + @java.lang.Override + public boolean hasDoubleValue() { + return valueCase_ == 5; + } + /** + * <code>double doubleValue = 5;</code> + * @return The doubleValue. + */ + @java.lang.Override + public double getDoubleValue() { + if (valueCase_ == 5) { + return (java.lang.Double) value_; + } + return 0D; + } + + public static final int BYTELIST_FIELD_NUMBER = 6; + /** + * <code>bytes byteList = 6;</code> + * @return Whether the byteList field is set. + */ + @java.lang.Override + public boolean hasByteList() { + return valueCase_ == 6; + } + /** + * <code>bytes byteList = 6;</code> + * @return The byteList. + */ + @java.lang.Override + public com.google.protobuf.ByteString getByteList() { + if (valueCase_ == 6) { + return (com.google.protobuf.ByteString) value_; + } + return com.google.protobuf.ByteString.EMPTY; + } + + public static final int INTLIST_FIELD_NUMBER = 7; + /** + * <code>.IntListProto intList = 7;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return valueCase_ == 7; + } + /** + * <code>.IntListProto intList = 7;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + /** + * <code>.IntListProto intList = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + + public static final int INTMATRIX_FIELD_NUMBER = 8; + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return Whether the intMatrix field is set. + */ + @java.lang.Override + public boolean hasIntMatrix() { + return valueCase_ == 8; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return The intMatrix. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix() { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder() { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + + public static final int EDGELIST_FIELD_NUMBER = 9; + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return Whether the edgeList field is set. + */ + @java.lang.Override + public boolean hasEdgeList() { + return valueCase_ == 9; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return The edgeList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList() { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder() { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + + public static final int POINT2D_FIELD_NUMBER = 10; + /** + * <code>.Point2DProto point2D = 10;</code> + * @return Whether the point2D field is set. + */ + @java.lang.Override + public boolean hasPoint2D() { + return valueCase_ == 10; + } + /** + * <code>.Point2DProto point2D = 10;</code> + * @return The point2D. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D() { + if (valueCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder() { + if (valueCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (urn_ != 0) { + output.writeInt32(1, urn_); + } + if (defined_ != false) { + output.writeBool(2, defined_); + } + if (valueCase_ == 3) { + output.writeInt32( + 3, (int)((java.lang.Integer) value_)); + } + if (valueCase_ == 4) { + output.writeBool( + 4, (boolean)((java.lang.Boolean) value_)); + } + if (valueCase_ == 5) { + output.writeDouble( + 5, (double)((java.lang.Double) value_)); + } + if (valueCase_ == 6) { + output.writeBytes( + 6, (com.google.protobuf.ByteString) value_); + } + if (valueCase_ == 7) { + output.writeMessage(7, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + } + if (valueCase_ == 8) { + output.writeMessage(8, (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + } + if (valueCase_ == 9) { + output.writeMessage(9, (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + } + if (valueCase_ == 10) { + output.writeMessage(10, (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (urn_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, urn_); + } + if (defined_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, defined_); + } + if (valueCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size( + 3, (int)((java.lang.Integer) value_)); + } + if (valueCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize( + 4, (boolean)((java.lang.Boolean) value_)); + } + if (valueCase_ == 5) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize( + 5, (double)((java.lang.Double) value_)); + } + if (valueCase_ == 6) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize( + 6, (com.google.protobuf.ByteString) value_); + } + if (valueCase_ == 7) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_); + } + if (valueCase_ == 8) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_); + } + if (valueCase_ == 9) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_); + } + if (valueCase_ == 10) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(10, (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.PropertyProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.PropertyProto other = (rescuecore2.messages.protobuf.RCRSProto.PropertyProto) obj; + + if (getUrn() + != other.getUrn()) return false; + if (getDefined() + != other.getDefined()) return false; + if (!getValueCase().equals(other.getValueCase())) return false; + switch (valueCase_) { + case 3: + if (getIntValue() + != other.getIntValue()) return false; + break; + case 4: + if (getBoolValue() + != other.getBoolValue()) return false; + break; + case 5: + if (java.lang.Double.doubleToLongBits(getDoubleValue()) + != java.lang.Double.doubleToLongBits( + other.getDoubleValue())) return false; + break; + case 6: + if (!getByteList() + .equals(other.getByteList())) return false; + break; + case 7: + if (!getIntList() + .equals(other.getIntList())) return false; + break; + case 8: + if (!getIntMatrix() + .equals(other.getIntMatrix())) return false; + break; + case 9: + if (!getEdgeList() + .equals(other.getEdgeList())) return false; + break; + case 10: + if (!getPoint2D() + .equals(other.getPoint2D())) return false; + break; + case 0: + default: + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URN_FIELD_NUMBER; + hash = (53 * hash) + getUrn(); + hash = (37 * hash) + DEFINED_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getDefined()); + switch (valueCase_) { + case 3: + hash = (37 * hash) + INTVALUE_FIELD_NUMBER; + hash = (53 * hash) + getIntValue(); + break; + case 4: + hash = (37 * hash) + BOOLVALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getBoolValue()); + break; + case 5: + hash = (37 * hash) + DOUBLEVALUE_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getDoubleValue())); + break; + case 6: + hash = (37 * hash) + BYTELIST_FIELD_NUMBER; + hash = (53 * hash) + getByteList().hashCode(); + break; + case 7: + hash = (37 * hash) + INTLIST_FIELD_NUMBER; + hash = (53 * hash) + getIntList().hashCode(); + break; + case 8: + hash = (37 * hash) + INTMATRIX_FIELD_NUMBER; + hash = (53 * hash) + getIntMatrix().hashCode(); + break; + case 9: + hash = (37 * hash) + EDGELIST_FIELD_NUMBER; + hash = (53 * hash) + getEdgeList().hashCode(); + break; + case 10: + hash = (37 * hash) + POINT2D_FIELD_NUMBER; + hash = (53 * hash) + getPoint2D().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.PropertyProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code PropertyProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:PropertyProto) + rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_PropertyProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_PropertyProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.class, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.PropertyProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + urn_ = 0; + + defined_ = false; + + valueCase_ = 0; + value_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_PropertyProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto build() { + rescuecore2.messages.protobuf.RCRSProto.PropertyProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.PropertyProto result = new rescuecore2.messages.protobuf.RCRSProto.PropertyProto(this); + result.urn_ = urn_; + result.defined_ = defined_; + if (valueCase_ == 3) { + result.value_ = value_; + } + if (valueCase_ == 4) { + result.value_ = value_; + } + if (valueCase_ == 5) { + result.value_ = value_; + } + if (valueCase_ == 6) { + result.value_ = value_; + } + if (valueCase_ == 7) { + if (intListBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = intListBuilder_.build(); + } + } + if (valueCase_ == 8) { + if (intMatrixBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = intMatrixBuilder_.build(); + } + } + if (valueCase_ == 9) { + if (edgeListBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = edgeListBuilder_.build(); + } + } + if (valueCase_ == 10) { + if (point2DBuilder_ == null) { + result.value_ = value_; + } else { + result.value_ = point2DBuilder_.build(); + } + } + result.valueCase_ = valueCase_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.PropertyProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.PropertyProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.PropertyProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance()) return this; + if (other.getUrn() != 0) { + setUrn(other.getUrn()); + } + if (other.getDefined() != false) { + setDefined(other.getDefined()); + } + switch (other.getValueCase()) { + case INTVALUE: { + setIntValue(other.getIntValue()); + break; + } + case BOOLVALUE: { + setBoolValue(other.getBoolValue()); + break; + } + case DOUBLEVALUE: { + setDoubleValue(other.getDoubleValue()); + break; + } + case BYTELIST: { + setByteList(other.getByteList()); + break; + } + case INTLIST: { + mergeIntList(other.getIntList()); + break; + } + case INTMATRIX: { + mergeIntMatrix(other.getIntMatrix()); + break; + } + case EDGELIST: { + mergeEdgeList(other.getEdgeList()); + break; + } + case POINT2D: { + mergePoint2D(other.getPoint2D()); + break; + } + case VALUE_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.PropertyProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.PropertyProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int valueCase_ = 0; + private java.lang.Object value_; + public ValueCase + getValueCase() { + return ValueCase.forNumber( + valueCase_); + } + + public Builder clearValue() { + valueCase_ = 0; + value_ = null; + onChanged(); + return this; + } + + + private int urn_ ; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + /** + * <code>int32 urn = 1;</code> + * @param value The urn to set. + * @return This builder for chaining. + */ + public Builder setUrn(int value) { + + urn_ = value; + onChanged(); + return this; + } + /** + * <code>int32 urn = 1;</code> + * @return This builder for chaining. + */ + public Builder clearUrn() { + + urn_ = 0; + onChanged(); + return this; + } + + private boolean defined_ ; + /** + * <code>bool defined = 2;</code> + * @return The defined. + */ + @java.lang.Override + public boolean getDefined() { + return defined_; + } + /** + * <code>bool defined = 2;</code> + * @param value The defined to set. + * @return This builder for chaining. + */ + public Builder setDefined(boolean value) { + + defined_ = value; + onChanged(); + return this; + } + /** + * <code>bool defined = 2;</code> + * @return This builder for chaining. + */ + public Builder clearDefined() { + + defined_ = false; + onChanged(); + return this; + } + + /** + * <code>int32 intValue = 3;</code> + * @return Whether the intValue field is set. + */ + public boolean hasIntValue() { + return valueCase_ == 3; + } + /** + * <code>int32 intValue = 3;</code> + * @return The intValue. + */ + public int getIntValue() { + if (valueCase_ == 3) { + return (java.lang.Integer) value_; + } + return 0; + } + /** + * <code>int32 intValue = 3;</code> + * @param value The intValue to set. + * @return This builder for chaining. + */ + public Builder setIntValue(int value) { + valueCase_ = 3; + value_ = value; + onChanged(); + return this; + } + /** + * <code>int32 intValue = 3;</code> + * @return This builder for chaining. + */ + public Builder clearIntValue() { + if (valueCase_ == 3) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>bool boolValue = 4;</code> + * @return Whether the boolValue field is set. + */ + public boolean hasBoolValue() { + return valueCase_ == 4; + } + /** + * <code>bool boolValue = 4;</code> + * @return The boolValue. + */ + public boolean getBoolValue() { + if (valueCase_ == 4) { + return (java.lang.Boolean) value_; + } + return false; + } + /** + * <code>bool boolValue = 4;</code> + * @param value The boolValue to set. + * @return This builder for chaining. + */ + public Builder setBoolValue(boolean value) { + valueCase_ = 4; + value_ = value; + onChanged(); + return this; + } + /** + * <code>bool boolValue = 4;</code> + * @return This builder for chaining. + */ + public Builder clearBoolValue() { + if (valueCase_ == 4) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>double doubleValue = 5;</code> + * @return Whether the doubleValue field is set. + */ + public boolean hasDoubleValue() { + return valueCase_ == 5; + } + /** + * <code>double doubleValue = 5;</code> + * @return The doubleValue. + */ + public double getDoubleValue() { + if (valueCase_ == 5) { + return (java.lang.Double) value_; + } + return 0D; + } + /** + * <code>double doubleValue = 5;</code> + * @param value The doubleValue to set. + * @return This builder for chaining. + */ + public Builder setDoubleValue(double value) { + valueCase_ = 5; + value_ = value; + onChanged(); + return this; + } + /** + * <code>double doubleValue = 5;</code> + * @return This builder for chaining. + */ + public Builder clearDoubleValue() { + if (valueCase_ == 5) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + /** + * <code>bytes byteList = 6;</code> + * @return Whether the byteList field is set. + */ + public boolean hasByteList() { + return valueCase_ == 6; + } + /** + * <code>bytes byteList = 6;</code> + * @return The byteList. + */ + public com.google.protobuf.ByteString getByteList() { + if (valueCase_ == 6) { + return (com.google.protobuf.ByteString) value_; + } + return com.google.protobuf.ByteString.EMPTY; + } + /** + * <code>bytes byteList = 6;</code> + * @param value The byteList to set. + * @return This builder for chaining. + */ + public Builder setByteList(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + valueCase_ = 6; + value_ = value; + onChanged(); + return this; + } + /** + * <code>bytes byteList = 6;</code> + * @return This builder for chaining. + */ + public Builder clearByteList() { + if (valueCase_ == 6) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + return this; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> intListBuilder_; + /** + * <code>.IntListProto intList = 7;</code> + * @return Whether the intList field is set. + */ + @java.lang.Override + public boolean hasIntList() { + return valueCase_ == 7; + } + /** + * <code>.IntListProto intList = 7;</code> + * @return The intList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProto getIntList() { + if (intListBuilder_ == null) { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } else { + if (valueCase_ == 7) { + return intListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 7;</code> + */ + public Builder setIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + intListBuilder_.setMessage(value); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntListProto intList = 7;</code> + */ + public Builder setIntList( + rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder builderForValue) { + if (intListBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + intListBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntListProto intList = 7;</code> + */ + public Builder mergeIntList(rescuecore2.messages.protobuf.RCRSProto.IntListProto value) { + if (intListBuilder_ == null) { + if (valueCase_ == 7 && + value_ != rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 7) { + intListBuilder_.mergeFrom(value); + } + intListBuilder_.setMessage(value); + } + valueCase_ = 7; + return this; + } + /** + * <code>.IntListProto intList = 7;</code> + */ + public Builder clearIntList() { + if (intListBuilder_ == null) { + if (valueCase_ == 7) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 7) { + valueCase_ = 0; + value_ = null; + } + intListBuilder_.clear(); + } + return this; + } + /** + * <code>.IntListProto intList = 7;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder getIntListBuilder() { + return getIntListFieldBuilder().getBuilder(); + } + /** + * <code>.IntListProto intList = 7;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder getIntListOrBuilder() { + if ((valueCase_ == 7) && (intListBuilder_ != null)) { + return intListBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 7) { + return (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + } + /** + * <code>.IntListProto intList = 7;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder> + getIntListFieldBuilder() { + if (intListBuilder_ == null) { + if (!(valueCase_ == 7)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntListProto.getDefaultInstance(); + } + intListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntListProto, rescuecore2.messages.protobuf.RCRSProto.IntListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntListProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 7; + onChanged();; + return intListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder> intMatrixBuilder_; + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return Whether the intMatrix field is set. + */ + @java.lang.Override + public boolean hasIntMatrix() { + return valueCase_ == 8; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + * @return The intMatrix. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto getIntMatrix() { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } else { + if (valueCase_ == 8) { + return intMatrixBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + public Builder setIntMatrix(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto value) { + if (intMatrixBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + intMatrixBuilder_.setMessage(value); + } + valueCase_ = 8; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + public Builder setIntMatrix( + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder builderForValue) { + if (intMatrixBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + intMatrixBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 8; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + public Builder mergeIntMatrix(rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto value) { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 8 && + value_ != rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 8) { + intMatrixBuilder_.mergeFrom(value); + } + intMatrixBuilder_.setMessage(value); + } + valueCase_ = 8; + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + public Builder clearIntMatrix() { + if (intMatrixBuilder_ == null) { + if (valueCase_ == 8) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 8) { + valueCase_ = 0; + value_ = null; + } + intMatrixBuilder_.clear(); + } + return this; + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder getIntMatrixBuilder() { + return getIntMatrixFieldBuilder().getBuilder(); + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder getIntMatrixOrBuilder() { + if ((valueCase_ == 8) && (intMatrixBuilder_ != null)) { + return intMatrixBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 8) { + return (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + } + /** + * <code>.IntMatrixProto intMatrix = 8;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder> + getIntMatrixFieldBuilder() { + if (intMatrixBuilder_ == null) { + if (!(valueCase_ == 8)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.getDefaultInstance(); + } + intMatrixBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto.Builder, rescuecore2.messages.protobuf.RCRSProto.IntMatrixProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.IntMatrixProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 8; + onChanged();; + return intMatrixBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder> edgeListBuilder_; + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return Whether the edgeList field is set. + */ + @java.lang.Override + public boolean hasEdgeList() { + return valueCase_ == 9; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + * @return The edgeList. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getEdgeList() { + if (edgeListBuilder_ == null) { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } else { + if (valueCase_ == 9) { + return edgeListBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + public Builder setEdgeList(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto value) { + if (edgeListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + edgeListBuilder_.setMessage(value); + } + valueCase_ = 9; + return this; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + public Builder setEdgeList( + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder builderForValue) { + if (edgeListBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + edgeListBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 9; + return this; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + public Builder mergeEdgeList(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto value) { + if (edgeListBuilder_ == null) { + if (valueCase_ == 9 && + value_ != rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 9) { + edgeListBuilder_.mergeFrom(value); + } + edgeListBuilder_.setMessage(value); + } + valueCase_ = 9; + return this; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + public Builder clearEdgeList() { + if (edgeListBuilder_ == null) { + if (valueCase_ == 9) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 9) { + valueCase_ = 0; + value_ = null; + } + edgeListBuilder_.clear(); + } + return this; + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder getEdgeListBuilder() { + return getEdgeListFieldBuilder().getBuilder(); + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder getEdgeListOrBuilder() { + if ((valueCase_ == 9) && (edgeListBuilder_ != null)) { + return edgeListBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 9) { + return (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + } + /** + * <code>.EdgeListProto edgeList = 9;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder> + getEdgeListFieldBuilder() { + if (edgeListBuilder_ == null) { + if (!(valueCase_ == 9)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + edgeListBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 9; + onChanged();; + return edgeListBuilder_; + } + + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder> point2DBuilder_; + /** + * <code>.Point2DProto point2D = 10;</code> + * @return Whether the point2D field is set. + */ + @java.lang.Override + public boolean hasPoint2D() { + return valueCase_ == 10; + } + /** + * <code>.Point2DProto point2D = 10;</code> + * @return The point2D. + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getPoint2D() { + if (point2DBuilder_ == null) { + if (valueCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } else { + if (valueCase_ == 10) { + return point2DBuilder_.getMessage(); + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + public Builder setPoint2D(rescuecore2.messages.protobuf.RCRSProto.Point2DProto value) { + if (point2DBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + value_ = value; + onChanged(); + } else { + point2DBuilder_.setMessage(value); + } + valueCase_ = 10; + return this; + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + public Builder setPoint2D( + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder builderForValue) { + if (point2DBuilder_ == null) { + value_ = builderForValue.build(); + onChanged(); + } else { + point2DBuilder_.setMessage(builderForValue.build()); + } + valueCase_ = 10; + return this; + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + public Builder mergePoint2D(rescuecore2.messages.protobuf.RCRSProto.Point2DProto value) { + if (point2DBuilder_ == null) { + if (valueCase_ == 10 && + value_ != rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance()) { + value_ = rescuecore2.messages.protobuf.RCRSProto.Point2DProto.newBuilder((rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_) + .mergeFrom(value).buildPartial(); + } else { + value_ = value; + } + onChanged(); + } else { + if (valueCase_ == 10) { + point2DBuilder_.mergeFrom(value); + } + point2DBuilder_.setMessage(value); + } + valueCase_ = 10; + return this; + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + public Builder clearPoint2D() { + if (point2DBuilder_ == null) { + if (valueCase_ == 10) { + valueCase_ = 0; + value_ = null; + onChanged(); + } + } else { + if (valueCase_ == 10) { + valueCase_ = 0; + value_ = null; + } + point2DBuilder_.clear(); + } + return this; + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder getPoint2DBuilder() { + return getPoint2DFieldBuilder().getBuilder(); + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder getPoint2DOrBuilder() { + if ((valueCase_ == 10) && (point2DBuilder_ != null)) { + return point2DBuilder_.getMessageOrBuilder(); + } else { + if (valueCase_ == 10) { + return (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_; + } + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + } + /** + * <code>.Point2DProto point2D = 10;</code> + */ + private com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder> + getPoint2DFieldBuilder() { + if (point2DBuilder_ == null) { + if (!(valueCase_ == 10)) { + value_ = rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + point2DBuilder_ = new com.google.protobuf.SingleFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.Point2DProto, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder, rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder>( + (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) value_, + getParentForChildren(), + isClean()); + value_ = null; + } + valueCase_ = 10; + onChanged();; + return point2DBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:PropertyProto) + } + + // @@protoc_insertion_point(class_scope:PropertyProto) + private static final rescuecore2.messages.protobuf.RCRSProto.PropertyProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.PropertyProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.PropertyProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<PropertyProto> + PARSER = new com.google.protobuf.AbstractParser<PropertyProto>() { + @java.lang.Override + public PropertyProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new PropertyProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<PropertyProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<PropertyProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface Point2DProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:Point2DProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>double X = 1;</code> + * @return The x. + */ + double getX(); + + /** + * <code>double Y = 2;</code> + * @return The y. + */ + double getY(); + } + /** + * Protobuf type {@code Point2DProto} + */ + public static final class Point2DProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:Point2DProto) + Point2DProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use Point2DProto.newBuilder() to construct. + private Point2DProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private Point2DProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new Point2DProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Point2DProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 9: { + + x_ = input.readDouble(); + break; + } + case 17: { + + y_ = input.readDouble(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_Point2DProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_Point2DProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.class, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder.class); + } + + public static final int X_FIELD_NUMBER = 1; + private double x_; + /** + * <code>double X = 1;</code> + * @return The x. + */ + @java.lang.Override + public double getX() { + return x_; + } + + public static final int Y_FIELD_NUMBER = 2; + private double y_; + /** + * <code>double Y = 2;</code> + * @return The y. + */ + @java.lang.Override + public double getY() { + return y_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (x_ != 0D) { + output.writeDouble(1, x_); + } + if (y_ != 0D) { + output.writeDouble(2, y_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (x_ != 0D) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize(1, x_); + } + if (y_ != 0D) { + size += com.google.protobuf.CodedOutputStream + .computeDoubleSize(2, y_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.Point2DProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.Point2DProto other = (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) obj; + + if (java.lang.Double.doubleToLongBits(getX()) + != java.lang.Double.doubleToLongBits( + other.getX())) return false; + if (java.lang.Double.doubleToLongBits(getY()) + != java.lang.Double.doubleToLongBits( + other.getY())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + X_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getX())); + hash = (37 * hash) + Y_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + java.lang.Double.doubleToLongBits(getY())); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.Point2DProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code Point2DProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:Point2DProto) + rescuecore2.messages.protobuf.RCRSProto.Point2DProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_Point2DProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_Point2DProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.Point2DProto.class, rescuecore2.messages.protobuf.RCRSProto.Point2DProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.Point2DProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + x_ = 0D; + + y_ = 0D; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_Point2DProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto build() { + rescuecore2.messages.protobuf.RCRSProto.Point2DProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.Point2DProto result = new rescuecore2.messages.protobuf.RCRSProto.Point2DProto(this); + result.x_ = x_; + result.y_ = y_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.Point2DProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.Point2DProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.Point2DProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.Point2DProto.getDefaultInstance()) return this; + if (other.getX() != 0D) { + setX(other.getX()); + } + if (other.getY() != 0D) { + setY(other.getY()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.Point2DProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.Point2DProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private double x_ ; + /** + * <code>double X = 1;</code> + * @return The x. + */ + @java.lang.Override + public double getX() { + return x_; + } + /** + * <code>double X = 1;</code> + * @param value The x to set. + * @return This builder for chaining. + */ + public Builder setX(double value) { + + x_ = value; + onChanged(); + return this; + } + /** + * <code>double X = 1;</code> + * @return This builder for chaining. + */ + public Builder clearX() { + + x_ = 0D; + onChanged(); + return this; + } + + private double y_ ; + /** + * <code>double Y = 2;</code> + * @return The y. + */ + @java.lang.Override + public double getY() { + return y_; + } + /** + * <code>double Y = 2;</code> + * @param value The y to set. + * @return This builder for chaining. + */ + public Builder setY(double value) { + + y_ = value; + onChanged(); + return this; + } + /** + * <code>double Y = 2;</code> + * @return This builder for chaining. + */ + public Builder clearY() { + + y_ = 0D; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:Point2DProto) + } + + // @@protoc_insertion_point(class_scope:Point2DProto) + private static final rescuecore2.messages.protobuf.RCRSProto.Point2DProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.Point2DProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.Point2DProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<Point2DProto> + PARSER = new com.google.protobuf.AbstractParser<Point2DProto>() { + @java.lang.Override + public Point2DProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Point2DProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<Point2DProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<Point2DProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.Point2DProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface EntityProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:EntityProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + int getUrn(); + + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + int getEntityID(); + + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> + getPropertiesList(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + int getPropertiesCount(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index); + } + /** + * Protobuf type {@code EntityProto} + */ + public static final class EntityProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:EntityProto) + EntityProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EntityProto.newBuilder() to construct. + private EntityProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EntityProto() { + properties_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EntityProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EntityProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + urn_ = input.readInt32(); + break; + } + case 16: { + + entityID_ = input.readInt32(); + break; + } + case 26: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + properties_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.PropertyProto>(); + mutable_bitField0_ |= 0x00000001; + } + properties_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.PropertyProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + properties_ = java.util.Collections.unmodifiableList(properties_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.class, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder.class); + } + + public static final int URN_FIELD_NUMBER = 1; + private int urn_; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + + public static final int ENTITYID_FIELD_NUMBER = 2; + private int entityID_; + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + + public static final int PROPERTIES_FIELD_NUMBER = 3; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> properties_; + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> getPropertiesList() { + return properties_; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList() { + return properties_; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public int getPropertiesCount() { + return properties_.size(); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index) { + return properties_.get(index); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index) { + return properties_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (urn_ != 0) { + output.writeInt32(1, urn_); + } + if (entityID_ != 0) { + output.writeInt32(2, entityID_); + } + for (int i = 0; i < properties_.size(); i++) { + output.writeMessage(3, properties_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (urn_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, urn_); + } + if (entityID_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, entityID_); + } + for (int i = 0; i < properties_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, properties_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.EntityProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.EntityProto other = (rescuecore2.messages.protobuf.RCRSProto.EntityProto) obj; + + if (getUrn() + != other.getUrn()) return false; + if (getEntityID() + != other.getEntityID()) return false; + if (!getPropertiesList() + .equals(other.getPropertiesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URN_FIELD_NUMBER; + hash = (53 * hash) + getUrn(); + hash = (37 * hash) + ENTITYID_FIELD_NUMBER; + hash = (53 * hash) + getEntityID(); + if (getPropertiesCount() > 0) { + hash = (37 * hash) + PROPERTIES_FIELD_NUMBER; + hash = (53 * hash) + getPropertiesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.EntityProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code EntityProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:EntityProto) + rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.class, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.EntityProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getPropertiesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + urn_ = 0; + + entityID_ = 0; + + if (propertiesBuilder_ == null) { + properties_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + propertiesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto build() { + rescuecore2.messages.protobuf.RCRSProto.EntityProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.EntityProto result = new rescuecore2.messages.protobuf.RCRSProto.EntityProto(this); + int from_bitField0_ = bitField0_; + result.urn_ = urn_; + result.entityID_ = entityID_; + if (propertiesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + properties_ = java.util.Collections.unmodifiableList(properties_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.properties_ = properties_; + } else { + result.properties_ = propertiesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.EntityProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EntityProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.EntityProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()) return this; + if (other.getUrn() != 0) { + setUrn(other.getUrn()); + } + if (other.getEntityID() != 0) { + setEntityID(other.getEntityID()); + } + if (propertiesBuilder_ == null) { + if (!other.properties_.isEmpty()) { + if (properties_.isEmpty()) { + properties_ = other.properties_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensurePropertiesIsMutable(); + properties_.addAll(other.properties_); + } + onChanged(); + } + } else { + if (!other.properties_.isEmpty()) { + if (propertiesBuilder_.isEmpty()) { + propertiesBuilder_.dispose(); + propertiesBuilder_ = null; + properties_ = other.properties_; + bitField0_ = (bitField0_ & ~0x00000001); + propertiesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getPropertiesFieldBuilder() : null; + } else { + propertiesBuilder_.addAllMessages(other.properties_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.EntityProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.EntityProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int urn_ ; + /** + * <code>int32 urn = 1;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + /** + * <code>int32 urn = 1;</code> + * @param value The urn to set. + * @return This builder for chaining. + */ + public Builder setUrn(int value) { + + urn_ = value; + onChanged(); + return this; + } + /** + * <code>int32 urn = 1;</code> + * @return This builder for chaining. + */ + public Builder clearUrn() { + + urn_ = 0; + onChanged(); + return this; + } + + private int entityID_ ; + /** + * <code>int32 entityID = 2;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + /** + * <code>int32 entityID = 2;</code> + * @param value The entityID to set. + * @return This builder for chaining. + */ + public Builder setEntityID(int value) { + + entityID_ = value; + onChanged(); + return this; + } + /** + * <code>int32 entityID = 2;</code> + * @return This builder for chaining. + */ + public Builder clearEntityID() { + + entityID_ = 0; + onChanged(); + return this; + } + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> properties_ = + java.util.Collections.emptyList(); + private void ensurePropertiesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + properties_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.PropertyProto>(properties_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> propertiesBuilder_; + + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> getPropertiesList() { + if (propertiesBuilder_ == null) { + return java.util.Collections.unmodifiableList(properties_); + } else { + return propertiesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public int getPropertiesCount() { + if (propertiesBuilder_ == null) { + return properties_.size(); + } else { + return propertiesBuilder_.getCount(); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index) { + if (propertiesBuilder_ == null) { + return properties_.get(index); + } else { + return propertiesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder setProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.set(index, value); + onChanged(); + } else { + propertiesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder setProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.set(index, builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties(rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.add(value); + onChanged(); + } else { + propertiesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.add(index, value); + onChanged(); + } else { + propertiesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.add(builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.add(index, builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addAllProperties( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProto> values) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, properties_); + onChanged(); + } else { + propertiesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder clearProperties() { + if (propertiesBuilder_ == null) { + properties_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + propertiesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder removeProperties(int index) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.remove(index); + onChanged(); + } else { + propertiesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder getPropertiesBuilder( + int index) { + return getPropertiesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index) { + if (propertiesBuilder_ == null) { + return properties_.get(index); } else { + return propertiesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList() { + if (propertiesBuilder_ != null) { + return propertiesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(properties_); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder addPropertiesBuilder() { + return getPropertiesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance()); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder addPropertiesBuilder( + int index) { + return getPropertiesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance()); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder> + getPropertiesBuilderList() { + return getPropertiesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesFieldBuilder() { + if (propertiesBuilder_ == null) { + propertiesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder>( + properties_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + properties_ = null; + } + return propertiesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:EntityProto) + } + + // @@protoc_insertion_point(class_scope:EntityProto) + private static final rescuecore2.messages.protobuf.RCRSProto.EntityProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.EntityProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.EntityProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EntityProto> + PARSER = new com.google.protobuf.AbstractParser<EntityProto>() { + @java.lang.Override + public EntityProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EntityProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EntityProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EntityProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface EntityListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:EntityListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> + getEntitiesList(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + int getEntitiesCount(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList(); + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index); + } + /** + * Protobuf type {@code EntityListProto} + */ + public static final class EntityListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:EntityListProto) + EntityListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EntityListProto.newBuilder() to construct. + private EntityListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EntityListProto() { + entities_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EntityListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EntityListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + entities_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EntityProto>(); + mutable_bitField0_ |= 0x00000001; + } + entities_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EntityProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + entities_ = java.util.Collections.unmodifiableList(entities_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EntityListProto.class, rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder.class); + } + + public static final int ENTITIES_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> entities_; + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> getEntitiesList() { + return entities_; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList() { + return entities_; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public int getEntitiesCount() { + return entities_.size(); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index) { + return entities_.get(index); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index) { + return entities_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < entities_.size(); i++) { + output.writeMessage(1, entities_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < entities_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, entities_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.EntityListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.EntityListProto other = (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) obj; + + if (!getEntitiesList() + .equals(other.getEntitiesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getEntitiesCount() > 0) { + hash = (37 * hash) + ENTITIES_FIELD_NUMBER; + hash = (53 * hash) + getEntitiesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.EntityListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code EntityListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:EntityListProto) + rescuecore2.messages.protobuf.RCRSProto.EntityListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EntityListProto.class, rescuecore2.messages.protobuf.RCRSProto.EntityListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.EntityListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getEntitiesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (entitiesBuilder_ == null) { + entities_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + entitiesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EntityListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto build() { + rescuecore2.messages.protobuf.RCRSProto.EntityListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.EntityListProto result = new rescuecore2.messages.protobuf.RCRSProto.EntityListProto(this); + int from_bitField0_ = bitField0_; + if (entitiesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + entities_ = java.util.Collections.unmodifiableList(entities_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.entities_ = entities_; + } else { + result.entities_ = entitiesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.EntityListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EntityListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.EntityListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.EntityListProto.getDefaultInstance()) return this; + if (entitiesBuilder_ == null) { + if (!other.entities_.isEmpty()) { + if (entities_.isEmpty()) { + entities_ = other.entities_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureEntitiesIsMutable(); + entities_.addAll(other.entities_); + } + onChanged(); + } + } else { + if (!other.entities_.isEmpty()) { + if (entitiesBuilder_.isEmpty()) { + entitiesBuilder_.dispose(); + entitiesBuilder_ = null; + entities_ = other.entities_; + bitField0_ = (bitField0_ & ~0x00000001); + entitiesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getEntitiesFieldBuilder() : null; + } else { + entitiesBuilder_.addAllMessages(other.entities_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.EntityListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.EntityListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> entities_ = + java.util.Collections.emptyList(); + private void ensureEntitiesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + entities_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EntityProto>(entities_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> entitiesBuilder_; + + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto> getEntitiesList() { + if (entitiesBuilder_ == null) { + return java.util.Collections.unmodifiableList(entities_); + } else { + return entitiesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public int getEntitiesCount() { + if (entitiesBuilder_ == null) { + return entities_.size(); + } else { + return entitiesBuilder_.getCount(); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto getEntities(int index) { + if (entitiesBuilder_ == null) { + return entities_.get(index); + } else { + return entitiesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder setEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.set(index, value); + onChanged(); + } else { + entitiesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder setEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.set(index, builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities(rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.add(value); + onChanged(); + } else { + entitiesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto value) { + if (entitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEntitiesIsMutable(); + entities_.add(index, value); + onChanged(); + } else { + entitiesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.add(builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addEntities( + int index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder builderForValue) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.add(index, builderForValue.build()); + onChanged(); + } else { + entitiesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder addAllEntities( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProto> values) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, entities_); + onChanged(); + } else { + entitiesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder clearEntities() { + if (entitiesBuilder_ == null) { + entities_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + entitiesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public Builder removeEntities(int index) { + if (entitiesBuilder_ == null) { + ensureEntitiesIsMutable(); + entities_.remove(index); + onChanged(); + } else { + entitiesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder getEntitiesBuilder( + int index) { + return getEntitiesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder getEntitiesOrBuilder( + int index) { + if (entitiesBuilder_ == null) { + return entities_.get(index); } else { + return entitiesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesOrBuilderList() { + if (entitiesBuilder_ != null) { + return entitiesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(entities_); + } + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder addEntitiesBuilder() { + return getEntitiesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder addEntitiesBuilder( + int index) { + return getEntitiesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.EntityProto.getDefaultInstance()); + } + /** + * <code>repeated .EntityProto entities = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder> + getEntitiesBuilderList() { + return getEntitiesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder> + getEntitiesFieldBuilder() { + if (entitiesBuilder_ == null) { + entitiesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EntityProto, rescuecore2.messages.protobuf.RCRSProto.EntityProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EntityProtoOrBuilder>( + entities_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + entities_ = null; + } + return entitiesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:EntityListProto) + } + + // @@protoc_insertion_point(class_scope:EntityListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.EntityListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.EntityListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.EntityListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EntityListProto> + PARSER = new com.google.protobuf.AbstractParser<EntityListProto>() { + @java.lang.Override + public EntityListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EntityListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EntityListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EntityListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EntityListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface ConfigProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:ConfigProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>map<string, string> data = 1;</code> + */ + int getDataCount(); + /** + * <code>map<string, string> data = 1;</code> + */ + boolean containsData( + java.lang.String key); + /** + * Use {@link #getDataMap()} instead. + */ + @java.lang.Deprecated + java.util.Map<java.lang.String, java.lang.String> + getData(); + /** + * <code>map<string, string> data = 1;</code> + */ + java.util.Map<java.lang.String, java.lang.String> + getDataMap(); + /** + * <code>map<string, string> data = 1;</code> + */ + + java.lang.String getDataOrDefault( + java.lang.String key, + java.lang.String defaultValue); + /** + * <code>map<string, string> data = 1;</code> + */ + + java.lang.String getDataOrThrow( + java.lang.String key); + } + /** + * Protobuf type {@code ConfigProto} + */ + public static final class ConfigProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:ConfigProto) + ConfigProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use ConfigProto.newBuilder() to construct. + private ConfigProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private ConfigProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ConfigProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ConfigProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + data_ = com.google.protobuf.MapField.newMapField( + DataDefaultEntryHolder.defaultEntry); + mutable_bitField0_ |= 0x00000001; + } + com.google.protobuf.MapEntry<java.lang.String, java.lang.String> + data__ = input.readMessage( + DataDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + data_.getMutableMap().put( + data__.getKey(), data__.getValue()); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapField internalGetMapField( + int number) { + switch (number) { + case 1: + return internalGetData(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.class, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder.class); + } + + public static final int DATA_FIELD_NUMBER = 1; + private static final class DataDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .<java.lang.String, java.lang.String>newDefaultInstance( + rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_DataEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> data_; + private com.google.protobuf.MapField<java.lang.String, java.lang.String> + internalGetData() { + if (data_ == null) { + return com.google.protobuf.MapField.emptyMapField( + DataDefaultEntryHolder.defaultEntry); + } + return data_; + } + + public int getDataCount() { + return internalGetData().getMap().size(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + + @java.lang.Override + public boolean containsData( + java.lang.String key) { + if (key == null) { throw new java.lang.NullPointerException(); } + return internalGetData().getMap().containsKey(key); + } + /** + * Use {@link #getDataMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map<java.lang.String, java.lang.String> getData() { + return getDataMap(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.util.Map<java.lang.String, java.lang.String> getDataMap() { + return internalGetData().getMap(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.lang.String getDataOrDefault( + java.lang.String key, + java.lang.String defaultValue) { + if (key == null) { throw new java.lang.NullPointerException(); } + java.util.Map<java.lang.String, java.lang.String> map = + internalGetData().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.lang.String getDataOrThrow( + java.lang.String key) { + if (key == null) { throw new java.lang.NullPointerException(); } + java.util.Map<java.lang.String, java.lang.String> map = + internalGetData().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + com.google.protobuf.GeneratedMessageV3 + .serializeStringMapTo( + output, + internalGetData(), + DataDefaultEntryHolder.defaultEntry, + 1); + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (java.util.Map.Entry<java.lang.String, java.lang.String> entry + : internalGetData().getMap().entrySet()) { + com.google.protobuf.MapEntry<java.lang.String, java.lang.String> + data__ = DataDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, data__); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.ConfigProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.ConfigProto other = (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) obj; + + if (!internalGetData().equals( + other.internalGetData())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (!internalGetData().getMap().isEmpty()) { + hash = (37 * hash) + DATA_FIELD_NUMBER; + hash = (53 * hash) + internalGetData().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.ConfigProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ConfigProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:ConfigProto) + rescuecore2.messages.protobuf.RCRSProto.ConfigProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapField internalGetMapField( + int number) { + switch (number) { + case 1: + return internalGetData(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapField internalGetMutableMapField( + int number) { + switch (number) { + case 1: + return internalGetMutableData(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ConfigProto.class, rescuecore2.messages.protobuf.RCRSProto.ConfigProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.ConfigProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + internalGetMutableData().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ConfigProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto build() { + rescuecore2.messages.protobuf.RCRSProto.ConfigProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.ConfigProto result = new rescuecore2.messages.protobuf.RCRSProto.ConfigProto(this); + int from_bitField0_ = bitField0_; + result.data_ = internalGetData(); + result.data_.makeImmutable(); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.ConfigProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ConfigProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.ConfigProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.ConfigProto.getDefaultInstance()) return this; + internalGetMutableData().mergeFrom( + other.internalGetData()); + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.ConfigProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.ConfigProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> data_; + private com.google.protobuf.MapField<java.lang.String, java.lang.String> + internalGetData() { + if (data_ == null) { + return com.google.protobuf.MapField.emptyMapField( + DataDefaultEntryHolder.defaultEntry); + } + return data_; + } + private com.google.protobuf.MapField<java.lang.String, java.lang.String> + internalGetMutableData() { + onChanged();; + if (data_ == null) { + data_ = com.google.protobuf.MapField.newMapField( + DataDefaultEntryHolder.defaultEntry); + } + if (!data_.isMutable()) { + data_ = data_.copy(); + } + return data_; + } + + public int getDataCount() { + return internalGetData().getMap().size(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + + @java.lang.Override + public boolean containsData( + java.lang.String key) { + if (key == null) { throw new java.lang.NullPointerException(); } + return internalGetData().getMap().containsKey(key); + } + /** + * Use {@link #getDataMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map<java.lang.String, java.lang.String> getData() { + return getDataMap(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.util.Map<java.lang.String, java.lang.String> getDataMap() { + return internalGetData().getMap(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.lang.String getDataOrDefault( + java.lang.String key, + java.lang.String defaultValue) { + if (key == null) { throw new java.lang.NullPointerException(); } + java.util.Map<java.lang.String, java.lang.String> map = + internalGetData().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + * <code>map<string, string> data = 1;</code> + */ + @java.lang.Override + + public java.lang.String getDataOrThrow( + java.lang.String key) { + if (key == null) { throw new java.lang.NullPointerException(); } + java.util.Map<java.lang.String, java.lang.String> map = + internalGetData().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + public Builder clearData() { + internalGetMutableData().getMutableMap() + .clear(); + return this; + } + /** + * <code>map<string, string> data = 1;</code> + */ + + public Builder removeData( + java.lang.String key) { + if (key == null) { throw new java.lang.NullPointerException(); } + internalGetMutableData().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map<java.lang.String, java.lang.String> + getMutableData() { + return internalGetMutableData().getMutableMap(); + } + /** + * <code>map<string, string> data = 1;</code> + */ + public Builder putData( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new java.lang.NullPointerException(); } + if (value == null) { throw new java.lang.NullPointerException(); } + internalGetMutableData().getMutableMap() + .put(key, value); + return this; + } + /** + * <code>map<string, string> data = 1;</code> + */ + + public Builder putAllData( + java.util.Map<java.lang.String, java.lang.String> values) { + internalGetMutableData().getMutableMap() + .putAll(values); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:ConfigProto) + } + + // @@protoc_insertion_point(class_scope:ConfigProto) + private static final rescuecore2.messages.protobuf.RCRSProto.ConfigProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.ConfigProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.ConfigProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ConfigProto> + PARSER = new com.google.protobuf.AbstractParser<ConfigProto>() { + @java.lang.Override + public ConfigProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ConfigProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<ConfigProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ConfigProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ConfigProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface EdgeListProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:EdgeListProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto> + getEdgesList(); + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeProto getEdges(int index); + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + int getEdgesCount(); + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder> + getEdgesOrBuilderList(); + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder getEdgesOrBuilder( + int index); + } + /** + * Protobuf type {@code EdgeListProto} + */ + public static final class EdgeListProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:EdgeListProto) + EdgeListProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EdgeListProto.newBuilder() to construct. + private EdgeListProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EdgeListProto() { + edges_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EdgeListProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EdgeListProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + edges_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EdgeProto>(); + mutable_bitField0_ |= 0x00000001; + } + edges_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.EdgeProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + edges_ = java.util.Collections.unmodifiableList(edges_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.class, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder.class); + } + + public static final int EDGES_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto> edges_; + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto> getEdgesList() { + return edges_; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder> + getEdgesOrBuilderList() { + return edges_; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + @java.lang.Override + public int getEdgesCount() { + return edges_.size(); + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto getEdges(int index) { + return edges_.get(index); + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder getEdgesOrBuilder( + int index) { + return edges_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < edges_.size(); i++) { + output.writeMessage(1, edges_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < edges_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, edges_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.EdgeListProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto other = (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) obj; + + if (!getEdgesList() + .equals(other.getEdgesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getEdgesCount() > 0) { + hash = (37 * hash) + EDGES_FIELD_NUMBER; + hash = (53 * hash) + getEdgesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code EdgeListProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:EdgeListProto) + rescuecore2.messages.protobuf.RCRSProto.EdgeListProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeListProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeListProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.class, rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getEdgesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (edgesBuilder_ == null) { + edges_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + edgesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeListProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto build() { + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto result = new rescuecore2.messages.protobuf.RCRSProto.EdgeListProto(this); + int from_bitField0_ = bitField0_; + if (edgesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + edges_ = java.util.Collections.unmodifiableList(edges_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.edges_ = edges_; + } else { + result.edges_ = edgesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EdgeListProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.EdgeListProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.EdgeListProto.getDefaultInstance()) return this; + if (edgesBuilder_ == null) { + if (!other.edges_.isEmpty()) { + if (edges_.isEmpty()) { + edges_ = other.edges_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureEdgesIsMutable(); + edges_.addAll(other.edges_); + } + onChanged(); + } + } else { + if (!other.edges_.isEmpty()) { + if (edgesBuilder_.isEmpty()) { + edgesBuilder_.dispose(); + edgesBuilder_ = null; + edges_ = other.edges_; + bitField0_ = (bitField0_ & ~0x00000001); + edgesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getEdgesFieldBuilder() : null; + } else { + edgesBuilder_.addAllMessages(other.edges_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.EdgeListProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.EdgeListProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto> edges_ = + java.util.Collections.emptyList(); + private void ensureEdgesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + edges_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.EdgeProto>(edges_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeProto, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder> edgesBuilder_; + + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto> getEdgesList() { + if (edgesBuilder_ == null) { + return java.util.Collections.unmodifiableList(edges_); + } else { + return edgesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public int getEdgesCount() { + if (edgesBuilder_ == null) { + return edges_.size(); + } else { + return edgesBuilder_.getCount(); + } + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto getEdges(int index) { + if (edgesBuilder_ == null) { + return edges_.get(index); + } else { + return edgesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder setEdges( + int index, rescuecore2.messages.protobuf.RCRSProto.EdgeProto value) { + if (edgesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEdgesIsMutable(); + edges_.set(index, value); + onChanged(); + } else { + edgesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder setEdges( + int index, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder builderForValue) { + if (edgesBuilder_ == null) { + ensureEdgesIsMutable(); + edges_.set(index, builderForValue.build()); + onChanged(); + } else { + edgesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder addEdges(rescuecore2.messages.protobuf.RCRSProto.EdgeProto value) { + if (edgesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEdgesIsMutable(); + edges_.add(value); + onChanged(); + } else { + edgesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder addEdges( + int index, rescuecore2.messages.protobuf.RCRSProto.EdgeProto value) { + if (edgesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureEdgesIsMutable(); + edges_.add(index, value); + onChanged(); + } else { + edgesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder addEdges( + rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder builderForValue) { + if (edgesBuilder_ == null) { + ensureEdgesIsMutable(); + edges_.add(builderForValue.build()); + onChanged(); + } else { + edgesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder addEdges( + int index, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder builderForValue) { + if (edgesBuilder_ == null) { + ensureEdgesIsMutable(); + edges_.add(index, builderForValue.build()); + onChanged(); + } else { + edgesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder addAllEdges( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.EdgeProto> values) { + if (edgesBuilder_ == null) { + ensureEdgesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, edges_); + onChanged(); + } else { + edgesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder clearEdges() { + if (edgesBuilder_ == null) { + edges_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + edgesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public Builder removeEdges(int index) { + if (edgesBuilder_ == null) { + ensureEdgesIsMutable(); + edges_.remove(index); + onChanged(); + } else { + edgesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder getEdgesBuilder( + int index) { + return getEdgesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder getEdgesOrBuilder( + int index) { + if (edgesBuilder_ == null) { + return edges_.get(index); } else { + return edgesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder> + getEdgesOrBuilderList() { + if (edgesBuilder_ != null) { + return edgesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(edges_); + } + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder addEdgesBuilder() { + return getEdgesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.EdgeProto.getDefaultInstance()); + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder addEdgesBuilder( + int index) { + return getEdgesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.getDefaultInstance()); + } + /** + * <code>repeated .EdgeProto edges = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder> + getEdgesBuilderList() { + return getEdgesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeProto, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder> + getEdgesFieldBuilder() { + if (edgesBuilder_ == null) { + edgesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.EdgeProto, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder>( + edges_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + edges_ = null; + } + return edgesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:EdgeListProto) + } + + // @@protoc_insertion_point(class_scope:EdgeListProto) + private static final rescuecore2.messages.protobuf.RCRSProto.EdgeListProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.EdgeListProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EdgeListProto> + PARSER = new com.google.protobuf.AbstractParser<EdgeListProto>() { + @java.lang.Override + public EdgeListProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EdgeListProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EdgeListProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EdgeListProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeListProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface EdgeProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:EdgeProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 startX = 1;</code> + * @return The startX. + */ + int getStartX(); + + /** + * <code>int32 startY = 2;</code> + * @return The startY. + */ + int getStartY(); + + /** + * <code>int32 endX = 3;</code> + * @return The endX. + */ + int getEndX(); + + /** + * <code>int32 endY = 4;</code> + * @return The endY. + */ + int getEndY(); + + /** + * <code>int32 neighbour = 5;</code> + * @return The neighbour. + */ + int getNeighbour(); + } + /** + * Protobuf type {@code EdgeProto} + */ + public static final class EdgeProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:EdgeProto) + EdgeProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EdgeProto.newBuilder() to construct. + private EdgeProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EdgeProto() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EdgeProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EdgeProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + startX_ = input.readInt32(); + break; + } + case 16: { + + startY_ = input.readInt32(); + break; + } + case 24: { + + endX_ = input.readInt32(); + break; + } + case 32: { + + endY_ = input.readInt32(); + break; + } + case 40: { + + neighbour_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EdgeProto.class, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder.class); + } + + public static final int STARTX_FIELD_NUMBER = 1; + private int startX_; + /** + * <code>int32 startX = 1;</code> + * @return The startX. + */ + @java.lang.Override + public int getStartX() { + return startX_; + } + + public static final int STARTY_FIELD_NUMBER = 2; + private int startY_; + /** + * <code>int32 startY = 2;</code> + * @return The startY. + */ + @java.lang.Override + public int getStartY() { + return startY_; + } + + public static final int ENDX_FIELD_NUMBER = 3; + private int endX_; + /** + * <code>int32 endX = 3;</code> + * @return The endX. + */ + @java.lang.Override + public int getEndX() { + return endX_; + } + + public static final int ENDY_FIELD_NUMBER = 4; + private int endY_; + /** + * <code>int32 endY = 4;</code> + * @return The endY. + */ + @java.lang.Override + public int getEndY() { + return endY_; + } + + public static final int NEIGHBOUR_FIELD_NUMBER = 5; + private int neighbour_; + /** + * <code>int32 neighbour = 5;</code> + * @return The neighbour. + */ + @java.lang.Override + public int getNeighbour() { + return neighbour_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (startX_ != 0) { + output.writeInt32(1, startX_); + } + if (startY_ != 0) { + output.writeInt32(2, startY_); + } + if (endX_ != 0) { + output.writeInt32(3, endX_); + } + if (endY_ != 0) { + output.writeInt32(4, endY_); + } + if (neighbour_ != 0) { + output.writeInt32(5, neighbour_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (startX_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, startX_); + } + if (startY_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, startY_); + } + if (endX_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, endX_); + } + if (endY_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, endY_); + } + if (neighbour_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(5, neighbour_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.EdgeProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.EdgeProto other = (rescuecore2.messages.protobuf.RCRSProto.EdgeProto) obj; + + if (getStartX() + != other.getStartX()) return false; + if (getStartY() + != other.getStartY()) return false; + if (getEndX() + != other.getEndX()) return false; + if (getEndY() + != other.getEndY()) return false; + if (getNeighbour() + != other.getNeighbour()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + STARTX_FIELD_NUMBER; + hash = (53 * hash) + getStartX(); + hash = (37 * hash) + STARTY_FIELD_NUMBER; + hash = (53 * hash) + getStartY(); + hash = (37 * hash) + ENDX_FIELD_NUMBER; + hash = (53 * hash) + getEndX(); + hash = (37 * hash) + ENDY_FIELD_NUMBER; + hash = (53 * hash) + getEndY(); + hash = (37 * hash) + NEIGHBOUR_FIELD_NUMBER; + hash = (53 * hash) + getNeighbour(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.EdgeProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code EdgeProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:EdgeProto) + rescuecore2.messages.protobuf.RCRSProto.EdgeProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.EdgeProto.class, rescuecore2.messages.protobuf.RCRSProto.EdgeProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.EdgeProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + startX_ = 0; + + startY_ = 0; + + endX_ = 0; + + endY_ = 0; + + neighbour_ = 0; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_EdgeProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.EdgeProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto build() { + rescuecore2.messages.protobuf.RCRSProto.EdgeProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.EdgeProto result = new rescuecore2.messages.protobuf.RCRSProto.EdgeProto(this); + result.startX_ = startX_; + result.startY_ = startY_; + result.endX_ = endX_; + result.endY_ = endY_; + result.neighbour_ = neighbour_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.EdgeProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.EdgeProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.EdgeProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.EdgeProto.getDefaultInstance()) return this; + if (other.getStartX() != 0) { + setStartX(other.getStartX()); + } + if (other.getStartY() != 0) { + setStartY(other.getStartY()); + } + if (other.getEndX() != 0) { + setEndX(other.getEndX()); + } + if (other.getEndY() != 0) { + setEndY(other.getEndY()); + } + if (other.getNeighbour() != 0) { + setNeighbour(other.getNeighbour()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.EdgeProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.EdgeProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int startX_ ; + /** + * <code>int32 startX = 1;</code> + * @return The startX. + */ + @java.lang.Override + public int getStartX() { + return startX_; + } + /** + * <code>int32 startX = 1;</code> + * @param value The startX to set. + * @return This builder for chaining. + */ + public Builder setStartX(int value) { + + startX_ = value; + onChanged(); + return this; + } + /** + * <code>int32 startX = 1;</code> + * @return This builder for chaining. + */ + public Builder clearStartX() { + + startX_ = 0; + onChanged(); + return this; + } + + private int startY_ ; + /** + * <code>int32 startY = 2;</code> + * @return The startY. + */ + @java.lang.Override + public int getStartY() { + return startY_; + } + /** + * <code>int32 startY = 2;</code> + * @param value The startY to set. + * @return This builder for chaining. + */ + public Builder setStartY(int value) { + + startY_ = value; + onChanged(); + return this; + } + /** + * <code>int32 startY = 2;</code> + * @return This builder for chaining. + */ + public Builder clearStartY() { + + startY_ = 0; + onChanged(); + return this; + } + + private int endX_ ; + /** + * <code>int32 endX = 3;</code> + * @return The endX. + */ + @java.lang.Override + public int getEndX() { + return endX_; + } + /** + * <code>int32 endX = 3;</code> + * @param value The endX to set. + * @return This builder for chaining. + */ + public Builder setEndX(int value) { + + endX_ = value; + onChanged(); + return this; + } + /** + * <code>int32 endX = 3;</code> + * @return This builder for chaining. + */ + public Builder clearEndX() { + + endX_ = 0; + onChanged(); + return this; + } + + private int endY_ ; + /** + * <code>int32 endY = 4;</code> + * @return The endY. + */ + @java.lang.Override + public int getEndY() { + return endY_; + } + /** + * <code>int32 endY = 4;</code> + * @param value The endY to set. + * @return This builder for chaining. + */ + public Builder setEndY(int value) { + + endY_ = value; + onChanged(); + return this; + } + /** + * <code>int32 endY = 4;</code> + * @return This builder for chaining. + */ + public Builder clearEndY() { + + endY_ = 0; + onChanged(); + return this; + } + + private int neighbour_ ; + /** + * <code>int32 neighbour = 5;</code> + * @return The neighbour. + */ + @java.lang.Override + public int getNeighbour() { + return neighbour_; + } + /** + * <code>int32 neighbour = 5;</code> + * @param value The neighbour to set. + * @return This builder for chaining. + */ + public Builder setNeighbour(int value) { + + neighbour_ = value; + onChanged(); + return this; + } + /** + * <code>int32 neighbour = 5;</code> + * @return This builder for chaining. + */ + public Builder clearNeighbour() { + + neighbour_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:EdgeProto) + } + + // @@protoc_insertion_point(class_scope:EdgeProto) + private static final rescuecore2.messages.protobuf.RCRSProto.EdgeProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.EdgeProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.EdgeProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EdgeProto> + PARSER = new com.google.protobuf.AbstractParser<EdgeProto>() { + @java.lang.Override + public EdgeProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EdgeProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EdgeProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EdgeProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.EdgeProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public interface ChangeSetProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:ChangeSetProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> + getChangesList(); + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getChanges(int index); + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + int getChangesCount(); + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder> + getChangesOrBuilderList(); + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder getChangesOrBuilder( + int index); + + /** + * <code>repeated int32 deletes = 2;</code> + * @return A list containing the deletes. + */ + java.util.List<java.lang.Integer> getDeletesList(); + /** + * <code>repeated int32 deletes = 2;</code> + * @return The count of deletes. + */ + int getDeletesCount(); + /** + * <code>repeated int32 deletes = 2;</code> + * @param index The index of the element to return. + * @return The deletes at the given index. + */ + int getDeletes(int index); + } + /** + * Protobuf type {@code ChangeSetProto} + */ + public static final class ChangeSetProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:ChangeSetProto) + ChangeSetProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use ChangeSetProto.newBuilder() to construct. + private ChangeSetProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private ChangeSetProto() { + changes_ = java.util.Collections.emptyList(); + deletes_ = emptyIntList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ChangeSetProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ChangeSetProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + changes_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto>(); + mutable_bitField0_ |= 0x00000001; + } + changes_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.parser(), extensionRegistry)); + break; + } + case 16: { + if (!((mutable_bitField0_ & 0x00000002) != 0)) { + deletes_ = newIntList(); + mutable_bitField0_ |= 0x00000002; + } + deletes_.addInt(input.readInt32()); + break; + } + case 18: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00000002) != 0) && input.getBytesUntilLimit() > 0) { + deletes_ = newIntList(); + mutable_bitField0_ |= 0x00000002; + } + while (input.getBytesUntilLimit() > 0) { + deletes_.addInt(input.readInt32()); + } + input.popLimit(limit); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + changes_ = java.util.Collections.unmodifiableList(changes_); + } + if (((mutable_bitField0_ & 0x00000002) != 0)) { + deletes_.makeImmutable(); // C + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.class, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder.class); + } + + public interface EntityChangeProtoOrBuilder extends + // @@protoc_insertion_point(interface_extends:ChangeSetProto.EntityChangeProto) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>int32 entityID = 1;</code> + * @return The entityID. + */ + int getEntityID(); + + /** + * <code>int32 urn = 2;</code> + * @return The urn. + */ + int getUrn(); + + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> + getPropertiesList(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + int getPropertiesCount(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList(); + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index); + } + /** + * Protobuf type {@code ChangeSetProto.EntityChangeProto} + */ + public static final class EntityChangeProto extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:ChangeSetProto.EntityChangeProto) + EntityChangeProtoOrBuilder { + private static final long serialVersionUID = 0L; + // Use EntityChangeProto.newBuilder() to construct. + private EntityChangeProto(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) { + super(builder); + } + private EntityChangeProto() { + properties_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new EntityChangeProto(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private EntityChangeProto( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + + entityID_ = input.readInt32(); + break; + } + case 16: { + + urn_ = input.readInt32(); + break; + } + case 26: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + properties_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.PropertyProto>(); + mutable_bitField0_ |= 0x00000001; + } + properties_.add( + input.readMessage(rescuecore2.messages.protobuf.RCRSProto.PropertyProto.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + properties_ = java.util.Collections.unmodifiableList(properties_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_EntityChangeProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_EntityChangeProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.class, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder.class); + } + + public static final int ENTITYID_FIELD_NUMBER = 1; + private int entityID_; + /** + * <code>int32 entityID = 1;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + + public static final int URN_FIELD_NUMBER = 2; + private int urn_; + /** + * <code>int32 urn = 2;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + + public static final int PROPERTIES_FIELD_NUMBER = 3; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> properties_; + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> getPropertiesList() { + return properties_; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList() { + return properties_; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public int getPropertiesCount() { + return properties_.size(); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index) { + return properties_.get(index); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index) { + return properties_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (entityID_ != 0) { + output.writeInt32(1, entityID_); + } + if (urn_ != 0) { + output.writeInt32(2, urn_); + } + for (int i = 0; i < properties_.size(); i++) { + output.writeMessage(3, properties_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (entityID_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, entityID_); + } + if (urn_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, urn_); + } + for (int i = 0; i < properties_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, properties_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto other = (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto) obj; + + if (getEntityID() + != other.getEntityID()) return false; + if (getUrn() + != other.getUrn()) return false; + if (!getPropertiesList() + .equals(other.getPropertiesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ENTITYID_FIELD_NUMBER; + hash = (53 * hash) + getEntityID(); + hash = (37 * hash) + URN_FIELD_NUMBER; + hash = (53 * hash) + getUrn(); + if (getPropertiesCount() > 0) { + hash = (37 * hash) + PROPERTIES_FIELD_NUMBER; + hash = (53 * hash) + getPropertiesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ChangeSetProto.EntityChangeProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:ChangeSetProto.EntityChangeProto) + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_EntityChangeProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_EntityChangeProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.class, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getPropertiesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + entityID_ = 0; + + urn_ = 0; + + if (propertiesBuilder_ == null) { + properties_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + propertiesBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_EntityChangeProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto build() { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto result = new rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto(this); + int from_bitField0_ = bitField0_; + result.entityID_ = entityID_; + result.urn_ = urn_; + if (propertiesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + properties_ = java.util.Collections.unmodifiableList(properties_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.properties_ = properties_; + } else { + result.properties_ = propertiesBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.getDefaultInstance()) return this; + if (other.getEntityID() != 0) { + setEntityID(other.getEntityID()); + } + if (other.getUrn() != 0) { + setUrn(other.getUrn()); + } + if (propertiesBuilder_ == null) { + if (!other.properties_.isEmpty()) { + if (properties_.isEmpty()) { + properties_ = other.properties_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensurePropertiesIsMutable(); + properties_.addAll(other.properties_); + } + onChanged(); + } + } else { + if (!other.properties_.isEmpty()) { + if (propertiesBuilder_.isEmpty()) { + propertiesBuilder_.dispose(); + propertiesBuilder_ = null; + properties_ = other.properties_; + bitField0_ = (bitField0_ & ~0x00000001); + propertiesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getPropertiesFieldBuilder() : null; + } else { + propertiesBuilder_.addAllMessages(other.properties_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int entityID_ ; + /** + * <code>int32 entityID = 1;</code> + * @return The entityID. + */ + @java.lang.Override + public int getEntityID() { + return entityID_; + } + /** + * <code>int32 entityID = 1;</code> + * @param value The entityID to set. + * @return This builder for chaining. + */ + public Builder setEntityID(int value) { + + entityID_ = value; + onChanged(); + return this; + } + /** + * <code>int32 entityID = 1;</code> + * @return This builder for chaining. + */ + public Builder clearEntityID() { + + entityID_ = 0; + onChanged(); + return this; + } + + private int urn_ ; + /** + * <code>int32 urn = 2;</code> + * @return The urn. + */ + @java.lang.Override + public int getUrn() { + return urn_; + } + /** + * <code>int32 urn = 2;</code> + * @param value The urn to set. + * @return This builder for chaining. + */ + public Builder setUrn(int value) { + + urn_ = value; + onChanged(); + return this; + } + /** + * <code>int32 urn = 2;</code> + * @return This builder for chaining. + */ + public Builder clearUrn() { + + urn_ = 0; + onChanged(); + return this; + } + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> properties_ = + java.util.Collections.emptyList(); + private void ensurePropertiesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + properties_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.PropertyProto>(properties_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> propertiesBuilder_; + + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto> getPropertiesList() { + if (propertiesBuilder_ == null) { + return java.util.Collections.unmodifiableList(properties_); + } else { + return propertiesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public int getPropertiesCount() { + if (propertiesBuilder_ == null) { + return properties_.size(); + } else { + return propertiesBuilder_.getCount(); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto getProperties(int index) { + if (propertiesBuilder_ == null) { + return properties_.get(index); + } else { + return propertiesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder setProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.set(index, value); + onChanged(); + } else { + propertiesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder setProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.set(index, builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties(rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.add(value); + onChanged(); + } else { + propertiesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto value) { + if (propertiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePropertiesIsMutable(); + properties_.add(index, value); + onChanged(); + } else { + propertiesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.add(builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addProperties( + int index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder builderForValue) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.add(index, builderForValue.build()); + onChanged(); + } else { + propertiesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder addAllProperties( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProto> values) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, properties_); + onChanged(); + } else { + propertiesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder clearProperties() { + if (propertiesBuilder_ == null) { + properties_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + propertiesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public Builder removeProperties(int index) { + if (propertiesBuilder_ == null) { + ensurePropertiesIsMutable(); + properties_.remove(index); + onChanged(); + } else { + propertiesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder getPropertiesBuilder( + int index) { + return getPropertiesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder getPropertiesOrBuilder( + int index) { + if (propertiesBuilder_ == null) { + return properties_.get(index); } else { + return propertiesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesOrBuilderList() { + if (propertiesBuilder_ != null) { + return propertiesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(properties_); + } + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder addPropertiesBuilder() { + return getPropertiesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance()); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder addPropertiesBuilder( + int index) { + return getPropertiesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.getDefaultInstance()); + } + /** + * <code>repeated .PropertyProto properties = 3;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder> + getPropertiesBuilderList() { + return getPropertiesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder> + getPropertiesFieldBuilder() { + if (propertiesBuilder_ == null) { + propertiesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.PropertyProto, rescuecore2.messages.protobuf.RCRSProto.PropertyProto.Builder, rescuecore2.messages.protobuf.RCRSProto.PropertyProtoOrBuilder>( + properties_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + properties_ = null; + } + return propertiesBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:ChangeSetProto.EntityChangeProto) + } + + // @@protoc_insertion_point(class_scope:ChangeSetProto.EntityChangeProto) + private static final rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<EntityChangeProto> + PARSER = new com.google.protobuf.AbstractParser<EntityChangeProto>() { + @java.lang.Override + public EntityChangeProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new EntityChangeProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<EntityChangeProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<EntityChangeProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + public static final int CHANGES_FIELD_NUMBER = 1; + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> changes_; + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + @java.lang.Override + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> getChangesList() { + return changes_; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + @java.lang.Override + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder> + getChangesOrBuilderList() { + return changes_; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + @java.lang.Override + public int getChangesCount() { + return changes_.size(); + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getChanges(int index) { + return changes_.get(index); + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder getChangesOrBuilder( + int index) { + return changes_.get(index); + } + + public static final int DELETES_FIELD_NUMBER = 2; + private com.google.protobuf.Internal.IntList deletes_; + /** + * <code>repeated int32 deletes = 2;</code> + * @return A list containing the deletes. + */ + @java.lang.Override + public java.util.List<java.lang.Integer> + getDeletesList() { + return deletes_; + } + /** + * <code>repeated int32 deletes = 2;</code> + * @return The count of deletes. + */ + public int getDeletesCount() { + return deletes_.size(); + } + /** + * <code>repeated int32 deletes = 2;</code> + * @param index The index of the element to return. + * @return The deletes at the given index. + */ + public int getDeletes(int index) { + return deletes_.getInt(index); + } + private int deletesMemoizedSerializedSize = -1; + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < changes_.size(); i++) { + output.writeMessage(1, changes_.get(i)); + } + if (getDeletesList().size() > 0) { + output.writeUInt32NoTag(18); + output.writeUInt32NoTag(deletesMemoizedSerializedSize); + } + for (int i = 0; i < deletes_.size(); i++) { + output.writeInt32NoTag(deletes_.getInt(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < changes_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, changes_.get(i)); + } + { + int dataSize = 0; + for (int i = 0; i < deletes_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(deletes_.getInt(i)); + } + size += dataSize; + if (!getDeletesList().isEmpty()) { + size += 1; + size += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(dataSize); + } + deletesMemoizedSerializedSize = dataSize; + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto)) { + return super.equals(obj); + } + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto other = (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) obj; + + if (!getChangesList() + .equals(other.getChangesList())) return false; + if (!getDeletesList() + .equals(other.getDeletesList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getChangesCount() > 0) { + hash = (37 * hash) + CHANGES_FIELD_NUMBER; + hash = (53 * hash) + getChangesList().hashCode(); + } + if (getDeletesCount() > 0) { + hash = (37 * hash) + DELETES_FIELD_NUMBER; + hash = (53 * hash) + getDeletesList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code ChangeSetProto} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:ChangeSetProto) + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProtoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_fieldAccessorTable + .ensureFieldAccessorsInitialized( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.class, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.Builder.class); + } + + // Construct using rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getChangesFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + if (changesBuilder_ == null) { + changes_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + changesBuilder_.clear(); + } + deletes_ = emptyIntList(); + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return rescuecore2.messages.protobuf.RCRSProto.internal_static_ChangeSetProto_descriptor; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getDefaultInstanceForType() { + return rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance(); + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto build() { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto buildPartial() { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto result = new rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto(this); + int from_bitField0_ = bitField0_; + if (changesBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + changes_ = java.util.Collections.unmodifiableList(changes_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.changes_ = changes_; + } else { + result.changes_ = changesBuilder_.build(); + } + if (((bitField0_ & 0x00000002) != 0)) { + deletes_.makeImmutable(); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.deletes_ = deletes_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) { + return mergeFrom((rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto other) { + if (other == rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.getDefaultInstance()) return this; + if (changesBuilder_ == null) { + if (!other.changes_.isEmpty()) { + if (changes_.isEmpty()) { + changes_ = other.changes_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureChangesIsMutable(); + changes_.addAll(other.changes_); + } + onChanged(); + } + } else { + if (!other.changes_.isEmpty()) { + if (changesBuilder_.isEmpty()) { + changesBuilder_.dispose(); + changesBuilder_ = null; + changes_ = other.changes_; + bitField0_ = (bitField0_ & ~0x00000001); + changesBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getChangesFieldBuilder() : null; + } else { + changesBuilder_.addAllMessages(other.changes_); + } + } + } + if (!other.deletes_.isEmpty()) { + if (deletes_.isEmpty()) { + deletes_ = other.deletes_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureDeletesIsMutable(); + deletes_.addAll(other.deletes_); + } + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> changes_ = + java.util.Collections.emptyList(); + private void ensureChangesIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + changes_ = new java.util.ArrayList<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto>(changes_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder> changesBuilder_; + + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> getChangesList() { + if (changesBuilder_ == null) { + return java.util.Collections.unmodifiableList(changes_); + } else { + return changesBuilder_.getMessageList(); + } + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public int getChangesCount() { + if (changesBuilder_ == null) { + return changes_.size(); + } else { + return changesBuilder_.getCount(); + } + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto getChanges(int index) { + if (changesBuilder_ == null) { + return changes_.get(index); + } else { + return changesBuilder_.getMessage(index); + } + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder setChanges( + int index, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto value) { + if (changesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureChangesIsMutable(); + changes_.set(index, value); + onChanged(); + } else { + changesBuilder_.setMessage(index, value); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder setChanges( + int index, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder builderForValue) { + if (changesBuilder_ == null) { + ensureChangesIsMutable(); + changes_.set(index, builderForValue.build()); + onChanged(); + } else { + changesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder addChanges(rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto value) { + if (changesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureChangesIsMutable(); + changes_.add(value); + onChanged(); + } else { + changesBuilder_.addMessage(value); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder addChanges( + int index, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto value) { + if (changesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureChangesIsMutable(); + changes_.add(index, value); + onChanged(); + } else { + changesBuilder_.addMessage(index, value); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder addChanges( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder builderForValue) { + if (changesBuilder_ == null) { + ensureChangesIsMutable(); + changes_.add(builderForValue.build()); + onChanged(); + } else { + changesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder addChanges( + int index, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder builderForValue) { + if (changesBuilder_ == null) { + ensureChangesIsMutable(); + changes_.add(index, builderForValue.build()); + onChanged(); + } else { + changesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder addAllChanges( + java.lang.Iterable<? extends rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto> values) { + if (changesBuilder_ == null) { + ensureChangesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, changes_); + onChanged(); + } else { + changesBuilder_.addAllMessages(values); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder clearChanges() { + if (changesBuilder_ == null) { + changes_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + changesBuilder_.clear(); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public Builder removeChanges(int index) { + if (changesBuilder_ == null) { + ensureChangesIsMutable(); + changes_.remove(index); + onChanged(); + } else { + changesBuilder_.remove(index); + } + return this; + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder getChangesBuilder( + int index) { + return getChangesFieldBuilder().getBuilder(index); + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder getChangesOrBuilder( + int index) { + if (changesBuilder_ == null) { + return changes_.get(index); } else { + return changesBuilder_.getMessageOrBuilder(index); + } + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public java.util.List<? extends rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder> + getChangesOrBuilderList() { + if (changesBuilder_ != null) { + return changesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(changes_); + } + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder addChangesBuilder() { + return getChangesFieldBuilder().addBuilder( + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.getDefaultInstance()); + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder addChangesBuilder( + int index) { + return getChangesFieldBuilder().addBuilder( + index, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.getDefaultInstance()); + } + /** + * <code>repeated .ChangeSetProto.EntityChangeProto changes = 1;</code> + */ + public java.util.List<rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder> + getChangesBuilderList() { + return getChangesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder> + getChangesFieldBuilder() { + if (changesBuilder_ == null) { + changesBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto.Builder, rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProtoOrBuilder>( + changes_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + changes_ = null; + } + return changesBuilder_; + } + + private com.google.protobuf.Internal.IntList deletes_ = emptyIntList(); + private void ensureDeletesIsMutable() { + if (!((bitField0_ & 0x00000002) != 0)) { + deletes_ = mutableCopy(deletes_); + bitField0_ |= 0x00000002; + } + } + /** + * <code>repeated int32 deletes = 2;</code> + * @return A list containing the deletes. + */ + public java.util.List<java.lang.Integer> + getDeletesList() { + return ((bitField0_ & 0x00000002) != 0) ? + java.util.Collections.unmodifiableList(deletes_) : deletes_; + } + /** + * <code>repeated int32 deletes = 2;</code> + * @return The count of deletes. + */ + public int getDeletesCount() { + return deletes_.size(); + } + /** + * <code>repeated int32 deletes = 2;</code> + * @param index The index of the element to return. + * @return The deletes at the given index. + */ + public int getDeletes(int index) { + return deletes_.getInt(index); + } + /** + * <code>repeated int32 deletes = 2;</code> + * @param index The index to set the value at. + * @param value The deletes to set. + * @return This builder for chaining. + */ + public Builder setDeletes( + int index, int value) { + ensureDeletesIsMutable(); + deletes_.setInt(index, value); + onChanged(); + return this; + } + /** + * <code>repeated int32 deletes = 2;</code> + * @param value The deletes to add. + * @return This builder for chaining. + */ + public Builder addDeletes(int value) { + ensureDeletesIsMutable(); + deletes_.addInt(value); + onChanged(); + return this; + } + /** + * <code>repeated int32 deletes = 2;</code> + * @param values The deletes to add. + * @return This builder for chaining. + */ + public Builder addAllDeletes( + java.lang.Iterable<? extends java.lang.Integer> values) { + ensureDeletesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, deletes_); + onChanged(); + return this; + } + /** + * <code>repeated int32 deletes = 2;</code> + * @return This builder for chaining. + */ + public Builder clearDeletes() { + deletes_ = emptyIntList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:ChangeSetProto) + } + + // @@protoc_insertion_point(class_scope:ChangeSetProto) + private static final rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto(); + } + + public static rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser<ChangeSetProto> + PARSER = new com.google.protobuf.AbstractParser<ChangeSetProto>() { + @java.lang.Override + public ChangeSetProto parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ChangeSetProto(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser<ChangeSetProto> parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser<ChangeSetProto> getParserForType() { + return PARSER; + } + + @java.lang.Override + public rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_MessageProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_MessageProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_MessageProto_ComponentsEntry_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_MessageProto_ComponentsEntry_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_MessageListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_MessageListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_MessageComponentProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_MessageComponentProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_StrListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_StrListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_IntListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_IntListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_FloatListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_FloatListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_IntMatrixProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_IntMatrixProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ValueProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ValueProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_PropertyProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_PropertyProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_Point2DProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_Point2DProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_EntityProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_EntityProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_EntityListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_EntityListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ConfigProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ConfigProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ConfigProto_DataEntry_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ConfigProto_DataEntry_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_EdgeListProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_EdgeListProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_EdgeProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_EdgeProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ChangeSetProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ChangeSetProto_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_ChangeSetProto_EntityChangeProto_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_ChangeSetProto_EntityChangeProto_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\017RCRSProto.proto\"\231\001\n\014MessageProto\022\013\n\003ur" + + "n\030\001 \001(\005\0221\n\ncomponents\030\002 \003(\0132\035.MessagePro" + + "to.ComponentsEntry\032I\n\017ComponentsEntry\022\013\n" + + "\003key\030\001 \001(\005\022%\n\005value\030\002 \001(\0132\026.MessageCompo" + + "nentProto:\0028\001\"3\n\020MessageListProto\022\037\n\010com" + + "mands\030\001 \003(\0132\r.MessageProto\"\302\003\n\025MessageCo" + + "mponentProto\022$\n\tchangeSet\030\001 \001(\0132\017.Change" + + "SetProtoH\000\022(\n\013commandList\030\002 \001(\0132\021.Messag" + + "eListProtoH\000\022\036\n\006config\030\003 \001(\0132\014.ConfigPro" + + "toH\000\022\036\n\006entity\030\004 \001(\0132\014.EntityProtoH\000\022\022\n\010" + + "entityID\030\005 \001(\005H\000\022%\n\014entityIDList\030\006 \001(\0132\r" + + ".IntListProtoH\000\022&\n\nentityList\030\007 \001(\0132\020.En" + + "tityListProtoH\000\022$\n\tfloatList\030\010 \001(\0132\017.Flo" + + "atListProtoH\000\022\022\n\010intValue\030\t \001(\005H\000\022 \n\007int" + + "List\030\n \001(\0132\r.IntListProtoH\000\022\021\n\007rawData\030\013" + + " \001(\014H\000\022\025\n\013stringValue\030\014 \001(\tH\000\022#\n\nstringL" + + "ist\030\r \001(\0132\r.StrListProtoH\000B\013\n\tcomponent\"" + + "\036\n\014StrListProto\022\016\n\006values\030\001 \003(\t\"\036\n\014IntLi" + + "stProto\022\016\n\006values\030\001 \003(\005\" \n\016FloatListProt" + + "o\022\016\n\006values\030\001 \003(\002\"/\n\016IntMatrixProto\022\035\n\006v" + + "alues\030\001 \003(\0132\r.IntListProto\"\210\002\n\nValueProt" + + "o\022\017\n\007defined\030\001 \001(\010\022\022\n\010intValue\030\002 \001(\005H\000\022\023" + + "\n\tboolValue\030\003 \001(\010H\000\022\025\n\013doubleValue\030\004 \001(\001" + + "H\000\022\022\n\010byteList\030\005 \001(\014H\000\022 \n\007intList\030\006 \001(\0132" + + "\r.IntListProtoH\000\022$\n\tintMatrix\030\007 \001(\0132\017.In" + + "tMatrixProtoH\000\022\"\n\010edgeList\030\010 \001(\0132\016.EdgeL" + + "istProtoH\000\022 \n\007point2D\030\t \001(\0132\r.Point2DPro" + + "toH\000B\007\n\005value\"\230\002\n\rPropertyProto\022\013\n\003urn\030\001" + + " \001(\005\022\017\n\007defined\030\002 \001(\010\022\022\n\010intValue\030\003 \001(\005H" + + "\000\022\023\n\tboolValue\030\004 \001(\010H\000\022\025\n\013doubleValue\030\005 " + + "\001(\001H\000\022\022\n\010byteList\030\006 \001(\014H\000\022 \n\007intList\030\007 \001" + + "(\0132\r.IntListProtoH\000\022$\n\tintMatrix\030\010 \001(\0132\017" + + ".IntMatrixProtoH\000\022\"\n\010edgeList\030\t \001(\0132\016.Ed" + + "geListProtoH\000\022 \n\007point2D\030\n \001(\0132\r.Point2D" + + "ProtoH\000B\007\n\005value\"$\n\014Point2DProto\022\t\n\001X\030\001 " + + "\001(\001\022\t\n\001Y\030\002 \001(\001\"P\n\013EntityProto\022\013\n\003urn\030\001 \001" + + "(\005\022\020\n\010entityID\030\002 \001(\005\022\"\n\nproperties\030\003 \003(\013" + + "2\016.PropertyProto\"1\n\017EntityListProto\022\036\n\010e" + + "ntities\030\001 \003(\0132\014.EntityProto\"`\n\013ConfigPro" + + "to\022$\n\004data\030\001 \003(\0132\026.ConfigProto.DataEntry" + + "\032+\n\tDataEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(" + + "\t:\0028\001\"*\n\rEdgeListProto\022\031\n\005edges\030\001 \003(\0132\n." + + "EdgeProto\"Z\n\tEdgeProto\022\016\n\006startX\030\001 \001(\005\022\016" + + "\n\006startY\030\002 \001(\005\022\014\n\004endX\030\003 \001(\005\022\014\n\004endY\030\004 \001" + + "(\005\022\021\n\tneighbour\030\005 \001(\005\"\255\001\n\016ChangeSetProto" + + "\0222\n\007changes\030\001 \003(\0132!.ChangeSetProto.Entit" + + "yChangeProto\022\017\n\007deletes\030\002 \003(\005\032V\n\021EntityC" + + "hangeProto\022\020\n\010entityID\030\001 \001(\005\022\013\n\003urn\030\002 \001(" + + "\005\022\"\n\nproperties\030\003 \003(\0132\016.PropertyProtoB*\n" + + "\035rescuecore2.messages.protobufB\tRCRSProt" + + "ob\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_MessageProto_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_MessageProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_MessageProto_descriptor, + new java.lang.String[] { "Urn", "Components", }); + internal_static_MessageProto_ComponentsEntry_descriptor = + internal_static_MessageProto_descriptor.getNestedTypes().get(0); + internal_static_MessageProto_ComponentsEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_MessageProto_ComponentsEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_MessageListProto_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_MessageListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_MessageListProto_descriptor, + new java.lang.String[] { "Commands", }); + internal_static_MessageComponentProto_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_MessageComponentProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_MessageComponentProto_descriptor, + new java.lang.String[] { "ChangeSet", "CommandList", "Config", "Entity", "EntityID", "EntityIDList", "EntityList", "FloatList", "IntValue", "IntList", "RawData", "StringValue", "StringList", "Component", }); + internal_static_StrListProto_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_StrListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_StrListProto_descriptor, + new java.lang.String[] { "Values", }); + internal_static_IntListProto_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_IntListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_IntListProto_descriptor, + new java.lang.String[] { "Values", }); + internal_static_FloatListProto_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_FloatListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_FloatListProto_descriptor, + new java.lang.String[] { "Values", }); + internal_static_IntMatrixProto_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_IntMatrixProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_IntMatrixProto_descriptor, + new java.lang.String[] { "Values", }); + internal_static_ValueProto_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_ValueProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ValueProto_descriptor, + new java.lang.String[] { "Defined", "IntValue", "BoolValue", "DoubleValue", "ByteList", "IntList", "IntMatrix", "EdgeList", "Point2D", "Value", }); + internal_static_PropertyProto_descriptor = + getDescriptor().getMessageTypes().get(8); + internal_static_PropertyProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_PropertyProto_descriptor, + new java.lang.String[] { "Urn", "Defined", "IntValue", "BoolValue", "DoubleValue", "ByteList", "IntList", "IntMatrix", "EdgeList", "Point2D", "Value", }); + internal_static_Point2DProto_descriptor = + getDescriptor().getMessageTypes().get(9); + internal_static_Point2DProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_Point2DProto_descriptor, + new java.lang.String[] { "X", "Y", }); + internal_static_EntityProto_descriptor = + getDescriptor().getMessageTypes().get(10); + internal_static_EntityProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_EntityProto_descriptor, + new java.lang.String[] { "Urn", "EntityID", "Properties", }); + internal_static_EntityListProto_descriptor = + getDescriptor().getMessageTypes().get(11); + internal_static_EntityListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_EntityListProto_descriptor, + new java.lang.String[] { "Entities", }); + internal_static_ConfigProto_descriptor = + getDescriptor().getMessageTypes().get(12); + internal_static_ConfigProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ConfigProto_descriptor, + new java.lang.String[] { "Data", }); + internal_static_ConfigProto_DataEntry_descriptor = + internal_static_ConfigProto_descriptor.getNestedTypes().get(0); + internal_static_ConfigProto_DataEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ConfigProto_DataEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_EdgeListProto_descriptor = + getDescriptor().getMessageTypes().get(13); + internal_static_EdgeListProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_EdgeListProto_descriptor, + new java.lang.String[] { "Edges", }); + internal_static_EdgeProto_descriptor = + getDescriptor().getMessageTypes().get(14); + internal_static_EdgeProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_EdgeProto_descriptor, + new java.lang.String[] { "StartX", "StartY", "EndX", "EndY", "Neighbour", }); + internal_static_ChangeSetProto_descriptor = + getDescriptor().getMessageTypes().get(15); + internal_static_ChangeSetProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ChangeSetProto_descriptor, + new java.lang.String[] { "Changes", "Deletes", }); + internal_static_ChangeSetProto_EntityChangeProto_descriptor = + internal_static_ChangeSetProto_descriptor.getNestedTypes().get(0); + internal_static_ChangeSetProto_EntityChangeProto_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_ChangeSetProto_EntityChangeProto_descriptor, + new java.lang.String[] { "EntityID", "Urn", "Properties", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.proto b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.proto new file mode 100644 index 0000000000000000000000000000000000000000..52bc65f842c489c55c576cb54385236ec8f38aab --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/messages/protobuf/RCRSProto.proto @@ -0,0 +1,117 @@ +syntax = "proto3"; + +option java_package = "rescuecore2.messages.protobuf"; +option java_outer_classname = "RCRSProto"; + +message MessageProto { + int32 urn = 1; + map<int32, MessageComponentProto> components = 2; +}; + +message MessageListProto { + repeated MessageProto commands = 1; +}; + +message MessageComponentProto { + oneof component { + ChangeSetProto changeSet = 1; + MessageListProto commandList = 2; + ConfigProto config = 3; + EntityProto entity = 4; + int32 entityID = 5; + IntListProto entityIDList = 6; + EntityListProto entityList = 7; + FloatListProto floatList = 8; + int32 intValue = 9; + IntListProto intList = 10; + bytes rawData = 11; + string stringValue = 12; + StrListProto stringList = 13; + } +}; + +message StrListProto { + repeated string values = 1; +}; + +message IntListProto { + repeated int32 values = 1; +}; + +message FloatListProto { + repeated float values = 1; +}; + +message IntMatrixProto { + repeated IntListProto values = 1; +}; + +message ValueProto { + bool defined = 1; + oneof value { + int32 intValue = 2; + bool boolValue = 3; + double doubleValue = 4; + bytes byteList = 5; + IntListProto intList = 6; + IntMatrixProto intMatrix = 7; + EdgeListProto edgeList = 8; + Point2DProto point2D = 9; + } +}; + +message PropertyProto { + int32 urn = 1; + bool defined = 2; + oneof value { + int32 intValue = 3; + bool boolValue = 4; + double doubleValue = 5; + bytes byteList = 6; + IntListProto intList = 7; + IntMatrixProto intMatrix = 8; + EdgeListProto edgeList = 9; + Point2DProto point2D = 10; + } +}; + +message Point2DProto { + double X = 1; + double Y = 2; +}; + +message EntityProto { + int32 urn = 1; + int32 entityID = 2; + repeated PropertyProto properties = 3; +}; + +message EntityListProto { + repeated EntityProto entities = 1; +}; + +message ConfigProto { + map<string, string> data = 1; +}; + +message EdgeListProto { + repeated EdgeProto edges = 1; +}; + +message EdgeProto { + int32 startX = 1; + int32 startY = 2; + int32 endX = 3; + int32 endY = 4; + int32 neighbour = 5; +}; + +message ChangeSetProto { + message EntityChangeProto { + int32 entityID = 1; + int32 urn = 2; + repeated PropertyProto properties = 3; + }; + repeated EntityChangeProto changes = 1; + repeated int32 deletes = 2; +}; \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/CommandLineOptions.java b/modules/rescuecore2/src/rescuecore2/misc/CommandLineOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..1787fab88c74d3a445c9d0af6f4f6a57c64b1b73 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/CommandLineOptions.java @@ -0,0 +1,64 @@ +package rescuecore2.misc; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.Constants; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.io.File; +import java.io.IOException; + +/** + A utility class for processing command line options. + */ +public final class CommandLineOptions { + /** The command-line flag for specifying a config file. */ + public static final String CONFIG_FLAG = "-c"; + + /** The command-line flag for specifying the kernel host. */ + public static final String HOST_FLAG = "-h"; + + /** The command-line flag for specifying the kernel port. */ + public static final String PORT_FLAG = "-p"; + + private CommandLineOptions() {} + + /** + Process a set of command line arguments. Config files ("-c" options) will be read and individual config entries ("--x=y" options) will be processed. Unrecognised arguments will be returned in order. + @param args The command line options. + @param config A Config to populate. + @return All unprocessed options. This will not be null. + @throws IOException If there is a problem reading a config file. + @throws ConfigException If there is a problem processing a config file. + */ + public static String[] processArgs(String[] args, Config config) throws IOException, ConfigException { + List<String> all = Arrays.asList(args); + List<String> result = new ArrayList<String>(); + Iterator<String> it = all.iterator(); + while (it.hasNext()) { + String next = it.next(); + if (CONFIG_FLAG.equals(next)) { + config.read(new File(it.next())); + } + else if (HOST_FLAG.equals(next)) { + config.setValue(Constants.KERNEL_HOST_NAME_KEY, it.next()); + } + else if (PORT_FLAG.equals(next)) { + config.setValue(Constants.KERNEL_PORT_NUMBER_KEY, it.next()); + } + else if (next.startsWith("--") && next.indexOf("=") != -1) { + int index = next.indexOf("="); + String key = next.substring(2, index); + String value = next.substring(index + 1); + config.setValue(key, value); + } + else { + result.add(next); + } + } + return result.toArray(new String[0]); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/CountingInputStream.java b/modules/rescuecore2/src/rescuecore2/misc/CountingInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..2e597bca5033a4e2701521bae546a83b8aa116fb --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/CountingInputStream.java @@ -0,0 +1,41 @@ +package rescuecore2.misc; + +import java.io.InputStream; +import java.io.IOException; + +/** + An InputStream that keeps track of how many bytes have been read. +*/ +public class CountingInputStream extends InputStream { + private int count; + private InputStream downstream; + + /** + Construct a CountingInputStream that reads from another stream. + @param downstream The downstream InputStream to read from. + */ + public CountingInputStream(InputStream downstream) { + this.downstream = downstream; + count = 0; + } + + @Override + public int read() throws IOException { + int result = downstream.read(); + ++count; + return result; + } + + @Override + public String toString() { + return downstream + " at position " + count; + } + + /** + Get the number of bytes read so far. + @return The number of bytes read. + */ + public int getByteCount() { + return count; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/EncodingTools.java b/modules/rescuecore2/src/rescuecore2/misc/EncodingTools.java new file mode 100644 index 0000000000000000000000000000000000000000..852afa2ddbec444944ab993090378ac22a826091 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/EncodingTools.java @@ -0,0 +1,877 @@ +package rescuecore2.misc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +import rescuecore2.messages.Message; +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +/** + * A bunch of useful tools for encoding and decoding things like integers. + */ +public final class EncodingTools { + /** The size of an INT_32 in bytes. */ + public static final int INT_32_SIZE = 4; + + /** Charset for encoding/decoding strings. Should always be UTF-8 */ + private static final Charset CHARSET = Charset.forName("UTF-8"); + + /** + * Private constructor: this is a utility class. + */ + private EncodingTools() { + } + + /** + * Turn off the checkstyle test for magic numbers since we use a lot of them + * here + */ + /** CHECKSTYLE:OFF:MagicNumber */ + + /** + * Write a 32-bit integer to an OutputStream, big-endian style. + * + * @param i The integer to write. + * @param out The OutputStream to write it to. + * @throws IOException If the OutputStream blows up. + */ + public static void writeInt32(int i, OutputStream out) throws IOException { + // Most significant byte first + out.write((byte) (i >> 24) & 0xFF); + out.write((byte) (i >> 16) & 0xFF); + out.write((byte) (i >> 8) & 0xFF); + out.write((byte) i & 0xFF); + } + + /** + * Write a 32-bit integer to a DataOutput, big-endian style. + * + * @param i The integer to write. + * @param out The DataOutput to write it to. + * @throws IOException If the DataOutput blows up. + */ + public static void writeInt32(int i, DataOutput out) throws IOException { + // DataOutput writes big-endian + out.write(i); + } + + /** + * Write a 32-bit integer to a byte array, big-endian style. + * + * @param i The integer to write. + * @param out The buffer to write it to. + * @param offset Where in the buffer to write it. + */ + public static void writeInt32(int i, byte[] out, int offset) { + // Most significant byte first + out[offset] = (byte) ((i >> 24) & 0xFF); + out[offset + 1] = (byte) ((i >> 16) & 0xFF); + out[offset + 2] = (byte) ((i >> 8) & 0xFF); + out[offset + 3] = (byte) (i & 0xFF); + } + + /** + * Read a 32-bit integer from an input stream, big-endian style. + * + * @param in The InputStream to read from. + * @return The next big-endian, 32-bit integer in the stream. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static int readInt32(InputStream in) throws IOException { + int first = in.read(); + if (first == -1) { + throw new EOFException("Broken input pipe. Read 0 bytes of 4."); + } + int second = in.read(); + if (second == -1) { + throw new EOFException("Broken input pipe. Read 1 bytes of 4."); + } + int third = in.read(); + if (third == -1) { + throw new EOFException("Broken input pipe. Read 2 bytes of 4."); + } + int fourth = in.read(); + if (fourth == -1) { + throw new EOFException("Broken input pipe. Read 3 bytes of 4."); + } + return (first << 24) | (second << 16) | (third << 8) | fourth; + } + + /** + * Read a 32-bit integer from a DataInput. + * + * @param in The DataInput to read from. + * @return The next big-endian, 32-bit integer in the stream. + * @throws IOException If the DataInput blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static int readInt32(DataInput in) throws IOException { + return in.readInt(); + } + + /** + * Read a 32-bit integer from an input stream, little-endian style. + * + * @param in The InputStream to read from. + * @return The next little-endian, 32-bit integer in the stream. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static int readInt32LE(InputStream in) throws IOException { + int first = in.read(); + if (first == -1) { + throw new EOFException("Broken input pipe. Read 0 bytes of 4."); + } + int second = in.read(); + if (second == -1) { + throw new EOFException("Broken input pipe. Read 1 bytes of 4."); + } + int third = in.read(); + if (third == -1) { + throw new EOFException("Broken input pipe. Read 2 bytes of 4."); + } + int fourth = in.read(); + if (fourth == -1) { + throw new EOFException("Broken input pipe. Read 3 bytes of 4."); + } + return (fourth << 24) | (third << 16) | (second << 8) | first; + } + + /** + * Read a 32-bit integer from a byte array, big-endian style. + * + * @param in The buffer to read from. + * @param offset Where to begin reading. + * @return The next big-endian, 32-bit integer in the buffer. + */ + public static int readInt32(byte[] in, int offset) { + return (in[offset] << 24) | (in[offset + 1] << 16) | (in[offset + 2] << 8) | (in[offset + 3]); + } + + /** + * Read a 32-bit integer from a byte array, big-endian style. This is equivalent + * to calling {@link #readInt32(byte[], int) readInt32(in, 0)}. + * + * @param in The buffer to read from. + * @return The first big-endian, 32-bit integer in the buffer. + */ + public static int readInt32(byte[] in) { + return readInt32(in, 0); + } + + /** + * Write a String to an OutputStream. Strings are always in UTF-8. + * + * @param s The String to write. + * @param out The OutputStream to write to. + * @throws IOException If the OutputStream blows up. + */ + public static void writeString(String s, OutputStream out) throws IOException { + byte[] bytes = s.getBytes(CHARSET); + writeInt32(bytes.length, out); + out.write(bytes); + } + + /** + * Write a String to a DataOutput. Strings are always in UTF-8. + * + * @param s The String to write. + * @param out The DataOutput to write to. + * @throws IOException If the DataOutput blows up. + */ + public static void writeString(String s, DataOutput out) throws IOException { + byte[] bytes = s.getBytes(CHARSET); + writeInt32(bytes.length, out); + out.write(bytes); + } + + /** + * Write a String to a byte array. Strings are always in UTF-8. + * + * @param s The String to write. + * @param out The byte array to write to. Make sure it's big enough! + * @param offset The index to start writing from. + */ + public static void writeString(String s, byte[] out, int offset) { + byte[] bytes = s.getBytes(CHARSET); + writeInt32(bytes.length, out, offset); + System.arraycopy(bytes, 0, out, offset + 4, bytes.length); + } + + /** + * Read a String from an InputStream. + * + * @param in The InputStream to read. + * @return The string that was read. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static String readString(InputStream in) throws IOException { + int length = readInt32(in); + byte[] buffer = new byte[length]; + int count = 0; + while (count < length) { + int read = in.read(buffer, count, length - count); + if (read == -1) { + throw new EOFException("Broken input pipe. Read " + count + " bytes of " + length + "."); + } + count += read; + } + return new String(buffer, CHARSET); + } + + /** + * Read a String from a DataInput. + * + * @param in The DataInput to read. + * @return The string that was read. + * @throws IOException If the DataInput blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static String readString(DataInput in) throws IOException { + int length = readInt32(in); + byte[] buffer = new byte[length]; + in.readFully(buffer); + return new String(buffer, CHARSET); + } + + /** + * Read a String from a byte array. This is equivalent to calling + * {@link #readString(byte[], int) readString(in, 0)}. + * + * @param in The byte array to read. + * @return The string that was read. + */ + public static String readString(byte[] in) { + return readString(in, 0); + } + + /** + * Read a String from a byte array. + * + * @param in The byte array to read. + * @param offset The index in the array to read from. + * @return The string that was read. + */ + public static String readString(byte[] in, int offset) { + int length = readInt32(in, offset); + byte[] buffer = new byte[length]; + System.arraycopy(in, offset + 4, buffer, 0, length); + return new String(buffer, CHARSET); + } + + /** + * Read a fixed number of bytes from an InputStream into an array. + * + * @param size The number of bytes to read. + * @param in The InputStream to read from. + * @return A new byte array containing the bytes. + * @throws IOException If the read operation fails. + */ + public static byte[] readBytes(int size, InputStream in) throws IOException { + byte[] buffer = new byte[size]; + int total = 0; + while (total < size) { + int read = in.read(buffer, total, size - total); + if (read == -1) { + throw new EOFException("Broken input pipe. Read " + total + " bytes of " + size + "."); + } + total += read; + } + return buffer; + } + + /** + * Read a fixed number of bytes from a DataInput into an array. + * + * @param size The number of bytes to read. + * @param in The DataInput to read from. + * @return A new byte array containing the bytes. + * @throws IOException If the read operation fails. + */ + public static byte[] readBytes(int size, DataInput in) throws IOException { + byte[] buffer = new byte[size]; + in.readFully(buffer); + return buffer; + } + + /** + * Write a double to an OutputStream. + * + * @param d The double to write. + * @param out The OutputStream to write it to. + * @throws IOException If the OutputStream blows up. + */ + public static void writeDouble(double d, OutputStream out) throws IOException { + long bits = Double.doubleToLongBits(d); + out.write((byte) (bits >> 56) & 0xFF); + out.write((byte) (bits >> 48) & 0xFF); + out.write((byte) (bits >> 40) & 0xFF); + out.write((byte) (bits >> 32) & 0xFF); + out.write((byte) (bits >> 24) & 0xFF); + out.write((byte) (bits >> 16) & 0xFF); + out.write((byte) (bits >> 8) & 0xFF); + out.write((byte) bits & 0xFF); + } + + /** + * Write a double to a DataOutput. + * + * @param d The double to write. + * @param out The DataOutput to write it to. + * @throws IOException If the DataOutput blows up. + */ + public static void writeDouble(double d, DataOutput out) throws IOException { + out.writeDouble(d); + } + + /** + * Write a double to a byte array. + * + * @param d The double to write. + * @param out The buffer to write it to. + * @param offset Where in the buffer to write it. + */ + public static void writeDouble(double d, byte[] out, int offset) { + long bits = Double.doubleToLongBits(d); + out[offset + 0] = (byte) ((bits >> 56) & 0xFF); + out[offset + 1] = (byte) ((bits >> 48) & 0xFF); + out[offset + 2] = (byte) ((bits >> 40) & 0xFF); + out[offset + 3] = (byte) ((bits >> 32) & 0xFF); + out[offset + 4] = (byte) ((bits >> 24) & 0xFF); + out[offset + 5] = (byte) ((bits >> 16) & 0xFF); + out[offset + 6] = (byte) ((bits >> 8) & 0xFF); + out[offset + 7] = (byte) (bits & 0xFF); + } + + /** + * Read a double from an input stream. + * + * @param in The InputStream to read from. + * @return The next double in the stream. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static double readDouble(InputStream in) throws IOException { + long[] data = new long[8]; + for (int i = 0; i < data.length; ++i) { + data[i] = in.read(); + if (data[i] == -1) { + throw new EOFException("Broken input pipe. Read " + i + " bytes of 8."); + } + } + long result = data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 + | data[6] << 8 | data[7]; + return Double.longBitsToDouble(result); + } + + /** + * Read a double from a DataInput. + * + * @param in The DataInput to read from. + * @return The next double in the stream. + * @throws IOException If the DataInput blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static double readDouble(DataInput in) throws IOException { + return in.readDouble(); + } + + /** + * Read a double from a byte array. + * + * @param in The buffer to read from. + * @param offset Where to begin reading. + * @return The next double in the buffer. + */ + public static double readDouble(byte[] in, int offset) { + long[] parts = new long[8]; + for (int i = 0; i < 8; ++i) { + parts[i] = in[offset + i]; + } + long result = parts[0] << 56 | parts[1] << 48 | parts[2] << 40 | parts[3] << 32 | parts[4] << 24 | parts[5] << 16 + | parts[6] << 8 | parts[7]; + return Double.longBitsToDouble(result); + } + + /** + * Read a double from a byte array. This is equivalent to calling + * {@link #readDouble(byte[], int) readDouble(in, 0)}. + * + * @param in The buffer to read from. + * @return The first double in the buffer. + */ + public static double readDouble(byte[] in) { + return readDouble(in, 0); + } + + /** + * Write a boolean to an OutputStream. + * + * @param b The boolean to write. + * @param out The OutputStream to write it to. + * @throws IOException If the OutputStream blows up. + */ + public static void writeBoolean(boolean b, OutputStream out) throws IOException { + out.write(b ? 1 : 0); + } + + /** + * Write a boolean to a DataOutput. + * + * @param b The boolean to write. + * @param out The DataOutput to write it to. + * @throws IOException If the DataOutput blows up. + */ + public static void writeBoolean(boolean b, DataOutput out) throws IOException { + out.writeBoolean(b); + } + + /** + * Write a boolean to a byte array. + * + * @param b The boolean to write. + * @param out The buffer to write it to. + * @param offset Where in the buffer to write it. + */ + public static void writeBoolean(boolean b, byte[] out, int offset) { + out[offset] = (byte) (b ? 1 : 0); + } + + /** + * Read a boolean from an input stream. + * + * @param in The InputStream to read from. + * @return The next boolean in the stream. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static boolean readBoolean(InputStream in) throws IOException { + int b = in.read(); + return b == 1; + } + + /** + * Read a boolean from a DataInput. + * + * @param in The DataInput to read from. + * @return The next boolean in the stream. + * @throws IOException If the DataInput blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static boolean readBoolean(DataInput in) throws IOException { + return in.readBoolean(); + } + + /** + * Read a boolean from a byte array. + * + * @param in The buffer to read from. + * @param offset Where to begin reading. + * @return The next boolean in the buffer. + */ + public static boolean readBoolean(byte[] in, int offset) { + return in[offset] == 1; + } + + /** + * Read a boolean from a byte array. This is equivalent to calling + * {@link #readBoolean(byte[], int) readBoolean(in, 0)}. + * + * @param in The buffer to read from. + * @return The first boolean in the buffer. + */ + public static boolean readBoolean(byte[] in) { + return readBoolean(in, 0); + } + + /** + * Call InputStream.skip until exactly <code>count</code> bytes have been + * skipped. If InputStream.skip ever returns a negative number then an + * EOFException is thrown. + * + * @param in The InputStream to skip. + * @param count The number of bytes to skip. + * @throws IOException If the bytes cannot be skipped for some reason. + */ + public static void reallySkip(InputStream in, long count) throws IOException { + long done = 0; + while (done < count) { + long next = in.skip(count - done); + if (next < 0) { + throw new EOFException(); + } + done += next; + } + } + + /** + * Call DataInput.skip until exactly <code>count</code> bytes have been skipped. + * If DataInput.skip ever returns a negative number then an EOFException is + * thrown. + * + * @param in The DataInput to skip. + * @param count The number of bytes to skip. + * @throws IOException If the bytes cannot be skipped for some reason. + */ + public static void reallySkip(DataInput in, int count) throws IOException { + int done = 0; + while (done < count) { + int next = in.skipBytes(count - done); + if (next < 0) { + throw new EOFException(); + } + done += next; + } + } + + /** + * Write an entity to a stream. + * + * @param e The entity to write. + * @param out The OutputStream to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeEntity(Entity e, OutputStream out) throws IOException { + // Type URN, entityID, size, content + // Gather the content first + ByteArrayOutputStream gather = new ByteArrayOutputStream(); + e.write(gather); + byte[] bytes = gather.toByteArray(); + + // Type URN + writeString(Registry.getCurrentRegistry().toURN_Str(e.getURN()), out); + // EntityID + writeInt32(e.getID().getValue(), out); + // Size + writeInt32(bytes.length, out); + // Content + out.write(bytes); + } + + /** + * Write an entity to a DataOutput. + * + * @param e The entity to write. + * @param out The DataOutput to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeEntity(Entity e, DataOutput out) throws IOException { + // Type URN, entityID, size, content + // Gather the content first + ByteArrayOutputStream gather = new ByteArrayOutputStream(); + e.write(gather); + byte[] bytes = gather.toByteArray(); + + // Type URN + writeString(Registry.getCurrentRegistry().toURN_Str(e.getURN()), out); + // EntityID + writeInt32(e.getID().getValue(), out); + // Size + writeInt32(bytes.length, out); + // Content + out.write(bytes); + } + + /** + * Read an entity from a stream. + * + * @param in The InputStream to read from. + * @return A new Entity, or null if the entity URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Entity readEntity(InputStream in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + int entityID = readInt32(in); + int size = readInt32(in); + byte[] content = readBytes(size, in); + Entity result = Registry.getCurrentRegistry().createEntity(Registry.getCurrentRegistry().toURN_Id(urn), + new EntityID(entityID)); + if (result != null) { + result.read(new ByteArrayInputStream(content)); + } + return result; + } + + /** + * Read an entity from a DataInput. + * + * @param in The DataInput to read from. + * @return A new Entity, or null if the entity URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Entity readEntity(DataInput in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + int entityID = readInt32(in); + int size = readInt32(in); + byte[] content = readBytes(size, in); + Entity result = Registry.getCurrentRegistry().createEntity(Registry.getCurrentRegistry().toURN_Id(urn), + new EntityID(entityID)); + if (result != null) { + result.read(new ByteArrayInputStream(content)); + } + return result; + } + + /** + * Write a property to a stream. + * + * @param p The property to write. + * @param out The OutputStream to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeProperty(Property p, OutputStream out) throws IOException { + // Type + writeString(Registry.getCurrentRegistry().toURN_Str(p.getURN()), out); + writeBoolean(p.isDefined(), out); + if (p.isDefined()) { + ByteArrayOutputStream gather = new ByteArrayOutputStream(); + p.write(gather); + byte[] bytes = gather.toByteArray(); + // Size + writeInt32(bytes.length, out); + // Data + out.write(bytes); + } + } + + /** + * Write a property to a DataOutput. + * + * @param p The property to write. + * @param out The DataOutput to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeProperty(Property p, DataOutput out) throws IOException { + // Type + writeString(Registry.getCurrentRegistry().toURN_Str(p.getURN()), out); + writeBoolean(p.isDefined(), out); + if (p.isDefined()) { + ByteArrayOutputStream gather = new ByteArrayOutputStream(); + p.write(gather); + byte[] bytes = gather.toByteArray(); + // Size + writeInt32(bytes.length, out); + // Data + out.write(bytes); + } + } + + /** + * Read a property from a stream. + * + * @param in The InputStream to read from. + * @return A new Property, or null if the property URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Property readProperty(InputStream in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + boolean defined = readBoolean(in); + Property result = Registry.getCurrentRegistry().createProperty(Registry.getCurrentRegistry().toURN_Id(urn)); + if (defined) { + int size = readInt32(in); + byte[] content = readBytes(size, in); + if (result != null) { + result.read(new ByteArrayInputStream(content)); + } + } + return result; + } + + /** + * Read a property from a DataInput. + * + * @param in The DataInput to read from. + * @return A new Property, or null if the property URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Property readProperty(DataInput in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + boolean defined = readBoolean(in); + Property result = Registry.getCurrentRegistry().createProperty(Registry.getCurrentRegistry().toURN_Id(urn)); + if (defined) { + int size = readInt32(in); + byte[] content = readBytes(size, in); + if (result != null) { + result.read(new ByteArrayInputStream(content)); + } + } + return result; + } + + /** + * Write a message to a stream. + * + * @param m The message to write. + * @param out The OutputStream to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeMessage(Message m, OutputStream out) throws IOException { + // Type URN, size, content + // Gather the content first + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + m.write(bytes); + byte[] content = bytes.toByteArray(); + + // Type URN + writeString(Registry.getCurrentRegistry().toURN_Str(m.getURN()), out); + // Size + writeInt32(content.length, out); + // Content + out.write(content); + } + + /** + * Write a message to a DataOutput. + * + * @param m The message to write. + * @param out The DataOutput to write to. + * @throws IOException If there is a problem writing to the stream. + */ + public static void writeMessage(Message m, DataOutput out) throws IOException { + // Type URN, size, content + // Gather the content first + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + m.write(bytes); + byte[] content = bytes.toByteArray(); + + // Type URN + writeString(Registry.getCurrentRegistry().toURN_Str(m.getURN()), out); + // Size + writeInt32(content.length, out); + // Content + out.write(content); + } + + /** + * Read a message from a stream. + * + * @param in The InputStream to read from. + * @return A new Message, or null if the message URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Message readMessage(InputStream in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + int size = readInt32(in); + byte[] content = readBytes(size, in); + Message result = Registry.getCurrentRegistry().createMessage(Registry.getCurrentRegistry().toURN_Id(urn), + new ByteArrayInputStream(content)); + return result; + } + + /** + * Read a message from a DataInput. + * + * @param in The DataInput to read from. + * @return A new Message, or null if the message URN is not recognised. + * @throws IOException If there is a problem reading from the stream. + */ + public static Message readMessage(DataInput in) throws IOException { + String urn = readString(in); + if ("".equals(urn)) { + return null; + } + int size = readInt32(in); + byte[] content = readBytes(size, in); + Message result = Registry.getCurrentRegistry().createMessage(Registry.getCurrentRegistry().toURN_Id(urn), + new ByteArrayInputStream(content)); + return result; + } + + /** + * Read a 32-bit float from an input stream, big-endian style. + * + * @param in The InputStream to read from. + * @return The next big-endian, 32-bit float in the stream. + * @throws IOException If the InputStream blows up. + * @throws EOFException If the end of the stream is reached. + */ + public static float readFloat32(InputStream in) throws IOException { + int i = readInt32(in); + return Float.intBitsToFloat(i); + } + + /** + * Write a 32-bit float to an OutputStream, big-endian style. + * + * @param f The float to write. + * @param out The OutputStream to write it to. + * @throws IOException If the OutputStream blows up. + */ + public static void writeFloat32(float f, OutputStream out) throws IOException { + /* Aftershock Requirement:2013 */ + int i = Float.floatToIntBits(f); + writeInt32(i, out); + } + + /** + * Write a 32-bit float to a DataOutput, big-endian style. + * + * @param f The float to write. + * @param out The DataOutput to write it to. + * @throws IOException If the DataOutput blows up. + */ + public static void writeFloat32(float f, DataOutput out) throws IOException { + /* Aftershock Requirement:2013 */ + // DataOutput writes big-endian + out.write(Float.floatToIntBits(f)); + } + + /** + * Write a 32-bit float to a byte array, big-endian style. + * + * @param f The float to write. + * @param out The buffer to write it to. + * @param offset Where in the buffer to write it. + */ + public static void writeFloat32(float f, byte[] out, int offset) { + /* Aftershock Requirement:2013 */ + // Most significant byte first + int i = Float.floatToIntBits(f); + writeInt32(i, out, offset); + + } + + /** + * Read a 32-bit float from a DataInput. + * + * @param in The DataInput to read from. + * @return The next big-endian, 32-bit float in the stream. + * @throws IOException If the DataInput blows up. + * @throws EOFException If the end of the stream is reached. + */ + + public static float readFloat32(DataInput in) throws IOException { + /* Aftershock Requirement:2013 */ + return Float.intBitsToFloat(in.readInt()); + } + + /** CHECKSTYLE:ON:MagicNumber */ + +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/EntityTools.java b/modules/rescuecore2/src/rescuecore2/misc/EntityTools.java new file mode 100644 index 0000000000000000000000000000000000000000..bb1046dcb9bdbd5c1c135ab6dcd6ea7669e573f9 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/EntityTools.java @@ -0,0 +1,63 @@ +package rescuecore2.misc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.Property; + +/** + * A bunch of useful tools for entities. + */ +public final class EntityTools { + /** Utility class; private constructor. */ + private EntityTools() { + } + + /** + * Copy relevant properties from one entity to another. + * + * @param from The entity to copy property values from. + * @param to The entity to copy property values to. + */ + public static void copyProperties(Entity from, Entity to) { + for (Property next : from.getProperties()) { + Property p = to.getProperty(next.getURN()); + if (p != null) { + p.takeValue(next); + } + } + } + + /** + * Sort a collection of entities by ID and return a sorted list. + * + * @param input The collection to sort. + * @param <T> A subtype of Entity. + * @return A sorted list. If the input is already a list then it will be sorted + * in place. + */ + public static <T extends Entity> List<T> sortedList(Collection<T> input) { + List<T> result; + if (input instanceof List) { + result = (List<T>) input; + } else { + result = new ArrayList<T>(input); + } + Collections.sort(result, new IDComparator()); + return result; + } + + /** + * Comparator that sorts entities by ID. + */ + public static class IDComparator implements Comparator<Entity>, java.io.Serializable { + @Override + public int compare(Entity e1, Entity e2) { + return e1.getID().getValue() - e2.getID().getValue(); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/Handy.java b/modules/rescuecore2/src/rescuecore2/misc/Handy.java new file mode 100644 index 0000000000000000000000000000000000000000..6b7b94e87706b1f0725ea71aefa17420fe37bded --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/Handy.java @@ -0,0 +1,29 @@ +package rescuecore2.misc; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +import java.util.Collection; +import java.util.Set; +import java.util.HashSet; + +/** + Handy utility functions. +*/ +public final class Handy { + private Handy() { + } + + /** + Turn a collection of Entities into a collection of EntityIDs. + @param entities The Entities to convert. + @return A new set of EntityID objects. + */ + public static Set<EntityID> objectsToIDs(Collection<? extends Entity> entities) { + Set<EntityID> result = new HashSet<EntityID>(); + for (Entity next : entities) { + result.add(next.getID()); + } + return result; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/MutableBoolean.java b/modules/rescuecore2/src/rescuecore2/misc/MutableBoolean.java new file mode 100644 index 0000000000000000000000000000000000000000..bb7a645092cd994340ad82404ac9dbd00c425626 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/MutableBoolean.java @@ -0,0 +1,32 @@ +package rescuecore2.misc; + +/** + A mutable boolean value. +*/ +public class MutableBoolean { + private boolean b; + + /** + Create a mutable boolean value. + @param b The initial value. + */ + public MutableBoolean(boolean b) { + this.b = b; + } + + /** + Set the value of this object. + @param value The new value. + */ + public void set(boolean value) { + b = value; + } + + /** + Get the value of this object. + @return The value. + */ + public boolean get() { + return b; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/Pair.java b/modules/rescuecore2/src/rescuecore2/misc/Pair.java new file mode 100644 index 0000000000000000000000000000000000000000..1fb099808f847aa0414479f98918cdc8d02f59e1 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/Pair.java @@ -0,0 +1,75 @@ +package rescuecore2.misc; + +/** + Utility class for a 2-tuple, often known as a pair. + @param <S> The type of the first element of the pair. + @param <T> The type of the second element of the pair. + */ +public class Pair<S, T> { + private S first; + private T second; + + /** + Construct a pair object. + @param first The first element. + @param second The second element. + */ + public Pair(S first, T second) { + this.first = first; + this.second = second; + } + + /** + Get the first element of this pair. + @return The first element. + */ + public S first() { + return first; + } + + /** + Get the second element of this pair. + @return The second element. + */ + public T second() { + return second; + } + + @Override + public String toString() { + return "<" + first + ", " + second + ">"; + } + + @Override + public int hashCode() { + return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()); + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(Object o) { + if (o instanceof Pair) { + Pair<S, T> p = (Pair<S, T>)o; + if (this.first == null && p.first != null) { + return false; + } + if (this.first != null && p.first == null) { + return false; + } + if (this.second == null && p.second != null) { + return false; + } + if (this.second != null && p.second == null) { + return false; + } + if (this.first != null && p.first != null && !this.first.equals(p.first)) { + return false; + } + if (this.second != null && p.second != null && !this.second.equals(p.second)) { + return false; + } + return true; + } + return false; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/RoundingNumberGenerator.java b/modules/rescuecore2/src/rescuecore2/misc/RoundingNumberGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..131af7cfef3497e3749f5af6a27d809d62b8f9ab --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/RoundingNumberGenerator.java @@ -0,0 +1,24 @@ +package rescuecore2.misc; + +import org.uncommons.maths.number.NumberGenerator; + +/** + A NumberGenerator that rounds output from a downstream NumberGenerator. +*/ +public class RoundingNumberGenerator implements NumberGenerator<Integer> { + private NumberGenerator<? extends Number> downstream; + + /** + Construct a RoundingNumberGenerator. + @param downstream The downstream generator to round output from. + */ + public RoundingNumberGenerator(NumberGenerator<? extends Number> downstream) { + this.downstream = downstream; + } + + @Override + public Integer nextValue() { + Number n = downstream.nextValue(); + return (int)Math.round(n.doubleValue()); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/WorkerThread.java b/modules/rescuecore2/src/rescuecore2/misc/WorkerThread.java new file mode 100644 index 0000000000000000000000000000000000000000..1f00fac1aad344b257662e072d1d662f1c029177 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/WorkerThread.java @@ -0,0 +1,86 @@ +package rescuecore2.misc; + +/** + Abstract class for threads that need to repeat a unit of work, for example listening on a socket and dispatching data to listeners. + */ +public abstract class WorkerThread extends Thread { + private volatile boolean running; + private volatile boolean killed; + + /** + Construct a worker thread. + */ + public WorkerThread() { + running = true; + killed = false; + } + + /** + Interrupt any current work, tell the thread to stop and wait for the thread to die. + @throws InterruptedException If the thread that called kill is itself interrupted. + */ + public void kill() throws InterruptedException { + synchronized (this) { + if (killed) { + return; + } + killed = true; + } + if (Thread.currentThread() == this) { + // This thread is killing itself. Just return - the run loop will terminate now that "killed" is true. + return; + } + // Interrupt the worker thread + this.interrupt(); + // Maybe the calling thread is already interrupted + if (Thread.interrupted()) { + throw new InterruptedException(); + } + this.join(); + } + + @Override + public void run() { + setup(); + try { + while (isRunning()) { + try { + running = work(); + } + catch (InterruptedException e) { + running = false; + } + } + } + finally { + cleanup(); + } + } + + /** + Find out if this thread is still running. If {@link #kill} has been called (and returned) or if {@link #work} has returned false then this worker thread is not still running. + @return True if it is still running and has not been killed, false otherwise. + */ + public boolean isRunning() { + synchronized (this) { + return !killed && running; + } + } + + /** + Do a unit of work and return whether there is more work to be done. Implementations should check periodically for interruptions and throw an exception when required. + @return True if more work remains, false otherwise. + @throws InterruptedException If the worker thread is interrupted. + */ + protected abstract boolean work() throws InterruptedException; + + /** + Perform any setup necessary before work begins. Default implementation does nothing. + */ + protected void setup() {} + + /** + Perform any cleanup necessary after work finishes. This will be called even if the {@link #work} method throws an exception. It is highly recommended that this method does not throw any exceptions. Default implementation does nothing. + */ + protected void cleanup() {} +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/collections/ArrayTools.java b/modules/rescuecore2/src/rescuecore2/misc/collections/ArrayTools.java new file mode 100644 index 0000000000000000000000000000000000000000..15a3e5b2386477dc62ac80519e508bc1828e8e44 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/collections/ArrayTools.java @@ -0,0 +1,47 @@ +package rescuecore2.misc.collections; + +import java.lang.reflect.Array; + +/** + Useful array tools. +*/ +public final class ArrayTools { + private ArrayTools() {} + + /** + Convert an array of objects to a string. + @param array The array to convert. + @return A stringified version of the array. + */ + public static String convertArrayToString(Object[] array) { + StringBuilder result = new StringBuilder(); + result.append("["); + for (int i = 0; i < array.length; ++i) { + result.append(array[i]); + if (i < array.length - 1) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } + + /** + Convert an array object to a string. This will handle arrays of primitives as well as of Objects. + @param array The array object to convert. + @return A stringified version of the array. + */ + public static String convertArrayObjectToString(Object array) { + StringBuilder result = new StringBuilder(); + result.append("["); + int length = Array.getLength(array); + for (int i = 0; i < length; ++i) { + result.append(Array.get(array, i)); + if (i < length - 1) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/collections/DelegatingMap.java b/modules/rescuecore2/src/rescuecore2/misc/collections/DelegatingMap.java new file mode 100644 index 0000000000000000000000000000000000000000..0d7fd268827ef6d0fbe6857af9931017eab47b67 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/collections/DelegatingMap.java @@ -0,0 +1,95 @@ +package rescuecore2.misc.collections; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; + +/** + A DelegatingMap is a HashMap that delegates to another map for keys that do not have a value. + @param <K> The key type. + @param <V> The value type. + */ +public abstract class DelegatingMap<K, V> extends HashMap<K, V> { + private Map<K, V> downstream; + + /** + Construct a DelegatingMap that delegates to a given map. + @param downstream The delegate map. + */ + public DelegatingMap(Map<K, V> downstream) { + this.downstream = downstream; + } + + @Override + public boolean containsKey(Object key) { + return super.containsKey(key) || downstream.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return super.containsValue(value) || downstream.containsValue(value); + } + + @Override + public Set<Map.Entry<K, V>> entrySet() { + Set<Map.Entry<K, V>> result = new HashSet<Map.Entry<K, V>>(); + result.addAll(super.entrySet()); + for (Map.Entry<K, V> next : downstream.entrySet()) { + if (!super.containsKey(next.getKey())) { + result.add(next); + } + } + return result; + } + + @Override + public boolean equals(Object o) { + if (o instanceof DelegatingMap) { + return super.equals(o) && this.downstream.equals(((DelegatingMap)o).downstream); + } + return false; + } + + @Override + @SuppressWarnings("unchecked") + public V get(Object key) { + V result = super.get(key); + if (result == null) { + return downstream.get(key); + } + return result; + } + + @Override + public int hashCode() { + return super.hashCode() ^ downstream.hashCode(); + } + + @Override + public boolean isEmpty() { + return super.isEmpty() && downstream.isEmpty(); + } + + @Override + public Set<K> keySet() { + Set<K> result = new HashSet<K>(); + result.addAll(super.keySet()); + result.addAll(downstream.keySet()); + return result; + } + + @Override + public int size() { + return keySet().size(); + } + + @Override + public Collection<V> values() { + Collection<V> result = new HashSet<V>(); + result.addAll(super.values()); + result.addAll(downstream.values()); + return result; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/collections/LazyMap.java b/modules/rescuecore2/src/rescuecore2/misc/collections/LazyMap.java new file mode 100644 index 0000000000000000000000000000000000000000..b938d58680a591365d4d0b53dbdffc71f5087c1e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/collections/LazyMap.java @@ -0,0 +1,117 @@ +package rescuecore2.misc.collections; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.Collection; + +/** + A LazyMap is a Map that generates default values for keys that do not yet have a value. This class delegates to a real Map implementation for all methods with the exception of {@link #get(Object)}. If the downstream get returns a value then that value is returned to the caller. If the downstream get returns null then the {@link #createValue()} method will be called and the result of that is added to the downstream map and returned. + @param <K> The key type. + @param <V> The value type. + */ +public abstract class LazyMap<K, V> implements Map<K, V> { + private Map<K, V> downstream; + + /** + Construct a LazyMap that delegates to a new HashMap. + */ + public LazyMap() { + this(new HashMap<K, V>()); + } + + /** + Construct a LazyMap that delegates to a given map. + @param downstream The delegate map. + */ + public LazyMap(Map<K, V> downstream) { + this.downstream = downstream; + } + + /** + Construct a new value object for a key that does not yet have a value. + @return A new value object. + */ + public abstract V createValue(); + + @Override + public void clear() { + downstream.clear(); + } + + @Override + public boolean containsKey(Object key) { + return downstream.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return downstream.containsValue(value); + } + + @Override + public Set<Map.Entry<K, V>> entrySet() { + return downstream.entrySet(); + } + + @Override + public boolean equals(Object o) { + return (o instanceof LazyMap) && (((LazyMap)o).downstream.equals(this.downstream)); + } + + @Override + @SuppressWarnings("unchecked") + public V get(Object key) { + V result = downstream.get(key); + if (result == null) { + try { + result = createValue(); + downstream.put((K)key, result); + } + catch (ClassCastException e) { + result = null; + } + } + return result; + } + + @Override + public int hashCode() { + return downstream.hashCode(); + } + + @Override + public boolean isEmpty() { + return downstream.isEmpty(); + } + + @Override + public Set<K> keySet() { + return downstream.keySet(); + } + + @Override + public V put(K key, V value) { + return downstream.put(key, value); + } + + @Override + public void putAll(Map<? extends K, ? extends V> m) { + downstream.putAll(m); + } + + @Override + public V remove(Object key) { + return downstream.remove(key); + } + + @Override + public int size() { + return downstream.size(); + } + + @Override + public Collection<V> values() { + return downstream.values(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/GeometryTools2D.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/GeometryTools2D.java new file mode 100644 index 0000000000000000000000000000000000000000..ca305884bf0822efb110f584160a77c302aa77f1 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/GeometryTools2D.java @@ -0,0 +1,464 @@ +package rescuecore2.misc.geometry; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + A bunch of useful 2D geometry tools: finding line intersections, closest points and so on. + */ +public final class GeometryTools2D { + /** + The threshold for equality testing in geometric operations. Lines will be considered parallel if the D factor is less than this; points will be considered equivalent if their position difference is less than this and so on. + */ + public static final double THRESHOLD = 0.0000000000001; + + private GeometryTools2D() {} + + /** + Find the intersection point of two lines. + @param l1 The first line. + @param l2 The second line. + @return The intersection point of the two lines, or null if the lines are parallel. + */ + public static Point2D getIntersectionPoint(Line2D l1, Line2D l2) { + double t1 = l1.getIntersection(l2); + double t2 = l2.getIntersection(l1); + if (Double.isNaN(t1) || Double.isNaN(t2)) { + return null; + } + return l1.getPoint(t1); + } + + /** + Find the intersection point of two line segments. + @param l1 The first line segment. + @param l2 The second line segment. + @return The intersection point, or null if the line segments are parallel or do not intersect. + */ + public static Point2D getSegmentIntersectionPoint(Line2D l1, Line2D l2) { + double t1 = l1.getIntersection(l2); + double t2 = l2.getIntersection(l1); + if (Double.isNaN(t1) || Double.isNaN(t2) || t1 < 0 || t1 > 1 || t2 < 0 || t2 > 1) { + return null; + } + return l1.getPoint(t1); + } + + /** + Are two lines parallel? + @param l1 The first line. + @param l2 The second line. + @return true iff the lines are parallel (within tolerance). Direction does not matter; lines pointing in opposite directions are still parallel. + */ + public static boolean parallel(Line2D l1, Line2D l2) { + double d = (l1.getDirection().getX() * l2.getDirection().getY()) - (l1.getDirection().getY() * l2.getDirection().getX()); + return nearlyZero(d); + } + + /** + Find out if a point is on a line. + @param line The line to test. + @param point The point to test. + @return true iff the point is on the line (within tolerance). + */ + public static boolean contains(Line2D line, Point2D point) { + if (nearlyZero(line.getDirection().getX())) { + // Line is parallel to the Y axis so just check that the X coordinate is correct and the Y coordinate is within bounds + double d = point.getX() - line.getOrigin().getX(); + double y = point.getY(); + double yMin = Math.min(line.getOrigin().getY(), line.getEndPoint().getY()); + double yMax = Math.max(line.getOrigin().getY(), line.getEndPoint().getY()); + return nearlyZero(d) && y >= yMin && y <= yMax; + } + if (nearlyZero(line.getDirection().getY())) { + // Line is parallel to the X axis so just check that the Y coordinate is correct and the X coordinate is within bounds + double d = point.getY() - line.getOrigin().getY(); + double x = point.getX(); + double xMin = Math.min(line.getOrigin().getX(), line.getEndPoint().getX()); + double xMax = Math.max(line.getOrigin().getX(), line.getEndPoint().getX()); + return nearlyZero(d) && x >= xMin && x <= xMax; + } + double tx = (point.getX() - line.getOrigin().getX()) / line.getDirection().getX(); + double ty = (point.getY() - line.getOrigin().getY()) / line.getDirection().getY(); + if (tx < 0 || tx > 1 || ty < 0 || ty > 1) { + return false; + } + double d = tx - ty; + return nearlyZero(d); + } + + /** + Find out how far a point is along a line. + @param line The line to test. + @param point The point to test. + @return The t value of the point along the line, or NaN if the point is not on the line. + */ + public static double positionOnLine(Line2D line, Point2D point) { + if (nearlyZero(line.getDirection().getX())) { + // Line is parallel to the Y axis so just solve for Y + double d = (point.getY() - line.getOrigin().getY()) / line.getDirection().getY(); + return d; + } + if (nearlyZero(line.getDirection().getY())) { + // Line is parallel to the X axis so just solve for X + double d = (point.getX() - line.getOrigin().getX()) / line.getDirection().getX(); + return d; + } + // Solve for both X and Y + double tx = (point.getX() - line.getOrigin().getX()) / line.getDirection().getX(); + double ty = (point.getY() - line.getOrigin().getY()) / line.getDirection().getY(); + double d = tx - ty; + if (nearlyZero(d)) { + return tx; + } + else { + return Double.NaN; + } + } + + /** + Compute the angle between two lines in a clockwise direction. + @param first The first line. + @param second The second line. + @return The angle in degrees measured in a clockwise direction. + */ + public static double getRightAngleBetweenLines(Line2D first, Line2D second) { + return getRightAngleBetweenVectors(first.getDirection(), second.getDirection()); + } + + /** + Compute the angle between two lines in a counter-clockwise direction. + @param first The first line. + @param second The second line. + @return The angle in degrees measured in a counter-clockwise direction. + */ + public static double getLeftAngleBetweenLines(Line2D first, Line2D second) { + return getLeftAngleBetweenVectors(first.getDirection(), second.getDirection()); + } + + /** + Compute the angle between two vectors in degrees. + @param first The first vector. + @param second The second vector. + @return The angle in degrees. This will be between 0 and 180. + */ + public static double getAngleBetweenVectors(Vector2D first, Vector2D second) { + Vector2D v1 = first.normalised(); + Vector2D v2 = second.normalised(); + double cos = v1.dot(v2); + if (cos > 1) { + cos = 1; + } + double angle = Math.toDegrees(Math.acos(cos)); + return angle; + } + + /** + Compute the angle between two vectors in a clockwise direction. + @param first The first vector. + @param second The second vector. + @return The angle in degrees measured in a clockwise direction. + */ + public static double getRightAngleBetweenVectors(Vector2D first, Vector2D second) { + double angle = getAngleBetweenVectors(first, second); + // Now find out if we're turning left or right + if (isRightTurn(first, second)) { + return angle; + } + else { + // It's a left turn + // CHECKSTYLE:OFF:MagicNumber + return 360.0 - angle; + // CHECKSTYLE:ON:MagicNumber + } + } + + /** + Compute the angle between two vectors in a counter-clockwise direction. + @param first The first vector. + @param second The second vector. + @return The angle in degrees measured in a counter-clockwise direction. + */ + public static double getLeftAngleBetweenVectors(Vector2D first, Vector2D second) { + double angle = getAngleBetweenVectors(first, second); + // Now find out if we're turning left or right + if (isRightTurn(first, second)) { + // CHECKSTYLE:OFF:MagicNumber + return 360.0 - angle; + // CHECKSTYLE:ON:MagicNumber + } + else { + return angle; + } + } + + /** + Find out if we turn right from one line to another. + @param first The first line. + @param second The second line. + @return True if the second line is a right turn from the first, false otherwise. + */ + public static boolean isRightTurn(Line2D first, Line2D second) { + return isRightTurn(first.getDirection(), second.getDirection()); + } + + /** + Find out if we turn right from one vector to another. + @param first The first vector. + @param second The second vector. + @return True if the second vector is a right turn from the first, false otherwise. + */ + public static boolean isRightTurn(Vector2D first, Vector2D second) { + double t = (first.getX() * second.getY()) - (first.getY() * second.getX()); + return t < 0; + } + + /** + Find out if a number is near enough to zero. + @param d The number to test. + @return true iff the number is nearly zero. + */ + public static boolean nearlyZero(double d) { + return d > -THRESHOLD && d < THRESHOLD; + } + + /** + Compute the area of a simple polygon. + @param vertices The vertices of the polygon. + @return The area of the polygon. + */ + public static double computeArea(List<Point2D> vertices) { + return Math.abs(computeAreaUnsigned(vertices)); + } + + /** + Compute the centroid of a simple polygon. + @param vertices The vertices of the polygon. + @return The centroid. + */ + public static Point2D computeCentroid(List<Point2D> vertices) { + double area = computeAreaUnsigned(vertices); + Iterator<Point2D> it = vertices.iterator(); + Point2D last = it.next(); + Point2D first = last; + double xSum = 0; + double ySum = 0; + while (it.hasNext()) { + Point2D next = it.next(); + double lastX = last.getX(); + double lastY = last.getY(); + double nextX = next.getX(); + double nextY = next.getY(); + xSum += (lastX + nextX) * ((lastX * nextY) - (nextX * lastY)); + ySum += (lastY + nextY) * ((lastX * nextY) - (nextX * lastY)); + last = next; + } + double lastX = last.getX(); + double lastY = last.getY(); + double nextX = first.getX(); + double nextY = first.getY(); + xSum += (lastX + nextX) * ((lastX * nextY) - (nextX * lastY)); + ySum += (lastY + nextY) * ((lastX * nextY) - (nextX * lastY)); + // CHECKSTYLE:OFF:MagicNumber + xSum /= 6.0 * area; + ySum /= 6.0 * area; + // CHECKSTYLE:ON:MagicNumber + return new Point2D(xSum, ySum); + } + + /** + Convert a vertex array to a list of Point2D objects. + @param vertices The vertices in x, y order. + @return A list of Point2D objects. + */ + public static List<Point2D> vertexArrayToPoints(int[] vertices) { + List<Point2D> result = new ArrayList<Point2D>(); + for (int i = 0; i < vertices.length; i += 2) { + result.add(new Point2D(vertices[i], vertices[i + 1])); + } + return result; + } + + /** + Convert a vertex array to a list of Point2D objects. + @param vertices The vertices in x, y order. + @return A list of Point2D objects. + */ + public static List<Point2D> vertexArrayToPoints(double[] vertices) { + List<Point2D> result = new ArrayList<Point2D>(); + for (int i = 0; i < vertices.length; i += 2) { + result.add(new Point2D(vertices[i], vertices[i + 1])); + } + return result; + } + + /** + Convert a list of Point2D objects to a list of lines connecting adjacent points. The shape will not be automatically closed. + @param points The points to connect. + @return A list of Line2D objects. + */ + public static List<Line2D> pointsToLines(List<Point2D> points) { + return pointsToLines(points, false); + } + + /** + Convert a list of Point2D objects to a list of lines connecting adjacent points. + @param points The points to connect. + @param close Whether to close the shape or not. + @return A list of Line2D objects. + */ + public static List<Line2D> pointsToLines(List<Point2D> points, boolean close) { + List<Line2D> result = new ArrayList<Line2D>(); + Iterator<Point2D> it = points.iterator(); + Point2D first = it.next(); + Point2D prev = first; + while (it.hasNext()) { + Point2D next = it.next(); + result.add(new Line2D(prev, next)); + prev = next; + } + if (close && !prev.equals(first)) { + result.add(new Line2D(prev, first)); + } + return result; + } + + /** + Find the closest point on a line. + @param line The line to check. + @param point The point to check against. + @return The point on the line that is closest to the reference point. + */ + public static Point2D getClosestPoint(Line2D line, Point2D point) { + Point2D p1 = line.getOrigin(); + Point2D p2 = line.getEndPoint(); + double u = (((point.getX() - p1.getX()) * (p2.getX() - p1.getX())) + ((point.getY() - p1.getY()) * (p2.getY() - p1.getY()))) / (line.getDirection().getLength() * line.getDirection().getLength()); + return line.getPoint(u); + } + + /** + Find the closest point on a line. + @param line The line to check. + @param point The point to check against. + @return The point on the line that is closest to the reference point. + */ + public static Point2D getClosestPointOnSegment(Line2D line, Point2D point) { + Point2D p1 = line.getOrigin(); + Point2D p2 = line.getEndPoint(); + double u = (((point.getX() - p1.getX()) * (p2.getX() - p1.getX())) + ((point.getY() - p1.getY()) * (p2.getY() - p1.getY()))) / (line.getDirection().getLength() * line.getDirection().getLength()); + if (u <= 0) { + return p1; + } + if (u >= 1) { + return p2; + } + return line.getPoint(u); + } + + /** + Compute the distance between two points. + @param p1 The first point. + @param p2 The second point. + @return The distance between the two points. + */ + public static double getDistance(Point2D p1, Point2D p2) { + return Math.hypot(p1.getX() - p2.getX(), p1.getY() - p2.getY()); + } + + /** + Clip a line to a rectangle. + @param line The line to clip. + @param xMin The lower X coordinate of the rectangle. + @param yMin The lower Y coordinate of the rectangle. + @param xMax The upper X coordinate of the rectangle. + @param yMax The upper Y coordinate of the rectangle. + @return A clipped line, or null if the line is outside the rectangle. + */ + public static Line2D clipToRectangle(Line2D line, double xMin, double yMin, double xMax, double yMax) { + // Liang-Barsky line clipping algorithm + double x1 = line.getOrigin().getX(); + double y1 = line.getOrigin().getY(); + double x2 = line.getEndPoint().getX(); + double y2 = line.getEndPoint().getY(); + double tL = (xMin - x1) / (x2 - x1); + double tR = (xMax - x1) / (x2 - x1); + double tT = (yMax - y1) / (y2 - y1); + double tB = (yMin - y1) / (y2 - y1); + double tMin = 0; + double tMax = 1; + if ((x1 < xMin && x2 < xMin) + || (x1 > xMax && x2 > xMax) + || (y1 < yMin && y2 < yMin) + || (y1 > yMax && y2 > yMax)) { + return null; + } + // Left + if (tL > tMin && tL < tMax) { + if (x1 < x2) { + // Entering left edge + tMin = tL; + } + else { + tMax = tL; + } + } + // Right + if (tR > tMin && tR < tMax) { + if (x1 > x2) { + // Entering right edge + tMin = tR; + } + else { + tMax = tR; + } + } + // Top + if (tT > tMin && tT < tMax) { + if (y1 > y2) { + // Entering top edge + tMin = tT; + } + else { + tMax = tT; + } + } + // Left + if (tB > tMin && tB < tMax) { + if (y1 < y2) { + // Entering bottom edge + tMin = tB; + } + else { + tMax = tB; + } + } + if (tMin > tMax) { + return null; + } + return new Line2D(line.getPoint(tMin), line.getPoint(tMax)); + } + + private static double computeAreaUnsigned(List<Point2D> vertices) { + Iterator<Point2D> it = vertices.iterator(); + Point2D last = it.next(); + Point2D first = last; + double sum = 0; + while (it.hasNext()) { + Point2D next = it.next(); + double lastX = last.getX(); + double lastY = last.getY(); + double nextX = next.getX(); + double nextY = next.getY(); + sum += (lastX * nextY) - (nextX * lastY); + last = next; + } + double lastX = last.getX(); + double lastY = last.getY(); + double nextX = first.getX(); + double nextY = first.getY(); + sum += (lastX * nextY) - (nextX * lastY); + sum /= 2.0; + return sum; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/Line2D.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/Line2D.java new file mode 100644 index 0000000000000000000000000000000000000000..f6dab9b23488a4a44ffe8be674ec5cf92283d397 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/Line2D.java @@ -0,0 +1,133 @@ +package rescuecore2.misc.geometry; + +import static rescuecore2.misc.geometry.GeometryTools2D.nearlyZero; + +import rescuecore2.misc.geometry.spatialindex.Indexable; +import rescuecore2.misc.geometry.spatialindex.Region; +import rescuecore2.misc.geometry.spatialindex.LineRegion; + +/** + A line segment in 2D space. Lines are immutable. + */ +public class Line2D implements Indexable { + private Point2D origin; + private Point2D end; + private Vector2D direction; + private LineRegion region; + + /** + Create a new line segment. + @param origin The origin of the line. + @param direction The direction of the line. + */ + public Line2D(Point2D origin, Vector2D direction) { + this.origin = origin; + this.direction = direction; + end=origin.plus(direction); + + calculateDirection(); + } + + /** + Create a new line segment. + @param origin The origin of the line. + @param end The end of the line. + */ + public Line2D(Point2D origin, Point2D end) { + this.origin = origin; + this.end = end; + this.direction = end.minus(origin); + } + + /** + Create a new line segment. + @param x The x coordinate of the origin. + @param y The y coordinate of the origin. + @param dx The x component of the direction. + @param dy The y component of the direction. + */ + public Line2D(double x, double y, double dx, double dy) { + this(new Point2D(x, y), new Vector2D(dx, dy)); + } + + /** + Get a point along this line. + @param t The distance along the direction vector to create the point. + @return A new Point2D. + */ + public Point2D getPoint(double t) { + return origin.translate(t * direction.getX(), t * direction.getY()); + } + + /** + Get the origin of this line segment. + @return The origin. + */ + public Point2D getOrigin() { + return origin; + } + + /** + Get the endpoint of this line segment. + @return The endpoint. + */ + public Point2D getEndPoint() { + return end; + } + + /** + Get the direction of this line segment. + @return The direction vector. + */ + public Vector2D getDirection() { + return direction; + } + + @Override + public String toString() { + return "Line from " + origin + " towards " + end + " (direction = " + direction + ")"; + } + + /** + Find out how far along this line the intersection point with another line is. + @param other The other line. + @return How far along this line (in terms of this line's direction vector) the intersection point is, or NaN if the lines are parallel. + */ + public double getIntersection(Line2D other) { + double bxax = direction.getX(); + double dycy = other.direction.getY(); + double byay = direction.getY(); + double dxcx = other.direction.getX(); + double cxax = other.origin.getX() - origin.getX(); + double cyay = other.origin.getY() - origin.getY(); + double d = (bxax * dycy) - (byay * dxcx); + double t = (cxax * dycy) - (cyay * dxcx); + if (nearlyZero(d)) { + // d is close to zero: lines are parallel so no intersection + return Double.NaN; + } + return t / d; + } + + @Override + public Region getBoundingRegion() { + if (region == null) { + region = new LineRegion(origin.getX(), origin.getY(), end.getX(), end.getY()); + } + return region; + } + + public void setEnd(Point2D end2) { + this.end = end2; + calculateDirection(); + + } + private void calculateDirection() { + this.direction = end.minus(origin); + } + + public void setOrigin(Point2D origin) { + this.origin = origin; + calculateDirection(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/Point2D.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/Point2D.java new file mode 100644 index 0000000000000000000000000000000000000000..b6a1a172ad9f5a27a2805db81682cceca965fbef --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/Point2D.java @@ -0,0 +1,102 @@ +package rescuecore2.misc.geometry; + +import rescuecore2.misc.geometry.spatialindex.Indexable; +import rescuecore2.misc.geometry.spatialindex.PointRegion; +import rescuecore2.misc.geometry.spatialindex.Region; + +/** + * A point in 2D space. Points are immutable. + */ +public class Point2D implements Indexable { + private double x; + private double y; + private PointRegion region; + + /** + * Create a new Point2D. + * + * @param x The x coordinate. + * @param y The y coordinate. + */ + public Point2D(double x, double y) { + this.x = x; + this.y = y; + region = null; + } + + /** + * Get the X coordinate. + * + * @return The X coordinate. + */ + public double getX() { + return x; + } + + /** + * Get the Y coordinate. + * + * @return The Y coordinate. + */ + public double getY() { + return y; + } + + /** + * Create a new Point2D that is a translation of this point. + * + * @param dx The x translation. + * @param dy The y translation. + * @return A new Point2D. + */ + public Point2D translate(double dx, double dy) { + return new Point2D(x + dx, y + dy); + } + + /** + * Create a vector by subtracting a point from this point. + * + * @param p The Point2D to subtract from this one. + * @return A new Vector2D that represents the vector from the other point to + * this one. + */ + public Vector2D minus(Point2D p) { + return new Vector2D(this.x - p.x, this.y - p.y); + } + + /** + * Create a Point2D by adding a vector to this point. + * + * @param v The Vector2D to add. + * @return A new Point2D. + */ + public Point2D plus(Vector2D v) { + return new Point2D(this.x + v.getX(), this.y + v.getY()); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Point2D)) { + return false; + } + return this.x == ((Point2D) o).x && this.y == ((Point2D) o).y; + } + + @Override + public int hashCode() { + return Double.valueOf(x).hashCode() ^ Double.valueOf(y).hashCode(); + } + + @Override + public String toString() { + return x + " , " + y; + } + + @Override + public Region getBoundingRegion() { + if (region == null) { + region = new PointRegion(x, y); + } + return region; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/Vector2D.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/Vector2D.java new file mode 100644 index 0000000000000000000000000000000000000000..aed79e732e4b5c901e8ca262c53556980879e2df --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/Vector2D.java @@ -0,0 +1,96 @@ +package rescuecore2.misc.geometry; + +/** + A vector in 2D space. Points are immutable. + */ +public class Vector2D { + private double dx; + private double dy; + private double length; + + /** + Create a new Vector2D. + @param dx The x component. + @param dy The y component. + */ + public Vector2D(double dx, double dy) { + this.dx = dx; + this.dy = dy; + length = Double.NaN; + } + + /** + Get the length of this vector. + @return The length of the vector. + */ + public double getLength() { + if (Double.isNaN(length)) { + length = Math.hypot(dx, dy); + } + return length; + } + + /** + Calculate the dot product of this vector and another vector. + @param v The other vector. + @return The dot product of this vector and the other vector. + */ + public double dot(Vector2D v) { + return dx * v.dx + dy * v.dy; + } + + /** + Get the X component of this vector. + @return The X component. + */ + public double getX() { + return dx; + } + + /** + Get the Y component of this vector. + @return The Y component. + */ + public double getY() { + return dy; + } + + /** + Create a new Vector2D by adding a vector to this one. + @param v The vector to add. + @return A new Vector2D. + */ + public Vector2D add(Vector2D v) { + return new Vector2D(dx + v.dx, dy + v.dy); + } + + /** + Create a scaled version of this vector. + @param amount The scale factor. + @return A new Vector2D. + */ + public Vector2D scale(double amount) { + return new Vector2D(dx * amount, dy * amount); + } + + /** + Create a normalised version of this vector. + @return A new Vector2D with length 1. + */ + public Vector2D normalised() { + return scale(1.0 / getLength()); + } + + /** + Get a Vector2D that is normal to this one. + @return A new Vector2D that is normal to this one. This will be the "left-hand" normal. + */ + public Vector2D getNormal() { + return new Vector2D(-dy, dx); + } + + @Override + public String toString() { + return dx + ", " + dy; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/AbstractSpatialIndex.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/AbstractSpatialIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..ce8fdc4f836dc7a6f8f2d5ff5d9d52c7cfd52253 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/AbstractSpatialIndex.java @@ -0,0 +1,13 @@ +package rescuecore2.misc.geometry.spatialindex; + +import java.util.Collection; + +/** + Abstract base class for spatial index implementations. +*/ +public abstract class AbstractSpatialIndex implements SpatialIndex { + @Override + public Collection<Indexable> getItemsInRegion(double xMin, double yMin, double xMax, double yMax) { + return getItemsInRegion(new RectangleRegion(xMin, yMin, xMax, yMax)); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/BBTree.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/BBTree.java new file mode 100644 index 0000000000000000000000000000000000000000..a58299d9f0ea7d396a7af529faefd673021ba952 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/BBTree.java @@ -0,0 +1,339 @@ +package rescuecore2.misc.geometry.spatialindex; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Stack; + +import rescuecore2.log.Logger; + +/** + A spatial index that creates a (probably) unbalanced tree of bounding boxes. This is almost certainly not as efficient as an RTree or similar, but it is much easier to implement. +*/ +public class BBTree extends AbstractSpatialIndex { + private static final int DEFAULT_MAX_CHILDREN = 3; + + private static final int DEFAULT_TIMING_POINTS = 50000; + private static final int DEFAULT_TIMING_LINES = 50000; + private static final int DEFAULT_TIMING_REGIONS = 1000; + + private Node root; + private int maxChildren; + + /** + Construct a BBTree with a default maximum number of children per branch. + */ + public BBTree() { + this(DEFAULT_MAX_CHILDREN); + } + + /** + Construct a BBTree with a given maximum number of children per branch. + @param maxChildren The maximum number of children per branch. + */ + public BBTree(int maxChildren) { + this.maxChildren = maxChildren; + root = new Branch(); + } + + /** + Conduct a timing test. + @param args Command line arguments: [-p points] [-l lines] [-r regions] + */ + public static void main(String[] args) { + int points = DEFAULT_TIMING_POINTS; + int lines = DEFAULT_TIMING_LINES; + int regions = DEFAULT_TIMING_REGIONS; + // CHECKSTYLE:OFF:ModifiedControlVariable + for (int i = 0; i < args.length; ++i) { + if ("-p".equalsIgnoreCase(args[i])) { + points = Integer.parseInt(args[++i]); + } + else if ("-l".equalsIgnoreCase(args[i])) { + lines = Integer.parseInt(args[++i]); + } + else if ("-r".equalsIgnoreCase(args[i])) { + regions = Integer.parseInt(args[++i]); + } + else { + System.out.println("Unrecognised option: " + args[i]); + } + } + // CHECKSTYLE:ON:ModifiedControlVariable + BBTree tree = new BBTree(); + long start = System.currentTimeMillis(); + for (int i = 0; i < points; ++i) { + rescuecore2.misc.geometry.Point2D p = new rescuecore2.misc.geometry.Point2D(Math.random(), Math.random()); + tree.insert(p); + } + for (int i = 0; i < lines; ++i) { + rescuecore2.misc.geometry.Point2D p1 = new rescuecore2.misc.geometry.Point2D(Math.random(), Math.random()); + rescuecore2.misc.geometry.Point2D p2 = new rescuecore2.misc.geometry.Point2D(Math.random(), Math.random()); + rescuecore2.misc.geometry.Line2D l = new rescuecore2.misc.geometry.Line2D(p1, p2); + tree.insert(l); + } + tree.logTree(); + long fill = System.currentTimeMillis(); + for (int i = 0; i < regions; ++i) { + double xMin = Math.random(); + double yMin = Math.random(); + double xMax = Math.random(); + double yMax = Math.random(); + tree.getItemsInRegion(Math.min(xMin, xMax), Math.min(yMin, yMax), Math.max(xMin, xMax), Math.max(yMin, yMax)); + } + long end = System.currentTimeMillis(); + long fillTime = fill - start; + long fetchTime = end - fill; + double fillAverage = ((double)fillTime) / (double)(points + lines); + double fetchAverage = ((double)fetchTime) / (double)regions; + System.out.println("Time to populate tree with " + points + " points and " + lines + " lines: " + fillTime + "ms (average " + fillAverage + "ms)"); + System.out.println("Time to read " + regions + " regions: " + fetchTime + "ms (average " + fetchAverage + "ms)"); + } + + @Override + public void insert(Indexable i) { + // Logger.debug("Inserting " + i); + // Logger.debug("Tree before insert"); + // logTree(); + Leaf newLeaf = new Leaf(i); + Node insertPoint = findInsertionPoint(root, i.getBoundingRegion()); + // Logger.debug("Insertion point: " + insertPoint); + if (insertPoint instanceof Leaf) { + Branch b = new Branch(); + if (insertPoint.parent != null) { + insertPoint.parent.insert(b); + insertPoint.parent.remove(insertPoint); + } + b.insert(insertPoint); + b.insert(newLeaf); + } + else { + Branch b = (Branch)insertPoint; + b.insert(newLeaf); + } + newLeaf.recomputeBounds(); + // Logger.debug("Tree after insert"); + // logTree(); + } + + @Override + public Collection<Indexable> getItemsInRegion(Region region) { + // Logger.debug("Getting items in region " + region); + Collection<Indexable> result = new ArrayList<Indexable>(); + if (root != null) { + Stack<Node> open = new Stack<Node>(); + open.push(root); + while (!open.isEmpty()) { + Node next = open.pop(); + // Logger.debug("Next node: " + next); + if (next.bounds.intersects(region)) { + if (next instanceof Branch) { + // Logger.debug("Adding children"); + open.addAll(((Branch)next).children); + } + else if (next instanceof Leaf) { + Leaf l = (Leaf)next; + if (region.intersects(l.entry.getBoundingRegion())) { + // Logger.debug("Leaf intersects region"); + result.add(l.entry); + } + /* + else { + Logger.debug("Leaf does not intersect region"); + } + */ + } + } + /* + else { + Logger.debug("No intersection"); + } + */ + } + } + return result; + } + + /** + Write this tree to the logger. + */ + public void logTree() { + Logger.debug("BBTree"); + Logger.debug("Max children per node: " + maxChildren); + Logger.debug("Tree depth: " + root.getDepth()); + root.log(" "); + } + + private Node findInsertionPoint(Node parent, Region newRegion) { + // Logger.debug("Choosing insertion point: current parent = " + parent + ", new region = " + newRegion); + if (parent instanceof Leaf) { + // Logger.debug("Parent is a leaf"); + return parent; + } + Branch b = (Branch)parent; + if (b.children.size() < maxChildren) { + // Logger.debug("Parent can fit the child"); + return b; + } + Node best = findLeastAreaEnlargement(b.children, newRegion); + // Logger.debug("Best child: " + best); + return findInsertionPoint(best, newRegion); + } + + private Node findLeastAreaEnlargement(Collection<Node> nodes, Region newRegion) { + // Logger.debug("Finding least area enlargement for " + newRegion); + Node best = null; + double bestDiff = 0; + for (Node next : nodes) { + // Logger.debug("Next node: " + next); + double diff = computeAreaEnlargement(next, newRegion); + if (best == null || diff < bestDiff) { + best = next; + bestDiff = diff; + } + } + // Logger.debug("Best: " + best); + return best; + } + + private double computeAreaEnlargement(Node node, Region newRegion) { + double oldArea = node.bounds instanceof RectangleRegion ? ((RectangleRegion)node.bounds).getArea() : 0; + double newArea = cover(node.bounds, newRegion).getArea(); + // Logger.debug("Old area: " + oldArea); + // Logger.debug("New area: " + newArea); + // Logger.debug("Increase: " + (newArea - oldArea)); + return newArea - oldArea; + } + + private RectangleRegion cover(Region... regions) { + return cover(Arrays.asList(regions)); + } + + private RectangleRegion cover(List<? extends Region> regions) { + if (regions.isEmpty()) { + throw new IllegalArgumentException("Cannot cover zero regions"); + } + double xMin = Double.POSITIVE_INFINITY; + double yMin = Double.POSITIVE_INFINITY; + double xMax = Double.NEGATIVE_INFINITY; + double yMax = Double.NEGATIVE_INFINITY; + for (Region next : regions) { + // CHECKSTYLE:OFF:EmptyBlock + if (next == null || next instanceof NullRegion) { + // Ignore + } + else { + xMin = Math.min(xMin, next.getXMin()); + xMax = Math.max(xMax, next.getXMax()); + yMin = Math.min(yMin, next.getYMin()); + yMax = Math.max(yMax, next.getYMax()); + } + } + if (Double.isInfinite(xMin)) { + return null; + } + return new RectangleRegion(xMin, yMin, xMax, yMax); + } + + private abstract class Node { + Region bounds; + Branch parent; + + Node() { + bounds = null; + parent = null; + } + + abstract void recomputeBounds(); + + abstract void log(String prefix); + + abstract int getDepth(); + } + + private class Branch extends Node { + List<Node> children; + + Branch() { + this.children = new ArrayList<Node>(maxChildren); + } + + void insert(Node child) { + children.add(child); + child.parent = this; + bounds = cover(bounds, child.bounds); + } + + void remove(Node child) { + children.remove(child); + child.parent = null; + } + + @Override + public String toString() { + return "Branch [" + bounds + "] (" + children.size() + " children) {depth " + getDepth() + "}"; + } + + @Override + void log(String prefix) { + Logger.debug(prefix + this); + String newPrefix = prefix + " "; + for (Node next : children) { + next.log(newPrefix); + } + } + + @Override + void recomputeBounds() { + List<Region> childBounds = new ArrayList<Region>(children.size()); + for (Node next : children) { + childBounds.add(next.bounds); + } + bounds = cover(childBounds); + if (parent != null) { + parent.recomputeBounds(); + } + } + + @Override + int getDepth() { + int max = 0; + for (Node next : children) { + max = Math.max(max, next.getDepth()); + } + return max + 1; + } + } + + private class Leaf extends Node { + Indexable entry; + + Leaf(Indexable entry) { + this.entry = entry; + bounds = entry.getBoundingRegion(); + } + + @Override + public String toString() { + return "Leaf [" + bounds + "] (" + entry + ")"; + } + + @Override + void log(String prefix) { + Logger.debug(prefix + this.toString()); + } + + @Override + void recomputeBounds() { + if (parent != null) { + parent.recomputeBounds(); + } + } + + @Override + int getDepth() { + return 1; + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Indexable.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Indexable.java new file mode 100644 index 0000000000000000000000000000000000000000..973f79c7236940e49833a3935425eeb55f768161 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Indexable.java @@ -0,0 +1,12 @@ +package rescuecore2.misc.geometry.spatialindex; + +/** + Interface for things that can go into a spatial index. +*/ +public interface Indexable { + /** + Get the bounding region of this object. + @return The bounding region. + */ + Region getBoundingRegion(); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/LineRegion.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/LineRegion.java new file mode 100644 index 0000000000000000000000000000000000000000..d1b31cf1d45bcd2525b5b76f2ad48a78fe894c78 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/LineRegion.java @@ -0,0 +1,176 @@ +package rescuecore2.misc.geometry.spatialindex; + +import static rescuecore2.misc.geometry.spatialindex.Tools.equal; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A line region. +*/ +public class LineRegion implements Region { + private double x1; + private double y1; + private double x2; + private double y2; + private Line2D line; + + /** + Construct a line region. + @param x1 The first X coordinate. + @param y1 The first Y coordinate. + @param x2 The second X coordinate. + @param y2 The second Y coordinate. + */ + public LineRegion(double x1, double y1, double x2, double y2) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + line = null; + } + + /** + Get the first X coordinate. + @return The first X coordinate. + */ + public double getX1() { + return x1; + } + + /** + Get the first Y coordinate. + @return The first Y coordinate. + */ + public double getY1() { + return y1; + } + + /** + Get the second X coordinate. + @return The second X coordinate. + */ + public double getX2() { + return x2; + } + + /** + Get the second Y coordinate. + @return The second Y coordinate. + */ + public double getY2() { + return y2; + } + + /** + Get a Line2D representing this region. + @return A Line2D representation of the region. + */ + public Line2D getLine() { + if (line == null) { + line = new Line2D(new Point2D(x1, y1), new Point2D(x2, y2)); + } + return line; + } + + @Override + public double getXMin() { + return Math.min(x1, x2); + } + + @Override + public double getYMin() { + return Math.min(y1, y2); + } + + @Override + public double getXMax() { + return Math.max(x1, x2); + } + + @Override + public double getYMax() { + return Math.max(y1, y2); + } + + @Override + public boolean equals(Object o) { + if (o instanceof LineRegion) { + LineRegion l = (LineRegion)o; + return equal(x1, l.x1) && equal(y1, l.y1) && equal(x2, l.x2) && equal(y2, l.y2); + } + return false; + } + + @Override + public int hashCode() { + Double d = x1 + x2 + y1 + y2; + return d.hashCode(); + } + + @Override + public String toString() { + return "Line region: " + x1 + ", " + y1 + " -> " + x2 + ", " + y2; + } + + @Override + public boolean intersects(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return GeometryTools2D.clipToRectangle(getLine(), rect.getXMin(), rect.getYMin(), rect.getXMax(), rect.getYMax()) != null; + } + else if (r instanceof LineRegion) { + LineRegion l = (LineRegion)r; + return GeometryTools2D.getSegmentIntersectionPoint(getLine(), l.getLine()) != null; + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + return GeometryTools2D.contains(getLine(), p.getPoint()); + } + else { + return false; + } + } + + @Override + public boolean contains(Region r) { + if (r instanceof LineRegion) { + LineRegion l = (LineRegion)r; + double first = GeometryTools2D.positionOnLine(getLine(), l.getLine().getOrigin()); + double second = GeometryTools2D.positionOnLine(getLine(), l.getLine().getEndPoint()); + return first >= 0 && first <= 1 && second >= 0 && second <= 1; + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + double d = GeometryTools2D.positionOnLine(getLine(), p.getPoint()); + return d >= 0 && d <= 1; + } + return false; + } + + /* + @Override + public boolean touches(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return (equal(this.x, rect.getXMin()) + || equal(this.x, rect.getXMax()) + || equal(this.y, rect.getYMin()) + || equal(this.y, rect.getYMax())); + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + return (equal(this.x, p.x) + || equal(this.y, p.y)); + } + else if (r instanceof NullRegion) { + return false; + } + else { + throw new IllegalArgumentException("Cannot check for touch with " + r); + } + } + */ +} + diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/NullRegion.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/NullRegion.java new file mode 100644 index 0000000000000000000000000000000000000000..3161ff7ef781c29cd3deb94d663807197a42b868 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/NullRegion.java @@ -0,0 +1,55 @@ +package rescuecore2.misc.geometry.spatialindex; + +/** + An empty region. +*/ +public class NullRegion implements Region { + /** + Construct an empty region. + */ + public NullRegion() { + } + + @Override + public String toString() { + return "Empty region"; + } + + @Override + public boolean intersects(Region r) { + return false; + } + + @Override + public boolean contains(Region r) { + return false; + } + + /* + @Override + public boolean touches(Region r) { + return false; + } + */ + + @Override + public double getXMin() { + return Double.NaN; + } + + @Override + public double getYMin() { + return Double.NaN; + } + + @Override + public double getXMax() { + return Double.NaN; + } + + @Override + public double getYMax() { + return Double.NaN; + } +} + diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/PointRegion.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/PointRegion.java new file mode 100644 index 0000000000000000000000000000000000000000..cdabd3877821778ccf558ee667ec28adb75c9295 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/PointRegion.java @@ -0,0 +1,141 @@ +package rescuecore2.misc.geometry.spatialindex; + +import static rescuecore2.misc.geometry.spatialindex.Tools.equal; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A point region. +*/ +public class PointRegion implements Region { + private double x; + private double y; + private Point2D point; + + /** + Construct a point region. + @param x The X coordinate. + @param y The Y coordinate. + */ + public PointRegion(double x, double y) { + this.x = x; + this.y = y; + point = null; + } + + /** + Get the X coordinate. + @return The X coordinate. + */ + public double getX() { + return x; + } + + /** + Get the Y coordinate. + @return The Y coordinate. + */ + public double getY() { + return y; + } + + /** + Get a Point2D representing this region. + @return A Point2D representation of the region. + */ + public Point2D getPoint() { + if (point == null) { + point = new Point2D(x, y); + } + return point; + } + + @Override + public double getXMin() { + return x; + } + + @Override + public double getYMin() { + return y; + } + + @Override + public double getXMax() { + return x; + } + + @Override + public double getYMax() { + return y; + } + + @Override + public boolean equals(Object o) { + if (o instanceof PointRegion) { + PointRegion p = (PointRegion)o; + return equal(x, p.x) && equal(y, p.y); + } + return false; + } + + @Override + public int hashCode() { + Double d = x + y; + return d.hashCode(); + } + + @Override + public String toString() { + return "Point region: " + x + ", " + y; + } + + @Override + public boolean intersects(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return x >= rect.getXMin() && x <= rect.getXMax() && y >= rect.getYMin() && y <= rect.getYMax(); + } + else if (r instanceof LineRegion) { + LineRegion l = (LineRegion)r; + return GeometryTools2D.contains(l.getLine(), getPoint()); + } + else if (r instanceof PointRegion) { + return this.equals(r); + } + else { + return false; + } + } + + @Override + public boolean contains(Region r) { + return false; + } + + /* + @Override + public boolean touches(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return (equal(this.x, rect.getXMin()) + || equal(this.x, rect.getXMax()) + || equal(this.y, rect.getYMin()) + || equal(this.y, rect.getYMax())); + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + return (equal(this.x, p.x) + || equal(this.y, p.y)); + } + else if (r instanceof NullRegion) { + return false; + } + else { + throw new IllegalArgumentException("Cannot check for touch with " + r); + } + } + */ +} + diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/RectangleRegion.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/RectangleRegion.java new file mode 100644 index 0000000000000000000000000000000000000000..5904e60d97fe28e4f80f973441a285faf3c7ec84 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/RectangleRegion.java @@ -0,0 +1,187 @@ +package rescuecore2.misc.geometry.spatialindex; + +import static rescuecore2.misc.geometry.spatialindex.Tools.equal; + +import rescuecore2.misc.geometry.GeometryTools2D; + +/** + A rectangular region. +*/ +public class RectangleRegion implements Region { + private double xMin; + private double xMax; + private double yMin; + private double yMax; + + /** + Construct a rectangular region. + @param xMin The lower X coordinate. + @param yMin The lower Y coordinate. + @param xMax The upper X coordinate. + @param yMax The upper Y coordinate. + */ + public RectangleRegion(double xMin, double yMin, double xMax, double yMax) { + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + if (xMin > xMax) { + this.xMin = xMax; + this.xMax = xMin; + } + if (yMin > yMax) { + this.yMin = yMax; + this.yMax = yMin; + } + } + + @Override + public double getXMin() { + return xMin; + } + + @Override + public double getYMin() { + return yMin; + } + + @Override + public double getXMax() { + return xMax; + } + + @Override + public double getYMax() { + return yMax; + } + + /** + Calculate the overlap with another RectangleRegion. + @param other The other region. + @return The overlapping area. + */ + public double getOverlapArea(RectangleRegion other) { + if (!this.intersects(other)) { + return 0; + } + double overlapXMin = Math.max(this.xMin, other.xMin); + double overlapXMax = Math.min(this.xMax, other.xMax); + double overlapYMin = Math.max(this.yMin, other.yMin); + double overlapYMax = Math.min(this.yMax, other.yMax); + return (overlapXMax - overlapXMin) * (overlapYMax - overlapYMin); + } + + /** + Get the area of this rectangle. + @return The area. + */ + public double getArea() { + return (xMax - xMin) * (yMax - yMin); + } + + @Override + public String toString() { + return "Rectangle region: " + xMin + ", " + yMin + " -> " + xMax + ", " + yMax; + } + + @Override + public boolean equals(Object o) { + if (o instanceof RectangleRegion) { + RectangleRegion r = (RectangleRegion)o; + return equal(xMin, r.xMin) && equal(xMax, r.xMax) && equal(yMin, r.yMin) && equal(yMax, r.yMax); + } + return false; + } + + @Override + public int hashCode() { + Double d = xMin; + return d.hashCode(); + } + + @Override + public boolean intersects(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + if (rect.xMax < this.xMin + || rect.xMin > this.xMax + || rect.yMax < this.yMin + || rect.yMin > this.yMax) { + return false; + } + return true; + } + else if (r instanceof LineRegion) { + LineRegion l = (LineRegion)r; + return GeometryTools2D.clipToRectangle(l.getLine(), getXMin(), getYMin(), getXMax(), getYMax()) != null; + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + if (p.getX() < xMin + || p.getX() > xMax + || p.getY() < yMin + || p.getY() > yMax) { + return false; + } + return true; + } + else { + return false; + } + } + + @Override + public boolean contains(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return (rect.xMin >= this.xMin + && rect.xMax <= this.xMax + && rect.yMin >= this.yMin + && rect.yMax <= this.yMax); + } + else if (r instanceof LineRegion) { + LineRegion l = (LineRegion)r; + return (l.getXMin() >= this.xMin + && l.getXMax() <= this.xMax + && l.getYMin() >= this.yMin + && l.getYMax() <= this.yMax); + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + return (p.getX() >= this.xMin + && p.getX() <= this.xMax + && p.getY() >= this.yMin + && p.getY() <= this.yMax); + } + else { + return false; + } + } + + /* + @Override + public boolean touches(Region r) { + if (r instanceof RectangleRegion) { + RectangleRegion rect = (RectangleRegion)r; + return (equal(this.xMin, rect.xMin) + || equal(this.xMax, rect.xMax) + || equal(this.yMin, rect.yMin) + || equal(this.yMax, rect.yMax)); + } + else if (r instanceof PointRegion) { + PointRegion p = (PointRegion)r; + return (equal(this.xMin, p.getX()) + || equal(this.xMax, p.getX()) + || equal(this.yMin, p.getY()) + || equal(this.yMax, p.getY())); + } + else if (r instanceof NullRegion) { + return false; + } + else { + throw new IllegalArgumentException("Cannot check for touch with " + r); + } + } + */ +} + diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Region.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Region.java new file mode 100644 index 0000000000000000000000000000000000000000..6161b5b31a996ba35baf951810b49d6706bb73b3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Region.java @@ -0,0 +1,51 @@ +package rescuecore2.misc.geometry.spatialindex; + +/** + An indexable region. +*/ +public interface Region { + /** + Find out if this region intersects another region. + @param r The other region. + @return True if this region intersects the other. + */ + boolean intersects(Region r); + + /** + Find out if this region fully contains another region. + @param r The other region. + @return True if this region contains the other. + */ + boolean contains(Region r); + + /** + Find out if this region touches another region. + @param r The other region. + @return True if this region touches the other. + */ + // boolean touches(Region r); + + /** + Get the lower X coordinate. + @return The lower X coordinate. + */ + double getXMin(); + + /** + Get the lower Y coordinate. + @return The lower Y coordinate. + */ + double getYMin(); + + /** + Get the upper X coordinate. + @return The upper X coordinate. + */ + double getXMax(); + + /** + Get the upper Y coordinate. + @return The upper Y coordinate. + */ + double getYMax(); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/SpatialIndex.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/SpatialIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..59cf9a743b91b0297051feb576d49edcdd7ed291 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/SpatialIndex.java @@ -0,0 +1,31 @@ +package rescuecore2.misc.geometry.spatialindex; + +import java.util.Collection; + +/** + Top-level interface for spatial index implementations. +*/ +public interface SpatialIndex { + /** + Add an item to the index. + @param i The item to add. + */ + void insert(Indexable i); + + /** + Get all indexable objects in a region. + @param xMin The minimum X value. + @param yMin The minimum Y value. + @param xMax The maximum X value. + @param yMax The maximum Y value. + @return All Indexable objects in the region. + */ + Collection<Indexable> getItemsInRegion(double xMin, double yMin, double xMax, double yMax); + + /** + Get all indexable objects in a region. + @param region The region to check. + @return All Indexable objects in the region. + */ + Collection<Indexable> getItemsInRegion(Region region); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Tools.java b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Tools.java new file mode 100644 index 0000000000000000000000000000000000000000..a48f461ff773d17d503cd15c5dcde6bac3d30ef5 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/geometry/spatialindex/Tools.java @@ -0,0 +1,20 @@ +package rescuecore2.misc.geometry.spatialindex; + +/** + Useful tools for R* tree building. +*/ +public final class Tools { + private static final double EPSILON = 0.000000001; + + private Tools() {} + + /** + Find out if two numbers are equal (within epsilon). + @param d1 The first number. + @param d2 The second number. + @return True if d1 and d2 are within epsilon of each other. + */ + public static boolean equal(double d1, double d2) { + return ((d1 > d2 - EPSILON) && (d1 < d2 + EPSILON)); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/ChangeSetComponent.java b/modules/rescuecore2/src/rescuecore2/misc/gui/ChangeSetComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..e08123345543e6decc17c9e8236e57b2b374cdcb --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/ChangeSetComponent.java @@ -0,0 +1,144 @@ +package rescuecore2.misc.gui; + +import java.awt.BorderLayout; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.BorderFactory; +import javax.swing.AbstractListModel; +import javax.swing.table.AbstractTableModel; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.ListSelectionEvent; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.ChangeSet; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; + +/** + A JPanel for displaying ChangeSet objects. +*/ +public class ChangeSetComponent extends JPanel { + private PropertyTableModel propertiesModel; + private EntityListModel changedModel; + private EntityListModel deletedModel; + private ChangeSet changes; + + /** + Construct an empty ChangeSetComponent. + */ + public ChangeSetComponent() { + super(new BorderLayout()); + propertiesModel = new PropertyTableModel(); + changedModel = new EntityListModel(); + deletedModel = new EntityListModel(); + JTable propsTable = new JTable(propertiesModel); + final JList changedList = new JList(changedModel); + JList deletedList = new JList(deletedModel); + + JScrollPane scroll = new JScrollPane(propsTable); + scroll.setBorder(BorderFactory.createTitledBorder("Properties")); + add(scroll, BorderLayout.EAST); + scroll = new JScrollPane(changedList); + scroll.setBorder(BorderFactory.createTitledBorder("Changed entities")); + add(scroll, BorderLayout.CENTER); + scroll = new JScrollPane(deletedList); + scroll.setBorder(BorderFactory.createTitledBorder("Deleted entities")); + add(scroll, BorderLayout.WEST); + + changedList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + EntityID id = (EntityID)changedList.getSelectedValue(); + if (id != null) { + Set<Property> changedProperties = changes.getChangedProperties(id); + propertiesModel.show(changedProperties); + } + } + }); + } + + /** + Display a ChangeSet. + @param newChanges The ChangeSet to show. + */ + public void show(ChangeSet newChanges) { + changes = newChanges; + propertiesModel.show(new HashSet<Property>()); + changedModel.show(changes.getChangedEntities()); + deletedModel.show(changes.getDeletedEntities()); + } + + private static class EntityListModel extends AbstractListModel { + private List<EntityID> ids; + + public EntityListModel() { + ids = new ArrayList<EntityID>(); + } + + public void show(Set<EntityID> newIDs) { + ids.clear(); + ids.addAll(newIDs); + } + + public int getSize() { + return ids.size(); + } + + public Object getElementAt(int index) { + return ids.get(index); + } + } + + private static class PropertyTableModel extends AbstractTableModel { + private List<Property> properties; + + public PropertyTableModel() { + properties = new ArrayList<Property>(); + } + + public void show(Set<Property> p) { + properties.clear(); + properties.addAll(p); + } + + public int getRowCount() { + return properties.size(); + } + + public int getColumnCount() { + return 2; + } + + public Object getValueAt(int row, int col) { + if (row < 0 || row >= properties.size()) { + throw new IllegalArgumentException("Illegal row: " + row); + } + Property p = properties.get(row); + switch (col) { + case 0: + return p.getURN(); + case 1: + return p.isDefined() ? "undefined" : p.getValue().toString(); + default: + throw new IllegalArgumentException("Illegal column: " + col); + } + } + + public String getColumnName(int col) { + switch (col) { + case 0: + return "URN"; + case 1: + return "Value"; + default: + throw new IllegalArgumentException("Illegal column: " + col); + } + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/ConfigTree.java b/modules/rescuecore2/src/rescuecore2/misc/gui/ConfigTree.java new file mode 100644 index 0000000000000000000000000000000000000000..4c9403ec02308e9425de9e14bf49f1f16fce1d6e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/ConfigTree.java @@ -0,0 +1,213 @@ +package rescuecore2.misc.gui; + +import java.awt.Component; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.MouseEvent; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JTree; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.ToolTipManager; +import javax.swing.AbstractCellEditor; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeCellEditor; +import javax.swing.tree.TreePath; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.EventObject; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigConstraint; + +/** + A JTree that knows how to display and edit Config objects. +*/ +public class ConfigTree extends JTree { + private Config config; + + /** + Create a ConfigTree that will display a particular Config. + @param config The Config to display. + */ + public ConfigTree(Config config) { + this.config = config; + DefaultMutableTreeNode root = new DefaultMutableTreeNode("Config"); + List<String> keys = new ArrayList<String>(config.getAllKeys()); + Collections.sort(keys); + buildModel(root, keys); + setModel(new DefaultTreeModel(root)); + setEditable(true); + setCellRenderer(new ConfigEntryCellRenderer()); + setCellEditor(new ConfigEntryCellEditor()); + setInvokesStopCellEditing(true); + ToolTipManager.sharedInstance().registerComponent(this); + } + + private void buildModel(DefaultMutableTreeNode root, Collection<String> keys) { + Map<String, DefaultMutableTreeNode> branches = new HashMap<String, DefaultMutableTreeNode>(); + for (String next : keys) { + String[] branchNames = next.split("\\."); + // Create all parent branches if required + DefaultMutableTreeNode parent = root; + StringBuilder branchName = new StringBuilder(); + for (int i = 0; i < branchNames.length - 1; ++i) { + branchName.append(branchNames[i]); + String name = branchName.toString(); + DefaultMutableTreeNode nextParent = branches.get(name); + if (nextParent == null) { + nextParent = new DefaultMutableTreeNode(new ConfigCategoryNode(name)); + branches.put(name, nextParent); + parent.add(nextParent); + } + branchName.append("."); + parent = nextParent; + } + // Create the leaf node + DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(new ConfigEntryNode(next, config.getValue(next))); + parent.add(leaf); + } + } + + private static final class ConfigCategoryNode { + private String prefix; + + private ConfigCategoryNode(String prefix) { + this.prefix = prefix; + } + + @Override + public String toString() { + return prefix; + } + } + + private static final class ConfigEntryNode { + private String key; + private String value; + + private ConfigEntryNode(String key, String value) { + this.key = key; + this.value = value; + } + + String getKey() { + return key; + } + + String getValue() { + return value; + } + + @Override + public String toString() { + return key + ": " + value; + } + } + + private final class ConfigEntryCellRenderer extends JLabel implements TreeCellRenderer { + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + DefaultMutableTreeNode node = ((DefaultMutableTreeNode)value); + Object o = node.getUserObject(); + setText(o.toString()); + setOpaque(false); + setToolTipText(null); + if (o instanceof ConfigEntryNode) { + ConfigEntryNode entry = (ConfigEntryNode)o; + String key = entry.getKey(); + StringBuilder problems = new StringBuilder(); + for (ConfigConstraint constraint : config.getViolatedConstraints()) { + if (constraint.getKeys().contains(key)) { + if (problems.length() != 0) { + problems.append("\n"); + } + problems.append(constraint.getDescription()); + } + } + if (problems.length() != 0) { + setBackground(Color.RED); + setOpaque(true); + setToolTipText(problems.toString()); + } + } + return this; + } + } + + private final class ConfigEntryCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener { + private JPanel panel; + private JLabel label; + private JTextField field; + private DefaultMutableTreeNode node; + private String key; + + private ConfigEntryCellEditor() { + panel = new JPanel(new BorderLayout()); + label = new JLabel(); + field = new JTextField(); + panel.add(label, BorderLayout.CENTER); + panel.add(field, BorderLayout.EAST); + field.addActionListener(this); + node = null; + } + + @Override + public boolean isCellEditable(EventObject o) { + if (o instanceof MouseEvent && o.getSource() instanceof JTree) { + JTree tree = (JTree)o.getSource(); + MouseEvent e = (MouseEvent)o; + if (e.getClickCount() > 1) { + TreePath path = tree.getPathForLocation(e.getX(), e.getY()); + Object leaf = path.getLastPathComponent(); + if (leaf instanceof DefaultMutableTreeNode) { + Object content = ((DefaultMutableTreeNode)leaf).getUserObject(); + return content instanceof ConfigEntryNode; + } + } + } + return false; + } + + @Override + public Component getTreeCellEditorComponent(JTree tree, Object data, boolean selected, boolean expanded, boolean leaf, int row) { + node = ((DefaultMutableTreeNode)data); + ConfigEntryNode entry = (ConfigEntryNode)node.getUserObject(); + key = entry.getKey(); + label.setText(key + ": "); + field.setText(entry.getValue()); + return panel; + } + + @Override + public Object getCellEditorValue() { + return new ConfigEntryNode(key, field.getText()); + } + + @Override + public boolean stopCellEditing() { + if (node != null) { + String value = field.getText(); + node.setUserObject(new ConfigEntryNode(key, value)); + config.setValue(key, value); + } + fireEditingStopped(); + return true; + } + + @Override + public void actionPerformed(ActionEvent e) { + stopCellEditing(); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/DrawingTools.java b/modules/rescuecore2/src/rescuecore2/misc/gui/DrawingTools.java new file mode 100644 index 0000000000000000000000000000000000000000..c0ff0d8c46e08f6933536142594d4a25edd33be6 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/DrawingTools.java @@ -0,0 +1,88 @@ +package rescuecore2.misc.gui; + +import rescuecore2.misc.Pair; + +import java.awt.Graphics; + +/** + A bunch of useful functions for drawing things. + */ +public final class DrawingTools { + /** A default angle for arrow barbs. */ + public static final double DEFAULT_ARROW_ANGLE = Math.toRadians(135); + /** A default length (in pixels) for arrow barbs. */ + public static final double DEFAULT_ARROW_LENGTH = 5; + /** A default distance along the line for arrow barbs. */ + public static final double DEFAULT_ARROW_DISTANCE = 0.5; + + private DrawingTools() {} + + /** + Get the coordinates for arrow heads along a line. + @param startX The start of the line (X). + @param startY The start of the line (Y). + @param endX The end of the line (X). + @param endY The end of the line (Y). + @param angle The angle of the arrow barbs. + @param length The length of the arrow barbs. + @param d The distance along the line to place the barbs. This must be between zero and one. + @return A pair of coordinate pairs for the ends of the arrow barbs. + */ + public static Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> getArrowHeads(int startX, int startY, int endX, int endY, double angle, double length, double d) { + double dx = endX - startX; + double dy = endY - startY; + double headX = startX + (d * dx); + double headY = startY + (d * dy); + double vectorX = ((Math.cos(angle) * dx) - (Math.sin(angle) * dy)); + double vectorY = ((Math.sin(angle) * dx) + (Math.cos(angle) * dy)); + // Normalise the vector + double vLength = Math.hypot(vectorX, vectorY); + vectorX /= vLength; + vectorY /= vLength; + // Now calculate end points + double leftX = headX + (vectorX * length); + double leftY = headY + (vectorY * length); + double rightX = headX - (vectorY * length); + double rightY = headY + (vectorX * length); + Pair<Integer, Integer> left = new Pair<Integer, Integer>((int)leftX, (int)leftY); + Pair<Integer, Integer> right = new Pair<Integer, Integer>((int)rightX, (int)rightY); + return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(left, right); + } + + /** + Draw an arrowhead on a line. + @param startX The start of the line (X). + @param startY The start of the line (Y). + @param endX The end of the line (X). + @param endY The end of the line (Y). + @param angle The angle of the arrow barbs. + @param length The length of the arrow barbs. + @param d The distance along the line to draw the barbs. This must be between zero and one. + @param g The graphics object to draw on. + */ + public static void drawArrowHeads(int startX, int startY, int endX, int endY, double angle, double length, double d, Graphics g) { + Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> barbs = getArrowHeads(startX, startY, endX, endY, angle, length, d); + int leftX = barbs.first().first(); + int leftY = barbs.first().second(); + int rightX = barbs.second().first(); + int rightY = barbs.second().second(); + double dx = endX - startX; + double dy = endY - startY; + int headX = (int)(startX + (d * dx)); + int headY = (int)(startY + (d * dy)); + g.drawLine(leftX, leftY, headX, headY); + g.drawLine(rightX, rightY, headX, headY); + } + + /** + Draw an arrowhead on a line with some default options. + @param startX The start of the line (X). + @param startY The start of the line (Y). + @param endX The end of the line (X). + @param endY The end of the line (Y). + @param g The graphics object to draw on. + */ + public static void drawArrowHeads(int startX, int startY, int endX, int endY, Graphics g) { + drawArrowHeads(startX, startY, endX, endY, DEFAULT_ARROW_ANGLE, DEFAULT_ARROW_LENGTH, DEFAULT_ARROW_DISTANCE, g); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/ListModelList.java b/modules/rescuecore2/src/rescuecore2/misc/gui/ListModelList.java new file mode 100644 index 0000000000000000000000000000000000000000..3d0d4bfeebc3bec2a172a2d35c30222eed3bba6f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/ListModelList.java @@ -0,0 +1,292 @@ +package rescuecore2.misc.gui; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; +import javax.swing.ListModel; +import javax.swing.AbstractListModel; +import javax.swing.event.ListDataListener; + +/** + A java.util.List implementation that also implements javax.swing.ListModel. This class delegates to a real list for storing data. + @param <T> The type of object that lives in this list. + */ +public class ListModelList<T> extends AbstractListModel implements List<T>, ListModel { + private List<T> downstream; + + /** + Construct a ListModelList backed by an ArrayList. + */ + public ListModelList() { + this(new ArrayList<T>()); + } + + /** + Construct a ListModelList backed by a List. + @param downstream The backing List. + */ + public ListModelList(List<T> downstream) { + this.downstream = downstream; + } + + // ListModel interface + @Override + public Object getElementAt(int index) { + return downstream.get(index); + } + + @Override + public int getSize() { + return downstream.size(); + } + + // List interface + @Override + public boolean add(T t) { + boolean result = downstream.add(t); + if (result) { + fireIntervalAdded(this, Math.max(0, downstream.size() - 1), Math.max(0, downstream.size() - 1)); + } + return result; + } + + @Override + public void add(int index, T element) { + downstream.add(index, element); + fireContentsChanged(this, index, Math.max(0, downstream.size() - 1)); + } + + @Override + public boolean addAll(Collection<? extends T> c) { + int oldSize = downstream.size(); + boolean result = downstream.addAll(c); + fireIntervalAdded(this, oldSize, Math.max(0, downstream.size() - 1)); + return result; + } + + @Override + public boolean addAll(int index, Collection<? extends T> c) { + boolean result = downstream.addAll(index, c); + fireContentsChanged(this, index, Math.max(0, downstream.size() - 1)); + return result; + } + + @Override + public void clear() { + int size = downstream.size(); + downstream.clear(); + fireIntervalRemoved(this, 0, Math.max(0, size - 1)); + } + + @Override + public boolean contains(Object o) { + return downstream.contains(o); + } + + @Override + public boolean containsAll(Collection<?> c) { + return downstream.containsAll(c); + } + + @Override + public T get(int index) { + return downstream.get(index); + } + + @Override + public int indexOf(Object o) { + return downstream.indexOf(o); + } + + @Override + public boolean isEmpty() { + return downstream.isEmpty(); + } + + @Override + public Iterator<T> iterator() { + return new NotifyingIterator<T>(downstream.iterator()); + } + + @Override + public int lastIndexOf(Object o) { + return downstream.lastIndexOf(o); + } + + @Override + public ListIterator<T> listIterator() { + return new NotifyingListIterator<T>(downstream.listIterator()); + } + + @Override + public ListIterator<T> listIterator(int index) { + return new NotifyingListIterator<T>(downstream.listIterator(index), index); + } + + @Override + public T remove(int index) { + T result = downstream.remove(index); + fireIntervalRemoved(this, index, index); + return result; + } + + @Override + public boolean remove(Object o) { + int index = indexOf(o); + boolean result = downstream.remove(o); + if (result) { + fireIntervalRemoved(this, index, index); + } + return result; + } + + @Override + public boolean removeAll(Collection<?> c) { + int oldSize = downstream.size(); + boolean result = downstream.removeAll(c); + if (result) { + fireIntervalRemoved(this, downstream.size(), Math.max(0, oldSize - 1)); + fireContentsChanged(this, 0, Math.max(0, downstream.size() - 1)); + } + return result; + } + + @Override + public boolean retainAll(Collection<?> c) { + int oldSize = downstream.size(); + boolean result = downstream.retainAll(c); + if (result) { + fireIntervalRemoved(this, downstream.size(), Math.max(0, oldSize - 1)); + fireContentsChanged(this, 0, Math.max(0, downstream.size() - 1)); + } + return result; + } + + @Override + public T set(int index, T element) { + T result = downstream.set(index, element); + fireContentsChanged(this, index, index); + return result; + } + + @Override + public int size() { + return downstream.size(); + } + + @Override + public List<T> subList(int fromIndex, int toIndex) { + ListModelList<T> sublist = new ListModelList<T>(downstream.subList(fromIndex, toIndex)); + for (ListDataListener l : getListDataListeners()) { + sublist.addListDataListener(l); + } + return sublist; + } + + @Override + public Object[] toArray() { + return downstream.toArray(); + } + + @Override + public <S> S[] toArray(S[] a) { + return downstream.toArray(a); + } + + private class NotifyingIterator<T> implements Iterator<T> { + private int index; + private Iterator<T> downstream; + + NotifyingIterator(Iterator<T> downstream) { + index = 0; + this.downstream = downstream; + } + + @Override + public boolean hasNext() { + return downstream.hasNext(); + } + + @Override + public T next() { + T result = downstream.next(); + ++index; + return result; + } + + @Override + public void remove() { + downstream.remove(); + fireIntervalRemoved(ListModelList.this, index, index); + } + } + + private class NotifyingListIterator<T> implements ListIterator<T> { + private int index; + private ListIterator<T> downstream; + + NotifyingListIterator(ListIterator<T> downstream) { + this(downstream, 0); + } + + NotifyingListIterator(ListIterator<T> downstream, int index) { + this.index = index; + this.downstream = downstream; + } + + @Override + public void add(T t) { + downstream.add(t); + ++index; + fireIntervalAdded(ListModelList.this, index, index); + } + + @Override + public boolean hasNext() { + return downstream.hasNext(); + } + + @Override + public boolean hasPrevious() { + return downstream.hasPrevious(); + } + + @Override + public T next() { + T result = downstream.next(); + ++index; + return result; + } + + @Override + public int nextIndex() { + return downstream.nextIndex(); + } + + @Override + public T previous() { + T result = downstream.previous(); + --index; + return result; + } + + @Override + public int previousIndex() { + return downstream.previousIndex(); + } + + @Override + public void remove() { + downstream.remove(); + fireIntervalRemoved(ListModelList.this, index, index); + } + + @Override + public void set(T t) { + downstream.set(t); + fireContentsChanged(ListModelList.this, index, index); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/PanZoomListener.java b/modules/rescuecore2/src/rescuecore2/misc/gui/PanZoomListener.java new file mode 100644 index 0000000000000000000000000000000000000000..1ddf303fcf28f35accc72e05a9fc26d4f02c3bc3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/PanZoomListener.java @@ -0,0 +1,200 @@ +package rescuecore2.misc.gui; + +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.Point; +import java.awt.Insets; +import javax.swing.JComponent; + +/** + A mouse listener that will handle panning and zooming in conjunction with a ScreenTransform. + */ +public class PanZoomListener implements MouseListener, MouseMotionListener, MouseWheelListener { + private static final int DEFAULT_MOUSE_ZOOM_THRESHOLD = 100; + private double mouseDownX; + private double mouseDownY; + private int zoomMouseDownY; + private boolean dragging; + private boolean zooming; + private ScreenTransform transform; + private JComponent component; + private boolean enabled; + private int panTriggerModifiers; + private int zoomTriggerModifiers; + private int zoomThreshold; + + /** + Construct a PanZoomListener that listens for events on a JComponent. + @param component The component to listen for mouse events on. + */ + public PanZoomListener(JComponent component) { + this.component = component; + component.addMouseListener(this); + component.addMouseMotionListener(this); + component.addMouseWheelListener(this); + panTriggerModifiers = InputEvent.BUTTON1_DOWN_MASK; + zoomTriggerModifiers = InputEvent.BUTTON2_DOWN_MASK; + zoomThreshold = DEFAULT_MOUSE_ZOOM_THRESHOLD; + enabled = true; + } + + /** + Set the screen transform. + @param t The screen transform. + */ + public void setScreenTransform(ScreenTransform t) { + this.transform = t; + } + + /** + Enable or disable this PanZoomListener. + @param b Whether to process events or not. + */ + public void setEnabled(boolean b) { + enabled = b; + if (!enabled) { + dragging = false; + zooming = false; + } + } + + /** + Set the modifiers that will trigger zooming. + @param modifiers The modifiers mask that must be set for zooming to begin. + */ + public void setZoomTriggerModifiers(int modifiers) { + zoomTriggerModifiers = modifiers; + zooming = false; + } + + /** + Set the modifiers that will trigger panning. + @param modifiers The modifiers mask that must be set for panning to begin. + */ + public void setPanTriggerModifiers(int modifiers) { + panTriggerModifiers = modifiers; + dragging = false; + } + + /** + Set the pan trigger modifiers so that pressing the left mouse button triggers panning. + */ + public void setPanOnLeftMouse() { + setPanTriggerModifiers(InputEvent.BUTTON1_DOWN_MASK); + } + + /** + Set the pan trigger modifiers so that pressing the right mouse button triggers panning. + */ + public void setPanOnRightMouse() { + setPanTriggerModifiers(InputEvent.BUTTON3_DOWN_MASK); + } + + @Override + public void mousePressed(MouseEvent e) { + if (!enabled) { + return; + } + if (transform == null) { + return; + } + if ((e.getModifiersEx() & panTriggerModifiers) == panTriggerModifiers) { + Point p = fixEventPoint(e.getPoint()); + mouseDownX = transform.screenToX(p.x); + mouseDownY = transform.screenToY(p.y); + dragging = true; + } + if ((e.getModifiersEx() & zoomTriggerModifiers) == zoomTriggerModifiers) { + zoomMouseDownY = e.getPoint().y; + zooming = true; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (!enabled) { + return; + } + if ((e.getModifiersEx() & panTriggerModifiers) != panTriggerModifiers) { + dragging = false; + } + if ((e.getModifiersEx() & zoomTriggerModifiers) != zoomTriggerModifiers) { + zooming = false; + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (!enabled) { + return; + } + if (transform == null) { + return; + } + if (dragging) { + Point p = fixEventPoint(e.getPoint()); + transform.makeCentreRelativeTo(mouseDownX, mouseDownY, p.x, p.y); + component.repaint(); + } + if (zooming) { + int newY = e.getPoint().y; + if (newY < zoomMouseDownY - zoomThreshold) { + transform.zoomIn(); + zoomMouseDownY = newY; + component.repaint(); + } + else if (newY > zoomMouseDownY + zoomThreshold) { + transform.zoomOut(); + zoomMouseDownY = newY; + component.repaint(); + } + } + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if (!enabled) { + return; + } + if (transform == null) { + return; + } + if (e.getWheelRotation() < 0) { + Point p = fixEventPoint(e.getPoint()); + double x = transform.screenToX(p.x); + double y = transform.screenToY(p.y); + transform.zoomIn(); + transform.makeCentreRelativeTo(x, y, p.x, p.y); + component.repaint(); + } + if (e.getWheelRotation() > 0) { + Point p = fixEventPoint(e.getPoint()); + double x = transform.screenToX(p.x); + double y = transform.screenToY(p.y); + transform.zoomOut(); + transform.makeCentreRelativeTo(x, y, p.x, p.y); + component.repaint(); + } + } + + @Override + public void mouseClicked(MouseEvent e) {} + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) {} + + @Override + public void mouseMoved(MouseEvent e) {} + + private Point fixEventPoint(Point p) { + Insets insets = component.getInsets(); + return new Point(p.x - insets.left, p.y - insets.top); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/ScreenTransform.java b/modules/rescuecore2/src/rescuecore2/misc/gui/ScreenTransform.java new file mode 100644 index 0000000000000000000000000000000000000000..1d57dbb35816d0f24b63956f39ce8cbf074a4d3c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/ScreenTransform.java @@ -0,0 +1,224 @@ +package rescuecore2.misc.gui; + +import java.awt.geom.Rectangle2D; +import java.awt.geom.Point2D; +import java.awt.Shape; + +/** + A class that handles transforming between world coordinates and screen coordinates. This class should be used rather than affine transforms when you want to draw something in the right position (in world coordinates) but with a particular on-screen size. Affine transforms make it hard to control the output size. + */ +public class ScreenTransform { + private double minX; + private double minY; + private double xRange; + private double yRange; + private double centreX; + private double centreY; + private double zoom; + + private double pixelsPerX; + private double pixelsPerY; + private int xOffset; + private int yOffset; + private int lastScreenWidth; + private int lastScreenHeight; + private Rectangle2D viewBounds; + + private boolean fixedAspectRatio; + + /** + Create a ScreenTransform that covers a particular world coordinate range. + @param minX The minimum X world coordinate. + @param minY The minimum Y world coordinate. + @param maxX The maximum X world coordinate. + @param maxY The maximum Y world coordinate. + */ + public ScreenTransform(double minX, double minY, double maxX, double maxY) { + this.minX = minX; + this.minY = minY; + this.xRange = maxX - minX; + this.yRange = maxY - minY; + this.centreX = (minX + maxX) / 2; + this.centreY = (minY + maxY) / 2; + this.zoom = 1; + fixedAspectRatio = true; + } + + /** + Set the on-screen centre point. + @param x The X world coordinate to put in the centre. + @param y The Y world coordinate to put in the centre. + */ + public void setCentrePoint(double x, double y) { + centreX = x; + centreY = y; + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Set the on-screen center point relative to another point. + @param x The world X coordinate. + @param y The world Y coordinate. + @param screenX The screen X coordinate. + @param screenY The screen Y coordinate. + */ + public void makeCentreRelativeTo(double x, double y, int screenX, int screenY) { + int dx = screenX - (lastScreenWidth / 2); + int dy = screenY - (lastScreenHeight / 2); + centreX = x - (dx / pixelsPerX); + centreY = y + (dy / pixelsPerY); + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Set whether to use a fixed aspect ratio or not. + @param b True to use a fixed aspect ratio, false otherwise. + */ + public void setFixedAspectRatio(boolean b) { + fixedAspectRatio = b; + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Increase the zoom level by one step. + */ + public void zoomIn() { + zoom *= 1.5d; + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Decrease the zoom level by one step. + */ + public void zoomOut() { + zoom /= 1.5d; + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Reset the zoom level to one. This also clears the fixed point coordinate. + */ + public void resetZoom() { + zoom = 1; + centreX = minX + (xRange / 2); + centreY = minY + (yRange / 2); + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Zoom and translate to show a particular rectangle. + @param bounds The bounds of the rectangle to show. + */ + public void show(Rectangle2D bounds) { + centreX = bounds.getMinX() + (bounds.getWidth() / 2); + centreY = bounds.getMinY() + (bounds.getHeight() / 2); + double xZoom = xRange / bounds.getWidth(); + double yZoom = yRange / bounds.getHeight(); + zoom = Math.min(xZoom, yZoom); + rescale(lastScreenWidth, lastScreenHeight); + } + + /** + Recalculate the transform based on screen geometry. + @param width The width of the screen. + @param height The height of the screen. + */ + public void rescale(int width, int height) { + xOffset = 0; + yOffset = height; + pixelsPerX = (width / xRange) * zoom; + pixelsPerY = (height / yRange) * zoom; + if (fixedAspectRatio) { + if (pixelsPerX < pixelsPerY) { + pixelsPerY = pixelsPerX; + } + else if (pixelsPerY < pixelsPerX) { + pixelsPerX = pixelsPerY; + } + } + // Work out how to offset points so that the centre point is in the right place + // System.out.println("pixelsPerX = " + pixelsPerX + ", pixelsPerY = " + pixelsPerY); + // System.out.println("Before adjustment: fixed point " + fixedX + ", " + fixedY + " should be at " + fixedScreenX + ", " + fixedScreenY + "; actually at " + xToScreen(fixedX) + ", " + yToScreen(fixedY)); + int actualCentreX = xToScreen(centreX); + xOffset = (width / 2) - actualCentreX; + int actualCentreY = yToScreen(centreY); + yOffset = height + (height / 2) - actualCentreY; + // System.out.println("xOffset = " + xOffset + ", yOffset = " + yOffset); + // System.out.println("Fixed point now at " + xToScreen(fixedX) + ", " + yToScreen(fixedY)); + lastScreenWidth = width; + lastScreenHeight = height; + double x1 = screenToX(0); + double x2 = screenToX(width); + double y1 = screenToY(height); + double y2 = screenToY(0); + viewBounds = new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1); + } + + /** + Find out if a shape is inside the current view bounds. + @param s The shape to test. + @return True if any part of the shape is inside the current view bounds, false otherwise. + */ + public boolean isInView(Shape s) { + if (s == null) { + return false; + } + return s.intersects(viewBounds); + } + + /** + Find out if a point is inside the current view bounds. + @param p The point to test. + @return True if the point is inside the current view bounds, false otherwise. + */ + public boolean isInView(Point2D p) { + if (p == null) { + return false; + } + return viewBounds.contains(p); + } + + /** + Get the current view bounds in world coordinates. + @return The view bounds. + */ + public Rectangle2D getViewBounds() { + return viewBounds; + } + + /** + Convert a world X coordinate to a screen coordinate. + @param x The world X coordinate. + @return The screen coordinate. + */ + public int xToScreen(double x) { + return xOffset + (int)Math.round((x - minX) * pixelsPerX); + } + + /** + Convert a world Y coordinate to a screen coordinate. + @param y The world Y coordinate. + @return The screen coordinate. + */ + public int yToScreen(double y) { + return yOffset - (int)Math.round((y - minY) * pixelsPerY); + } + + /** + Convert a screen X coordinate to a world coordinate. + @param x The screen X coordinate. + @return The world coordinate. + */ + public double screenToX(int x) { + return ((x - xOffset) / pixelsPerX) + minX; + } + + /** + Convert a screen Y coordinate to a world coordinate. + @param y The screen Y coordinate. + @return The world coordinate. + */ + public double screenToY(int y) { + return ((yOffset - y) / pixelsPerY) + minY; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/gui/ShapeDebugFrame.java b/modules/rescuecore2/src/rescuecore2/misc/gui/ShapeDebugFrame.java new file mode 100644 index 0000000000000000000000000000000000000000..b95fb6f12d8c6636a8792df98a92eece5e38a833 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/gui/ShapeDebugFrame.java @@ -0,0 +1,881 @@ +package rescuecore2.misc.gui; + +import javax.swing.JFrame; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.BorderFactory; +import javax.swing.Action; +import javax.swing.AbstractAction; +import javax.swing.JPopupMenu; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Insets; +import java.awt.BasicStroke; +import java.awt.Rectangle; +import java.awt.Point; +import java.awt.geom.Area; +import java.awt.geom.PathIterator; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.BrokenBarrierException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; + +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.GeometryTools2D; +//import rescuecore2.log.Logger; + +/** + A JFrame that can be used to debug geometric shape operations. When {@link #enable enabled} this frame will block whenever a show method is called until the user clicks on a button to continue. The "step" button will cause the show method to return and leave the frame visible and activated. The "continue" button will hide and {@link #deactivate} the frame so that further calls to show will return immediately. + */ +public class ShapeDebugFrame extends JFrame { + private static final int DISPLAY_WIDTH = 500; + private static final int DISPLAY_HEIGHT = 500; + private static final int LEGEND_WIDTH = 500; + private static final int LEGEND_HEIGHT = 500; + + private static final double ZOOM_TO_OFFSET = 0.1; + private static final double ZOOM_TO_WIDTH_FACTOR = 1.2; + + private JLabel title; + private JButton step; + private JButton cont; + private ShapeViewer viewer; + private ShapeInfoLegend legend; + private CyclicBarrier barrier; + private boolean enabled; + private Collection<? extends ShapeInfo> background; + private boolean backgroundEnabled; + private JPopupMenu menu; + private boolean autoZoom; + + /** + Construct a new ShapeDebugFrame. + */ + public ShapeDebugFrame() { + barrier = new CyclicBarrier(2); + viewer = new ShapeViewer(); + legend = new ShapeInfoLegend(); + step = new JButton("Step"); + cont = new JButton("Continue"); + title = new JLabel(); + add(title, BorderLayout.NORTH); + add(viewer, BorderLayout.CENTER); + JPanel buttons = new JPanel(new GridLayout(1, 2)); + buttons.add(step); + buttons.add(cont); + add(buttons, BorderLayout.SOUTH); + add(legend, BorderLayout.EAST); + legend.setBorder(BorderFactory.createTitledBorder("Legend")); + viewer.setBorder(BorderFactory.createTitledBorder("Shapes")); + step.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + barrier.await(); + } + // CHECKSTYLE:OFF:EmptyBlock + catch (InterruptedException ex) { + // Ignore + } + catch (BrokenBarrierException ex) { + // Ignore + } + // CHECKSTYLE:ON:EmptyBlock + } + }); + cont.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + deactivate(); + barrier.await(); + } + // CHECKSTYLE:OFF:EmptyBlock + catch (InterruptedException ex) { + // Ignore + } + catch (BrokenBarrierException ex) { + // Ignore + } + // CHECKSTYLE:ON:EmptyBlock + } + }); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + try { + barrier.await(); + } + // CHECKSTYLE:OFF:EmptyBlock + catch (InterruptedException ex) { + // Ignore + } + catch (BrokenBarrierException ex) { + // Ignore + } + // CHECKSTYLE:ON:EmptyBlock + } + }); + MouseAdapter m = new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + menu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); + } + } + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + menu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); + } + } + @Override + public void mouseClicked(MouseEvent e) { + if (e.isPopupTrigger()) { + menu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); + } + } + }; + addMouseListener(m); + viewer.addMouseListener(m); + enabled = true; + clearBackground(); + backgroundEnabled = true; + autoZoom = true; + pack(); + menu = new JPopupMenu(); + menu.add(new BackgroundAction()); + } + + /** + Set the "background" shapes. These will be drawn on every invocation of show. + @param back The new background shapes. This should not be null. + */ + public void setBackground(Collection<? extends ShapeInfo> back) { + background = back; + if (background == null) { + clearBackground(); + } + } + + /** + Clear the "background" shapes. + */ + public void clearBackground() { + background = new ArrayList<ShapeInfo>(); + } + + /** + Set whether the background is drawn or not. + @param b True if the background should be drawn, false otherwise. + */ + public void setBackgroundEnabled(boolean b) { + backgroundEnabled = b; + } + + /** + Set whether autozoom is enabled. + @param b True if autozoom should be enabled, false otherwise. + */ + public void setAutozoomEnabled(boolean b) { + autoZoom = b; + } + + /** + Show a set of ShapeInfo objects. If this frame is enabled then this method will block until the user clicks a button to continue. + @param description A description. + @param shapes A list of collections of ShapeInfo objects. + */ + public void show(String description, Collection<? extends ShapeInfo>... shapes) { + List<ShapeInfo> all = new ArrayList<ShapeInfo>(); + for (Collection<? extends ShapeInfo> next : shapes) { + all.addAll(next); + } + show(description, all); + } + + /** + Show a set of ShapeInfo objects. If this frame is enabled then this method will block until the user clicks a button to continue. + @param description A description. + @param shapes An array of ShapeInfo objects. + */ + public void show(String description, ShapeInfo... shapes) { + show(description, Arrays.asList(shapes)); + } + + /** + Show a set of ShapeInfo objects. If this frame is enabled then this method will block until the user clicks a button to continue. + @param description A description. + @param shapes A collection of ShapeInfo objects. + */ + public void show(final String description, final Collection<ShapeInfo> shapes) { + if (!enabled) { + return; + } + final List<ShapeInfo> allShapes = new ArrayList<ShapeInfo>(shapes); + setVisible(true); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (description == null) { + title.setText(""); + } + else { + title.setText(description); + } + legend.setShapes(allShapes); + viewer.setShapes(allShapes); + if (autoZoom) { + viewer.zoomTo(shapes); + } + repaint(); + } + }); + try { + barrier.await(); + } + // CHECKSTYLE:OFF:EmptyBlock + catch (InterruptedException e) { + // Ignore + } + catch (BrokenBarrierException e) { + // Ignore + } + // CHECKSTYLE:ON:EmptyBlock + } + + /** + Activate this frame. Future calls to show will block until the user clicks a button. + */ + public void activate() { + enabled = true; + } + + /** + Deactivate and hides this frame. Future calls to show will return immediately. + */ + public void deactivate() { + enabled = false; + setVisible(false); + } + + private Rectangle2D getBounds(Collection<? extends ShapeInfo>... shapes) { + double minX = Double.POSITIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY; + double maxX = Double.NEGATIVE_INFINITY; + double maxY = Double.NEGATIVE_INFINITY; + for (Collection<? extends ShapeInfo> c : shapes) { + if (c != null) { + for (ShapeInfo next : c) { + Shape bounds = next.getBoundsShape(); + if (bounds != null) { + Rectangle2D rect = bounds.getBounds2D(); + minX = Math.min(minX, rect.getMinX()); + maxX = Math.max(maxX, rect.getMaxX()); + minY = Math.min(minY, rect.getMinY()); + maxY = Math.max(maxY, rect.getMaxY()); + } + java.awt.geom.Point2D point = next.getBoundsPoint(); + if (point != null) { + minX = Math.min(minX, point.getX()); + maxX = Math.max(maxX, point.getX()); + minY = Math.min(minY, point.getY()); + maxY = Math.max(maxY, point.getY()); + } + } + } + } + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } + + private class ShapeViewer extends JComponent { + private List<ShapeInfo> shapes; + private ScreenTransform transform; + private PanZoomListener panZoom; + private Map<Shape, ShapeInfo> drawnShapes; + + /** + Create a ShapeViewer. + */ + public ShapeViewer() { + panZoom = new PanZoomListener(this); + drawnShapes = new HashMap<Shape, ShapeInfo>(); + shapes = new ArrayList<ShapeInfo>(); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + Insets insets = getInsets(); + Point p = new Point(e.getPoint()); + p.translate(-insets.left, -insets.top); + List<ShapeInfo> s = getShapesAtPoint(p); + for (ShapeInfo next : s) { + System.out.println(next.getObject()); + } + } + } + }); + } + + @Override + public void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + drawnShapes.clear(); + if (shapes.isEmpty()) { + return; + } + Insets insets = getInsets(); + int width = getWidth() - insets.left - insets.right; + int height = getHeight() - insets.top - insets.bottom; + transform.rescale(width, height); + // Logger.debug("View bounds: " + transform.getViewBounds()); + for (ShapeInfo next : shapes) { + boolean visible = transform.isInView(next.getBoundsShape()) || transform.isInView(next.getBoundsPoint()); + if (visible) { + Graphics g = graphics.create(insets.left, insets.top, width, height); + Shape shape = next.paint((Graphics2D)g, transform); + if (shape != null) { + drawnShapes.put(shape, next); + } + } + // else { + // Logger.debug("Pruned " + next); + // Logger.debug("Shape bounds: " + next.getBoundsShape()); + // Logger.debug("Point bounds: " + next.getBoundsPoint()); + // } + } + if (backgroundEnabled) { + for (ShapeInfo next : background) { + boolean visible = transform.isInView(next.getBoundsShape()) || transform.isInView(next.getBoundsPoint()); + if (visible) { + Graphics g = graphics.create(insets.left, insets.top, width, height); + Shape shape = next.paint((Graphics2D)g, transform); + if (shape != null) { + drawnShapes.put(shape, next); + } + } + } + } + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(DISPLAY_WIDTH, DISPLAY_HEIGHT); + } + + /** + Set the list of ShapeInfo objects to draw. + @param s The new list of ShapeInfo objects. + */ + @SuppressWarnings("unchecked") + public void setShapes(Collection<ShapeInfo> s) { + shapes.clear(); + shapes.addAll(s); + Rectangle2D bounds = ShapeDebugFrame.this.getBounds(shapes, backgroundEnabled ? background : null); + transform = new ScreenTransform(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY()); + panZoom.setScreenTransform(transform); + repaint(); + } + + /** + Zoom to show a set of ShapeInfo objects. + @param zoom The set of objects to zoom to. + */ + @SuppressWarnings("unchecked") + public void zoomTo(Collection<ShapeInfo> zoom) { + Rectangle2D bounds = ShapeDebugFrame.this.getBounds(zoom); + // Increase the bounds by 10% + double newX = bounds.getMinX() - (bounds.getWidth() * ZOOM_TO_OFFSET); + double newY = bounds.getMinY() - (bounds.getHeight() * ZOOM_TO_OFFSET); + double newWidth = bounds.getWidth() * ZOOM_TO_WIDTH_FACTOR; + double newHeight = bounds.getHeight() * ZOOM_TO_WIDTH_FACTOR; + bounds.setRect(newX, newY, newWidth, newHeight); + transform.show(bounds); + repaint(); + } + + private List<ShapeInfo> getShapesAtPoint(Point p) { + List<ShapeInfo> result = new ArrayList<ShapeInfo>(); + for (Map.Entry<Shape, ShapeInfo> next : drawnShapes.entrySet()) { + Shape shape = next.getKey(); + if (shape.contains(p)) { + result.add(next.getValue()); + } + } + return result; + } + } + + /** + The legend for the debug frame. + */ + private class ShapeInfoLegend extends JComponent { + private static final int ROW_OFFSET = 5; + private static final int X_INDENT = 5; + private static final int ENTRY_WIDTH = 50; + private static final int ENTRY_HEIGHT = 9; + + private List<ShapeInfo> shapes; + + ShapeInfoLegend() { + shapes = new ArrayList<ShapeInfo>(); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(LEGEND_WIDTH, LEGEND_HEIGHT); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (shapes.isEmpty()) { + return; + } + Set<String> seen = new HashSet<String>(); + FontMetrics metrics = g.getFontMetrics(); + int height = metrics.getHeight(); + int y = getInsets().top; + int x = getInsets().left + X_INDENT; + if (backgroundEnabled) { + for (ShapeInfo next : background) { + String name = next.getName(); + if (name == null || "".equals(name)) { + continue; + } + if (seen.contains(name)) { + continue; + } + seen.add(name); + next.paintLegend((Graphics2D)g.create(x, y + (height / 2) - (ENTRY_HEIGHT / 2), ENTRY_WIDTH, ENTRY_HEIGHT), ENTRY_WIDTH, ENTRY_HEIGHT); + g.setColor(Color.black); + g.drawString(next.getName(), x + ENTRY_WIDTH + X_INDENT, y + metrics.getAscent()); + y += height + ROW_OFFSET; + } + } + for (ShapeInfo next : shapes) { + String name = next.getName(); + if (name == null || "".equals(name)) { + continue; + } + if (seen.contains(name)) { + continue; + } + seen.add(name); + next.paintLegend((Graphics2D)g.create(x, y + (height / 2) - (ENTRY_HEIGHT / 2), ENTRY_WIDTH, ENTRY_HEIGHT), ENTRY_WIDTH, ENTRY_HEIGHT); + g.setColor(Color.black); + g.drawString(next.getName(), x + ENTRY_WIDTH + X_INDENT, y + metrics.getAscent()); + y += height + ROW_OFFSET; + } + } + + /** + Set the list of shapes. + @param s The new list of shapes. + */ + public void setShapes(Collection<ShapeInfo> s) { + shapes.clear(); + shapes.addAll(s); + repaint(); + } + } + + /** + This class captures information about a shape that should be displayed on-screen. + */ + public abstract static class ShapeInfo { + /** The name of the shape. */ + protected String name; + /** The object this shape represents. */ + private Object object; + + /** + Construct a new ShapeInfo object. + @param object The object this shape represents. + @param name The name of the shape. + */ + protected ShapeInfo(Object object, String name) { + this.object = object; + this.name = name; + } + + /** + Paint this ShapeInfo on a Graphics2D object. + @param g The Graphics2D to draw on. + @param transform The current screen transform. + @return A shape for mouseover detection. + */ + public abstract Shape paint(Graphics2D g, ScreenTransform transform); + + /** + Paint this ShapeInfo on a the legend. + @param g The Graphics2D to draw on. + @param width The available width. + @param height The available height. + */ + public abstract void paintLegend(Graphics2D g, int width, int height); + + /** + Get the object this shape represents. + @return The object. + */ + public Object getObject() { + return object; + } + + /** + Get the name of this shape info. + @return The name. + */ + public String getName() { + return name; + } + + /** + Get the bounding shape of this shape. + @return The bounding shape or null if this shape represents a point. + */ + public abstract Shape getBoundsShape(); + + /** + Get the point representing this shape. + @return The shape point or null if this shape does not represent a point. + */ + public abstract java.awt.geom.Point2D getBoundsPoint(); + } + + /** + A ShapeInfo that encapsulates an awt Shape. + */ + public static class AWTShapeInfo extends ShapeInfo { + private Shape shape; + private boolean fill; + private Color colour; + private Rectangle2D bounds; + + /** + Construct a new AWTShapeInfo object. + @param shape The shape to display. + @param name The name of the shape. + @param colour The colour of the shape. + @param fill Whether to fill the shape. + */ + public AWTShapeInfo(Shape shape, String name, Color colour, boolean fill) { + super(shape, name); + this.shape = shape; + this.fill = fill; + this.colour = colour; + if (shape != null) { + bounds = shape.getBounds2D(); + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (shape == null || (shape instanceof Area && ((Area)shape).isEmpty())) { + return null; + } + Path2D path = new Path2D.Double(); + PathIterator pi = shape.getPathIterator(null); + // CHECKSTYLE:OFF:MagicNumber + double[] d = new double[6]; + while (!pi.isDone()) { + int type = pi.currentSegment(d); + switch (type) { + case PathIterator.SEG_MOVETO: + path.moveTo(transform.xToScreen(d[0]), transform.yToScreen(d[1])); + break; + case PathIterator.SEG_LINETO: + path.lineTo(transform.xToScreen(d[0]), transform.yToScreen(d[1])); + break; + case PathIterator.SEG_CLOSE: + path.closePath(); + break; + case PathIterator.SEG_QUADTO: + path.quadTo(transform.xToScreen(d[0]), transform.yToScreen(d[1]), transform.xToScreen(d[2]), transform.yToScreen(d[3])); + break; + case PathIterator.SEG_CUBICTO: + path.curveTo(transform.xToScreen(d[0]), transform.yToScreen(d[1]), transform.xToScreen(d[2]), transform.yToScreen(d[3]), transform.xToScreen(d[4]), transform.yToScreen(d[5])); + break; + default: + throw new RuntimeException("Unexpected PathIterator constant: " + type); + } + pi.next(); + } + // CHECKSTYLE:ON:MagicNumber + g.setColor(colour); + if (fill) { + g.fill(path); + } + else { + g.draw(path); + } + return path.createTransformedShape(null); + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (shape == null) { + return; + } + g.setColor(colour); + if (fill) { + g.fillRect(0, 0, width, height); + } + else { + g.drawRect(0, 0, width - 1, height - 1); + } + } + + @Override + public Rectangle2D getBoundsShape() { + return bounds; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return null; + } + } + + /** + A ShapeInfo that encapsulates a Point2D. + */ + public static class Point2DShapeInfo extends ShapeInfo { + private static final int SIZE = 3; + + private Point2D point; + private java.awt.geom.Point2D boundsPoint; + private boolean square; + private Color colour; + + /** + Construct a new Point2DShapeInfo object. + @param point The point to display. + @param name The name of the point. + @param colour The colour of the point. + @param square Whether to draw as a square or a cross. If false then a cross will be drawn. + */ + public Point2DShapeInfo(Point2D point, String name, Color colour, boolean square) { + super(point, name); + this.point = point; + this.square = square; + this.colour = colour; + if (point != null) { + boundsPoint = new java.awt.geom.Point2D.Double(point.getX(), point.getY()); + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (point == null) { + return null; + } + int x = transform.xToScreen(point.getX()); + int y = transform.yToScreen(point.getY()); + g.setColor(colour); + if (square) { + g.fillRect(x - SIZE, y - SIZE, SIZE * 2, SIZE * 2); + } + else { + g.drawLine(x - SIZE, y - SIZE, x + SIZE, y + SIZE); + g.drawLine(x - SIZE, y + SIZE, x + SIZE, y - SIZE); + } + // Logger.debug("Painting point " + name + " (" + point + ") at " + x + ", " + y); + return new Rectangle(x - SIZE, y - SIZE, SIZE * 2, SIZE * 2); + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (point == null) { + return; + } + g.setColor(colour); + int x = (width / 2); + int y = (height / 2); + if (square) { + g.fillRect(x - SIZE, y - SIZE, SIZE * 2, SIZE * 2); + } + else { + g.drawLine(x - SIZE, y - SIZE, x + SIZE, y + SIZE); + g.drawLine(x - SIZE, y + SIZE, x + SIZE, y - SIZE); + } + } + + @Override + public Shape getBoundsShape() { + return null; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return boundsPoint; + } + } + + /** + A ShapeInfo that encapsulates a Line2D. + */ + public static class Line2DShapeInfo extends ShapeInfo { + private static final int SIZE = 2; + private static final BasicStroke THICK_STROKE = new BasicStroke(SIZE * 3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final BasicStroke THIN_STROKE = new BasicStroke(SIZE, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + + private Collection<Line2D> lines; + private Shape bounds; + private boolean arrow; + private boolean thick; + private Color colour; + + /** + Construct a new Line2DShapeInfo object. + @param line The line to display. + @param name The name of the line. + @param colour The colour of the line. + @param thick Whether to draw the line with a thick stroke. + @param arrow Whether to draw an arrow showing the direction of the line. + */ + public Line2DShapeInfo(Line2D line, String name, Color colour, boolean thick, boolean arrow) { + this(Collections.singleton(line), name, colour, thick, arrow); + } + + /** + Construct a new Line2DShapeInfo object. + @param lines The lines to display. + @param name The name of the line. + @param colour The colour of the line. + @param thick Whether to draw the line with a thick stroke. + @param arrow Whether to draw an arrow showing the direction of the line. + */ + public Line2DShapeInfo(Collection<Line2D> lines, String name, Color colour, boolean thick, boolean arrow) { + super(lines, name); + this.lines = lines; + this.arrow = arrow; + this.thick = thick; + this.colour = colour; + if (lines.isEmpty()) { + return; + } + if (lines.size() == 1) { + Line2D l = lines.iterator().next(); + bounds = new java.awt.geom.Line2D.Double(l.getOrigin().getX(), l.getOrigin().getY(), l.getEndPoint().getX(), l.getEndPoint().getY()); + } + else { + double xMin = Double.POSITIVE_INFINITY; + double yMin = Double.POSITIVE_INFINITY; + double xMax = Double.NEGATIVE_INFINITY; + double yMax = Double.NEGATIVE_INFINITY; + for (Line2D line : lines) { + xMin = Math.min(xMin, line.getOrigin().getX()); + xMax = Math.max(xMax, line.getOrigin().getX()); + xMin = Math.min(xMin, line.getEndPoint().getX()); + xMax = Math.max(xMax, line.getEndPoint().getX()); + yMin = Math.min(yMin, line.getOrigin().getY()); + yMax = Math.max(yMax, line.getOrigin().getY()); + yMin = Math.min(yMin, line.getEndPoint().getY()); + yMax = Math.max(yMax, line.getEndPoint().getY()); + } + double xRange = xMax - xMin; + double yRange = yMax - yMin; + bounds = new Rectangle2D.Double(xMin, yMin, xMax - xMin, yMax - yMin); + if (GeometryTools2D.nearlyZero(xRange) || GeometryTools2D.nearlyZero(yRange)) { + bounds = new java.awt.geom.Line2D.Double(xMin, yMin, xMax, yMax); + } + } + } + + @Override + public Shape paint(Graphics2D g, ScreenTransform transform) { + if (lines.isEmpty()) { + return null; + } + if (thick) { + g.setStroke(THICK_STROKE); + } + else { + g.setStroke(THIN_STROKE); + } + g.setColor(colour); + Path2D result = new Path2D.Double(); + for (Line2D line : lines) { + Point2D start = line.getOrigin(); + Point2D end = line.getEndPoint(); + int x1 = transform.xToScreen(start.getX()); + int y1 = transform.yToScreen(start.getY()); + int x2 = transform.xToScreen(end.getX()); + int y2 = transform.yToScreen(end.getY()); + g.drawLine(x1, y1, x2, y2); + if (arrow) { + DrawingTools.drawArrowHeads(x1, y1, x2, y2, g); + } + result.moveTo(x1, y1); + result.lineTo(x2, y2); + // Logger.debug("Painting line " + name + " (" + line + ") from " + x1 + ", " + y1 + " -> " + x2 + ", " + y2); + } + return g.getStroke().createStrokedShape(result); + } + + @Override + public void paintLegend(Graphics2D g, int width, int height) { + if (thick) { + g.setStroke(THICK_STROKE); + } + else { + g.setStroke(THIN_STROKE); + } + g.setColor(colour); + g.drawLine(0, height / 2, width, height / 2); + if (arrow) { + DrawingTools.drawArrowHeads(0, height / 2, width, height / 2, g); + } + } + + @Override + public Shape getBoundsShape() { + return bounds; + } + + @Override + public java.awt.geom.Point2D getBoundsPoint() { + return null; + } + } + + private class BackgroundAction extends AbstractAction { + public BackgroundAction() { + super(backgroundEnabled ? "Hide background" : "Show background"); + putValue(Action.SELECTED_KEY, Boolean.valueOf(backgroundEnabled)); + } + + @Override + public void actionPerformed(ActionEvent e) { + boolean selected = ((Boolean)getValue(Action.SELECTED_KEY)).booleanValue(); + setBackgroundEnabled(!selected); + putValue(Action.SELECTED_KEY, Boolean.valueOf(backgroundEnabled)); + putValue(Action.NAME, backgroundEnabled ? "Hide background" : "Show background"); + ShapeDebugFrame.this.repaint(); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/misc/java/JavaTools.java b/modules/rescuecore2/src/rescuecore2/misc/java/JavaTools.java new file mode 100644 index 0000000000000000000000000000000000000000..35c7ff64e3493f459ab18b72ec5bec09c593aeef --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/java/JavaTools.java @@ -0,0 +1,98 @@ +package rescuecore2.misc.java; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import rescuecore2.log.Logger; + +/** + * A set of useful functions related to the Java language. + */ +public final class JavaTools { + private JavaTools() { + } + + /** + * Instantiate a class by reflection. This method will not throw exceptions but + * will return null on error. + * + * @param className The class name to instantiate. + * @param outputClass The class that specifies the type that should be returned. + * This will usually be a superclass of the given class name. + * @param <T> The desired return type. + * @return A new instance of the given class name cast as the output class, or + * null if the class cannot be instantiated. + */ + public static <T> T instantiate(String className, Class<T> outputClass) { + try { + Class<? extends T> clazz = Class.forName(className).asSubclass(outputClass); + if (Modifier.isAbstract(clazz.getModifiers())) { + return null; + } + return clazz.getDeclaredConstructor().newInstance(); + } catch (ClassNotFoundException e) { + Logger.info("Could not find class " + className); + } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { + Logger.info("Could not instantiate class " + className); + } + + return null; + } + + /** + * Instantiate a factory class by reflection. Essentially the same as + * {@link #instantiate(String, Class)} except that it will look for a static + * field called INSTANCE that contains an instance of the right class. If this + * doesn't exist (or in inaccessible or the wrong type) then a constructor will + * be used. + * + * @param classname The class name to instantiate. + * @param outputClass The class that specifies the type that should be returned. + * This will usually be a superclass of the given class name. + * @param <T> The desired return type. + * @return The content of the INSTANCE field if it exists; a new instance of the + * given class name cast as the output class, or null if the class + * cannot be instantiated. + */ + public static <T> T instantiateFactory(String classname, Class<T> outputClass) { + Class<? extends T> clazz; + try { + clazz = Class.forName(classname).asSubclass(outputClass); + } catch (ClassNotFoundException e) { + Logger.info("Could not find class " + classname); + return null; + } catch (ClassCastException e) { + Logger.info(classname + " is not a subclass of " + outputClass.getName(), e); + return null; + } + // Is there a singleton instance called INSTANCE? + try { + Field field = clazz.getField("INSTANCE"); + if (Modifier.isStatic(field.getModifiers())) { + try { + Object o = field.get(null); + if (o != null) { + return outputClass.cast(o); + } + } catch (IllegalAccessException e) { + Logger.info("Could not access INSTANCE field in class " + classname + ": trying constructor."); + } catch (ClassCastException e) { + Logger.info( + "Could not cast INSTANCE field to " + outputClass + " in class " + classname + ": trying constructor."); + } + } + } catch (NoSuchFieldException e) { + Logger.info("No INSTANCE field in class " + classname); + // No singleton instance. Try instantiating it. + } + try { + return clazz.getDeclaredConstructor().newInstance(); + } catch (IllegalAccessException e) { + Logger.info("Could not instantiate class " + classname); + } catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) { + Logger.info("Could not instantiate class " + classname); + } + return null; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/java/LoadableType.java b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableType.java new file mode 100644 index 0000000000000000000000000000000000000000..c84ce541d8e20342a285e94225ad2c1326e1ad4f --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableType.java @@ -0,0 +1,118 @@ +package rescuecore2.misc.java; + +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import rescuecore2.components.Agent; +import rescuecore2.components.Component; +import rescuecore2.components.Simulator; +import rescuecore2.components.Viewer; +import rescuecore2.log.Logger; +import rescuecore2.registry.Factory; + +/** + * This class contains information about a type that can be detected in a jar + * manifest or class file. For example, a MessageFactory implementation in a jar + * file can be designated in the jar manifest under a "MessageFactory" + * attribute, or implementation classes could be inferred by looking for a class + * name regular expression like "(.*MessageFactory).class". + * + * Classes in the jar file can be inspected and checked to see if they extend + * (or implement) a particular class (interface). + */ +public class LoadableType { + public static final LoadableType GENERIC_FACTORY = new LoadableType("Factory", "(.+Factory).class", Factory.class); + /** An Agent loadable type. */ + public static final LoadableType AGENT = new LoadableType("Agent", + "(.+(?:FireBrigade|PoliceForce|AmbulanceTeam|Centre|Center|Civilian)).class", Agent.class); + /** A Simulator loadable type. */ + public static final LoadableType SIMULATOR = new LoadableType("Simulator", "(.+Simulator).class", Simulator.class); + /** A Viewer loadable type. */ + public static final LoadableType VIEWER = new LoadableType("Viewer", "(.+Viewer).class", Viewer.class); + /** A Component loadable type. */ + public static final LoadableType COMPONENT = new LoadableType("Component", null, Component.class); + + private String manifestKey; + private Pattern regex; + private Class<?> clazz; + + /** + * Construct a new LoadableType. + * + * @param manifestKey The key to look for in a jar manifest for this type. + * @param regex A regex to use for determining if a class name should be + * tested. + * @param clazz A superclass for checking if candidate classes should be + * extracted. + */ + public LoadableType(String manifestKey, String regex, Class<?> clazz) { + this.manifestKey = manifestKey; + this.regex = regex == null ? null : Pattern.compile(regex); + this.clazz = clazz; + } + + /** + * Inspect a jar manifest and extract the entries in the manifestKey attribute + * if it exists. This method will check that entries also specify valid class + * names. + * + * @param mf The manifest to check. + * @return A list of class names. + */ + public List<String> processManifest(Manifest mf) { + Attributes att = mf.getMainAttributes(); + String value = att.getValue(manifestKey); + List<String> result = new ArrayList<String>(); + if (value != null) { + for (String next : value.split(" ")) { + try { + Class<?> testClass = Class.forName(next); + if (!clazz.isAssignableFrom(testClass)) { + Logger.warn("Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + + "' is not a subclass of '" + clazz.getName() + "'"); + } else if (testClass.isInterface()) { + Logger.warn( + "Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + "' is an interface"); + } else { + result.add(next); + } + } catch (ClassNotFoundException e) { + Logger.warn("Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + "' not found"); + } + } + } + return result; + } + + /** + * Inspect an entry in a jar file and see if it names a conformant class. + * + * @param e The JarEntry to check. + * @return The class name, or null if the entry does not name a conformant + * class. + */ + public String processJarEntry(JarEntry e) { + if (regex == null) { + return null; + } + Matcher m = regex.matcher(e.getName()); + if (m.matches()) { + String className = null; + try { + className = m.group(1).replace("/", "."); + Class<?> testClass = Class.forName(className); + if (clazz.isAssignableFrom(testClass) && !testClass.isInterface()) { + return className; + } + } catch (ClassNotFoundException ex) { + Logger.warn("Class " + className + " not found"); + } + } + return null; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeCallback.java b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..8659ed61fd0db9dd12b2099d0e860d13409adf4a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeCallback.java @@ -0,0 +1,23 @@ +package rescuecore2.misc.java; + +import java.util.Collection; + +/** + * Callback interface for processing loadable types. + */ +public interface LoadableTypeCallback { + /** + * Notification that a loadable type was found. + * + * @param type The LoadableType that was found. + * @param className The class name. + */ + void classFound(LoadableType type, String className); + + /** + * Get the set of loadable types that this callback is interested in. + * + * @return A collection of LoadableType objects. + */ + Collection<LoadableType> getTypes(); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeProcessor.java b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..b9095a78169ed5f9ca141c3ac9aec544e9cad982 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/misc/java/LoadableTypeProcessor.java @@ -0,0 +1,225 @@ +package rescuecore2.misc.java; + +import static rescuecore2.misc.java.JavaTools.instantiateFactory; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import rescuecore2.Constants; +import rescuecore2.config.Config; +import rescuecore2.log.Logger; +import rescuecore2.registry.Factory; +import rescuecore2.registry.Registry; + +/** + * A utility class for processing loadable types from jar files. + */ +public class LoadableTypeProcessor { + private List<LoadableTypeCallback> callbacks; + private Set<LoadableType> types; + private boolean deep; + private String dir; + private Set<String> ignore; + + /** + * Construct a LoadableTypeProcessor that will perform a deep inspection. + * + * @param config The system configuration. + */ + public LoadableTypeProcessor(Config config) { + callbacks = new ArrayList<LoadableTypeCallback>(); + types = new HashSet<LoadableType>(); + deep = config.getBooleanValue(Constants.DEEP_JAR_INSPECTION_KEY, Constants.DEFAULT_DEEP_JAR_INSPECTION); + dir = config.getValue(Constants.JAR_DIR_KEY, Constants.DEFAULT_JAR_DIR); + ignore = new HashSet<String>(); + ignore.addAll(config.getArrayValue(Constants.IGNORE_JARS_KEY, Constants.DEFAULT_IGNORE_JARS)); + } + + /** + * Add the message, property and entity factory register callbacks. + * + * @param registry The Registry to register factory classes with. + */ + public void addFactoryRegisterCallbacks(Registry registry) { + addCallback(new GenericFactoryRegisterCallback(registry)); + // addCallback(new MessageFactoryRegisterCallback(registry)); + // addCallback(new EntityFactoryRegisterCallback(registry)); + // addCallback(new PropertyFactoryRegisterCallback(registry)); + } + + /** + * Add a LoadableTypeCallback function. + * + * @param callback The callback to add. + */ + public void addCallback(LoadableTypeCallback callback) { + callbacks.add(callback); + types.addAll(callback.getTypes()); + } + + /** + * Add a config updating callback. This will append class names to a Config + * entry when a particular LoadableType returns an acceptable class. + * + * @param type The type to look for. + * @param config The config to update. + * @param configKey The key to update. + */ + public void addConfigUpdater(LoadableType type, Config config, String configKey) { + addCallback(new ConfigCallback(type, config, configKey)); + } + + /** + * Set whether to do a "deep" inspection or just inspect the manifest. If true + * then all entries will be tested to see if they match the target regex and + * class. + * + * @param newDeep Whether to do a deep inspection or not. + */ + public void setDeepInspection(boolean newDeep) { + this.deep = newDeep; + } + + /** + * Set the name of the directory to process. + * + * @param name The name of the directory. + */ + public void setDirectory(String name) { + dir = name; + } + + /** + * Process all jars in a directory. + * + * @throws IOException If there is a problem reading the jar files. + */ + public void process() throws IOException { + File baseDir = new File(dir); + Logger.info("Processing jar directory: " + baseDir.getAbsolutePath()); + File[] jarFiles = baseDir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dirName, String name) { + return name.endsWith(".jar"); + } + }); + if (jarFiles == null) { + return; + } + for (File next : jarFiles) { + JarFile jar = new JarFile(next); + processJarFile(jar); + } + } + + /** + * Inspect an individual jar file for loadable types. + * + * @param jar The jar file to inspect. + * @throws IOException If there is a problem reading the jar file. + */ + public void processJarFile(JarFile jar) throws IOException { + String name = jar.getName(); + String tail = name.substring(name.lastIndexOf("/") + 1); + if (ignore.contains(tail)) { + return; + } + Logger.info("Processing " + jar.getName()); + Manifest mf = jar.getManifest(); + if (mf != null) { + Logger.debug("Inspecting manifest..."); + for (LoadableType type : types) { + for (String next : type.processManifest(mf)) { + fireCallback(type, next); + } + } + } + if (deep) { + // Look for well-named classes + Logger.debug("Looking for likely class names..."); + for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements();) { + JarEntry next = e.nextElement(); + for (LoadableType type : types) { + String s = type.processJarEntry(next); + if (s != null) { + fireCallback(type, s); + } + } + } + } + } + + private void fireCallback(LoadableType type, String classname) { + for (LoadableTypeCallback next : callbacks) { + if (next.getTypes().contains(type)) { + next.classFound(type, classname); + } + } + } + + private static class ConfigCallback implements LoadableTypeCallback { + private LoadableType type; + private Config config; + private String key; + + public ConfigCallback(LoadableType type, Config config, String key) { + this.type = type; + this.config = config; + this.key = key; + } + + @Override + public void classFound(LoadableType otherType, String className) { + Logger.info("Adding " + className + " to " + key); + if (config.isDefined(key)) { + List<String> existing = config.getArrayValue(key); + if (!existing.contains(className)) { + config.appendValue(key, className); + } + } else { + config.setValue(key, className); + } + } + + @Override + public Collection<LoadableType> getTypes() { + return Collections.singleton(type); + } + } + + /** + * A LoadableTypeCallback that will registry ÙŽAll Factory implementations. + */ + public static final class GenericFactoryRegisterCallback implements LoadableTypeCallback { + private Registry registry; + + private GenericFactoryRegisterCallback(Registry registry) { + this.registry = registry; + } + + @Override + public void classFound(LoadableType type, String className) { + Factory factory = instantiateFactory(className, Factory.class); + if (factory != null) { + registry.registerFactory(factory); + Logger.info("Registered factory '" + className + "' with registry " + registry.getName()); + } + } + + @Override + public Collection<LoadableType> getTypes() { + return Collections.singleton(LoadableType.GENERIC_FACTORY); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractEntityFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractEntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2e0fcaddcb31645df38907f6f93d523168bfe64a --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractEntityFactory.java @@ -0,0 +1,38 @@ +package rescuecore2.registry; + +import rescuecore2.URN; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * An abstract entity factory with helper methods for defining URNs with enums. + * + * @param <T> An enum type that defines the URNs this factory knows about. + */ +public abstract class AbstractEntityFactory<T extends Enum<T> & URN> extends AbstractFactory<T> + implements EntityFactory { + + /** + * Constructor for AbstractEntityFactory. + * + * @param clazz The class of enum this factory uses. + */ + protected AbstractEntityFactory(Class<T> clazz) { + super(clazz); + } + + @Override + public Entity makeEntity(int urn, EntityID id) { + return makeEntity(getURNEnum(urn), id); + } + + /** + * Create a new Entity. + * + * @param urn The enum urn of the entity to create. + * @param id The id of the new entity. + * @return A new Entity of the correct type. + * @throws IllegalArgumentException If the urn is not recognised. + */ + protected abstract Entity makeEntity(T urn, EntityID id); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..affb4c9ba9767a80eef2c07c78c28a1a5002b6d0 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractFactory.java @@ -0,0 +1,90 @@ +package rescuecore2.registry; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.EnumSet; + +import rescuecore2.URN; + +public class AbstractFactory<T extends Enum<T> & URN> implements Factory { + private Class<T> clazz; + private Method fromString; + private Method fromInt; + + /** + * Constructor for AbstractPropertyFactory. + * + * @param clazz The class of enum this factory uses. + */ + protected AbstractFactory(Class<T> clazz) { + this.clazz = clazz; + try { + fromString = clazz.getDeclaredMethod("fromString", String.class); + } catch (NoSuchMethodException e) { + } + try { + fromInt = clazz.getDeclaredMethod("fromInt", int.class); + } catch (NoSuchMethodException e2) { + } + } + + @SuppressWarnings("unchecked") + public T getURNEnum(int urn) { + if (fromInt != null) { + try { + return (T) fromInt.invoke(null, urn); + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + return null; + } + + @SuppressWarnings("unchecked") + public T getURNEnum(String urn) { + if (fromString != null) { + try { + return (T) fromString.invoke(null, urn); + } catch (IllegalAccessException | InvocationTargetException e) { + } + } + return null; + } + + @Override + public int[] getKnownURNs() { + EnumSet<T> set = getKnownURNsEnum(); + int[] result = new int[set.size()]; + int i = 0; + for (T next : set) { + result[i++] = next.getURNId(); + } + return result; + } + + /** + * Get an EnumSet containing known property URNs. Default implementation returns + * EnumSet.allOf(T). + * + * @return An EnumSet containing known property URNs. + */ + protected EnumSet<T> getKnownURNsEnum() { + return EnumSet.allOf(clazz); + } + + @Override + public String getURNStr(int urnId) { + T urnEnum = getURNEnum(urnId); + if (urnEnum != null) + return urnEnum.getURNStr(); + return null; + } + + @Override + public String getPrettyName(int urnId) { + T urnEnum = getURNEnum(urnId); + if (urnEnum != null) + return urnEnum.name(); + return null; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractFilterFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractFilterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..106d0d674d62541b6ebfedb82c24efbd966f000b --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractFilterFactory.java @@ -0,0 +1,41 @@ +package rescuecore2.registry; + +import java.util.Set; + +public abstract class AbstractFilterFactory<T extends Factory> implements Factory { + + protected T downstream; + private Set<Integer> urns; + private boolean inclusive; + + public AbstractFilterFactory(T downstream, Set<Integer> urns, boolean inclusive) { + this.downstream = downstream; + this.urns = urns; + this.inclusive = inclusive; + } + + public boolean isValidUrn(int urn) { + if (inclusive && !urns.contains(urn)) { + return false; + } + if (!inclusive && urns.contains(urn)) { + return false; + } + return true; + } + + @Override + public int[] getKnownURNs() { + return downstream.getKnownURNs(); + } + + @Override + public String getURNStr(int urnId) { + return downstream.getURNStr(urnId); + } + + @Override + public String getPrettyName(int urn) { + return downstream.getPrettyName(urn); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageComponentFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageComponentFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2a1e80cec3621679f1d37fbc7d1abd77e0966eab --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageComponentFactory.java @@ -0,0 +1,21 @@ +package rescuecore2.registry; + +import rescuecore2.URN; + +/** + * An abstract message factory with helper methods for defining URNs with enums. + * + * @param <T> An enum type that defines the URNs this factory knows about. + */ +public abstract class AbstractMessageComponentFactory<T extends Enum<T> & URN> extends AbstractFactory<T> + implements MessageComponentFactory { + + /** + * Constructor for AbstractMessageFactory. + * + * @param clazz The class of enum this factory uses. + */ + protected AbstractMessageComponentFactory(Class<T> clazz) { + super(clazz); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..592c9f26dacf6ff1fb29857d840be49e493b8741 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractMessageFactory.java @@ -0,0 +1,49 @@ +package rescuecore2.registry; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.URN; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * An abstract message factory with helper methods for defining URNs with enums. + * + * @param <T> An enum type that defines the URNs this factory knows about. + */ +public abstract class AbstractMessageFactory<T extends Enum<T> & URN> extends AbstractFactory<T> + implements MessageFactory { + + /** + * Constructor for AbstractMessageFactory. + * + * @param clazz The class of enum this factory uses. + */ + protected AbstractMessageFactory(Class<T> clazz) { + super(clazz); + } + + @Override + public Message makeMessage(int urn, InputStream data) throws IOException { + return makeMessage(getURNEnum(urn), data); + } + + @Override + public Message makeMessage(int urn, MessageProto data) { + return makeMessage(getURNEnum(urn), data); + } + + /** + * Create a message based on its urn and populate it with data from a stream. If + * the urn is not recognised then return null. + * + * @param urn The urn of the message type to create. + * @param data An InputStream to read message data from. + * @return A new Message object, or null if the urn is not recognised. + * @throws IOException If there is a problem reading the stream. + */ + protected abstract Message makeMessage(T urn, InputStream data) throws IOException; + + protected abstract Message makeMessage(T urn, MessageProto data); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/AbstractPropertyFactory.java b/modules/rescuecore2/src/rescuecore2/registry/AbstractPropertyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2e8ae053d11ee7e94e92ae7ec7244f984cd2cdb5 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/AbstractPropertyFactory.java @@ -0,0 +1,32 @@ +package rescuecore2.registry; + +import rescuecore2.URN; +import rescuecore2.worldmodel.Property; + +/** + * An abstract property factory with helper methods for defining URNs with + * enums. + * + * @param <T> An enum type that defines the URNs this factory knows about. + */ +public abstract class AbstractPropertyFactory<T extends Enum<T> & URN> extends AbstractFactory<T> + implements PropertyFactory { + + protected AbstractPropertyFactory(Class<T> clazz) { + super(clazz); + } + + @Override + public Property makeProperty(int urn) { + return makeProperty(getURNEnum(urn)); + } + + /** + * Create a new Property. + * + * @param urn The enum urn of the property to create. + * @return A new Property of the correct type. + * @throws IllegalArgumentException If the urn is not recognised. + */ + protected abstract Property makeProperty(T urn); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/EntityFactory.java b/modules/rescuecore2/src/rescuecore2/registry/EntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..826711125ce89562a1c96c556de0697ad2f527a4 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/EntityFactory.java @@ -0,0 +1,20 @@ +package rescuecore2.registry; + + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + A factory for vending Entities. + */ +public interface EntityFactory extends Factory { + /** + Create a new Entity. + @param urn The urn of the entity to create. + @param id The id of the new entity. + @return A new Entity of the correct type. + @throws IllegalArgumentException If the urn is not recognised. + */ + Entity makeEntity(int urn, EntityID id); + +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/Factory.java b/modules/rescuecore2/src/rescuecore2/registry/Factory.java new file mode 100644 index 0000000000000000000000000000000000000000..6a3eabcdf1e33f72943459abdd03e45a208a1b88 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/Factory.java @@ -0,0 +1,14 @@ +package rescuecore2.registry; + +public interface Factory { + /** + * Get all message urns understood by this factory. + * + * @return All message urns. + */ + int[] getKnownURNs(); + + String getURNStr(int urnId); + + String getPrettyName(int urnId); +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/FilterEntityFactory.java b/modules/rescuecore2/src/rescuecore2/registry/FilterEntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a9a29f70490f4eb8da87e2606df7cd3786af2b79 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/FilterEntityFactory.java @@ -0,0 +1,34 @@ +package rescuecore2.registry; + +import java.util.Set; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * A entity factory that filters urns that match or don't match a given set. + */ +public class FilterEntityFactory extends AbstractFilterFactory<EntityFactory> + implements EntityFactory { + + /** + * Construct a FilterEntityFactory. + * + * @param downstream The downstream entity factory. + * @param urns The set of URNs. + * @param inclusive True if the set of URNs are allowed, false if they are + * forbidden. + */ + public FilterEntityFactory(EntityFactory downstream, Set<Integer> urns, + boolean inclusive) { + super(downstream, urns, inclusive); + } + + @Override + public Entity makeEntity(int urn, EntityID id) { + if (!isValidUrn(urn)) + return null; + return downstream.makeEntity(urn, id); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/FilterMessageFactory.java b/modules/rescuecore2/src/rescuecore2/registry/FilterMessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..fdecdea6aaf4c2ebe225226c5b6b4afc7ff4080b --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/FilterMessageFactory.java @@ -0,0 +1,45 @@ +package rescuecore2.registry; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Set; + +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * A message factory that filters urns that do not match a given set. + */ +public class FilterMessageFactory extends AbstractFilterFactory<MessageFactory> + implements MessageFactory { + + /** + * Construct a FilterMessageFactory. + * + * @param downstream The downstream message factory. + * @param urns The set of URNs. + * @param inclusive True if the set of URNs are allowed, false if they are + * forbidden. + */ + public FilterMessageFactory(MessageFactory downstream, Set<Integer> urns, + boolean inclusive) { + super(downstream, urns, inclusive); + } + + @Override + public Message makeMessage(int urn, InputStream data) throws IOException { + if (!isValidUrn(urn)) + return null; + + return downstream.makeMessage(urn, data); + } + + @Override + public Message makeMessage(int urn, MessageProto data) { + if (!isValidUrn(urn)) + return null; + + return downstream.makeMessage(urn, data); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/FilterPropertyFactory.java b/modules/rescuecore2/src/rescuecore2/registry/FilterPropertyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bd05d4f378c4466b6327c25470ce7aedacc74d67 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/FilterPropertyFactory.java @@ -0,0 +1,31 @@ +package rescuecore2.registry; + +import java.util.Set; + +import rescuecore2.worldmodel.Property; + +/** + * A property factory that filters urns that do not match a given set. + */ +public class FilterPropertyFactory extends AbstractFilterFactory<PropertyFactory> implements PropertyFactory { + + /** + * Construct a FilterPropertyFactory. + * + * @param downstream The downstream property factory. + * @param urns The set of URNs. + * @param inclusive True if the set of URNs are allowed, false if they are + * forbidden. + */ + public FilterPropertyFactory(PropertyFactory downstream, Set<Integer> urns, boolean inclusive) { + super(downstream, urns, inclusive); + } + + @Override + public Property makeProperty(int urn) { + if (!isValidUrn(urn)) + return null; + + return downstream.makeProperty(urn); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/MessageComponentFactory.java b/modules/rescuecore2/src/rescuecore2/registry/MessageComponentFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b086c166c88c6356ab3be36f2013214429770f52 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/MessageComponentFactory.java @@ -0,0 +1,7 @@ +package rescuecore2.registry; + +/** + Factory class for creating messages. + */ +public interface MessageComponentFactory extends Factory { +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/MessageFactory.java b/modules/rescuecore2/src/rescuecore2/registry/MessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..9a68084971b87d7575317630524c018f46b78fa0 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/MessageFactory.java @@ -0,0 +1,25 @@ +package rescuecore2.registry; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; + +/** + * Factory class for creating messages. + */ +public interface MessageFactory extends Factory { + /** + * Create a message based on its urn and populate it with data from a stream. If + * the urn is not recognised then return null. + * + * @param urn The urn of the message type to create. + * @param data An InputStream to read message data from. + * @return A new Message object, or null if the urn is not recognised. + * @throws IOException If there is a problem reading the stream. + */ + Message makeMessage(int urn, InputStream data) throws IOException; + + Message makeMessage(int urn, MessageProto proto); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/registry/PropertyFactory.java b/modules/rescuecore2/src/rescuecore2/registry/PropertyFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ae052a32a53f5018930495c261c0415c99693635 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/PropertyFactory.java @@ -0,0 +1,18 @@ +package rescuecore2.registry; + + +import rescuecore2.worldmodel.Property; + +/** + A factory for vending Properties. + */ +public interface PropertyFactory extends Factory { + /** + Create a new Property. + @param urn The urn of the property to create. + @return A new Property of the correct type. + @throws IllegalArgumentException If the urn is not recognised. + */ + Property makeProperty(int urn); + +} diff --git a/modules/rescuecore2/src/rescuecore2/registry/Registry.java b/modules/rescuecore2/src/rescuecore2/registry/Registry.java new file mode 100644 index 0000000000000000000000000000000000000000..2594375170100d0597d4f79899ed935a05c46f43 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/registry/Registry.java @@ -0,0 +1,309 @@ +package rescuecore2.registry; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +/** + * A class for managing the different types of entities, properties, messages + * and their associated factories. + */ +public final class Registry { + /** + * The system-(or at least Classloader)-wide Registry. + */ + public static final Registry SYSTEM_REGISTRY = new Registry("System", null); + + private static final ThreadLocal<Registry> CURRENT_REGISTRY = new InheritableThreadLocal<Registry>() { + @Override + public Registry initialValue() { + return SYSTEM_REGISTRY; + } + }; + + static { + // Register the ControlMessageFactory + SYSTEM_REGISTRY.registerFactory(rescuecore2.messages.control.ControlMessageFactory.INSTANCE); + SYSTEM_REGISTRY.registerFactory(rescuecore2.messages.control.ControlMessageComponentFactory.INSTANCE); + } + + private final Map<Integer, EntityFactory> entityFactories; + private final Map<Integer, PropertyFactory> propertyFactories; + private final Map<Integer, MessageFactory> messageFactories; + private final Map<Integer, MessageComponentFactory> messageComponentFactories; + private final Map<String, Integer> urn_map_str_id; + private final Map<Integer, String> urn_map_id_str; + private final Map<Integer, String> urn_prettyName; + + private final Registry parent; + private final String name; + + /** + * Create a new Registry that uses the system registry as a parent. + */ + public Registry() { + this(null, SYSTEM_REGISTRY); + } + + /** + * Create a new Registry with a particular name that uses the system registry as + * a parent. + * + * @param name The name of this Registry. + */ + public Registry(String name) { + this(name, SYSTEM_REGISTRY); + } + + /** + * Create a new Registry with a particular parent. + * + * @param parent The parent Registry. + */ + public Registry(Registry parent) { + this(null, parent); + } + + /** + * Create a new Registry with a particular name and parent. + * + * @param name The name of this Registry. + * @param parent The parent Registry. + */ + public Registry(String name, Registry parent) { + this.name = name; + this.parent = parent; + entityFactories = new HashMap<Integer, EntityFactory>(); + propertyFactories = new HashMap<Integer, PropertyFactory>(); + messageFactories = new HashMap<Integer, MessageFactory>(); + messageComponentFactories = new HashMap<Integer, MessageComponentFactory>(); + urn_map_str_id = new HashMap<String, Integer>(); + urn_map_id_str = new HashMap<Integer, String>(); + urn_prettyName = new HashMap<Integer, String>(); + } + + /** + * Get the current Registry for this thread. + * + * @return The current Registry for this thread. + */ + public static Registry getCurrentRegistry() { + return CURRENT_REGISTRY.get(); + } + + /** + * Set the current Registry for this thread. + * + * @param r The current Registry for this thread. + */ + public static void setCurrentRegistry(Registry r) { + CURRENT_REGISTRY.set(r); + } + + @Override + public String toString() { + return getName(); + } + + /** + * Get the name of this registry. + * + * @return The name of this registry. + */ + public String getName() { + if (name == null) { + return super.toString(); + } + return name; + } + + /** + * Register a factory. This will register all message URNs that the factory + * knows about. + * + * @param factory The factory to register. + */ + public void registerFactory(Factory factory) { + for (int urn : factory.getKnownURNs()) { + registerFactory(urn, factory); + } + } + + /** + * Register a URN and assign a Factory for constructing instances of this type. + * + * @param urn The urn to register. + * @param factory The factory that is responsible for constructing message + * instances of this type. + */ + public void registerFactory(int urn, Factory factory) { + if (factory instanceof MessageComponentFactory) + registerFactoryInternal(urn, (MessageComponentFactory) factory, messageComponentFactories); + else if (factory instanceof MessageFactory) + registerFactoryInternal(urn, (MessageFactory) factory, messageFactories); + // registerMessageFactory(urn,(MessageFactory) factory); + else if (factory instanceof PropertyFactory) + registerFactoryInternal(urn, (PropertyFactory) factory, propertyFactories); + // registerPropertyFactory(urn,(PropertyFactory) factory); + else if (factory instanceof EntityFactory) + registerFactoryInternal(urn, (EntityFactory) factory, entityFactories); + // registerEntityFactory(urn,(EntityFactory) factory); + else + Logger.error(getName() + ":unknown factory! " + factory.getClass().getName()); + + } + + private <T extends Factory> void registerFactoryInternal(int urnId, T factory, Map<Integer, T> target) { + synchronized (target) { + T old = target.get(urnId); + if (old != null && old != factory) { + Logger.warn(getName() + ": " + urnId + " (" + old.getPrettyName(urnId) + ":" + old.getURNStr(urnId) + ")" + + " is being clobbered by " + factory + ". Old factory: " + old); + } + target.put(urnId, factory); + + this.urn_prettyName.put(urnId, factory.getPrettyName(urnId)); + + String urn_str = factory.getURNStr(urnId); + this.urn_map_str_id.put(urn_str, urnId); + this.urn_map_id_str.put(urnId, urn_str); + } + + } + + /** + * Create an entity from a urn. If the urn is not recognised then return null. + * This method will delegate to the {@link #registerEntityFactory(EntityFactory) + * previously registered} EntityFactory. + * + * @param urn The urn of the entity type to create. + * @param id The EntityID of the Entity that will be created. + * @return A new Entity object, or null if the urn is not recognised. + */ + public Entity createEntity(int urn, EntityID id) { + EntityFactory factory = getEntityFactory(urn); + if (factory == null) { + Logger.warn(getName() + ": Entity " + urn + " not recognised."); + return null; + } + return factory.makeEntity(urn, id); + } + + /** + * Create a property from a urn. If the urn is not recognised then return null. + * This method will delegate to the + * {@link #registerPropertyFactory(PropertyFactory) previously registered} + * PropertyFactory. + * + * @param urn The urn of the property type to create. + * @return A new Property object, or null if the urn is not recognised. + */ + public Property createProperty(Integer urn) { + PropertyFactory factory = getPropertyFactory(urn); + if (factory == null) { + Logger.warn(getName() + ": Property " + urn + " not recognised."); + return null; + } + return factory.makeProperty(urn); + } + + /** + * Create a message from a urn. If the urn is not recognised then return null. + * This method will delegate to the + * {@link #registerMessageFactory(MessageFactory) previously registered} + * MessageFactory. + * + * @param urn The urn of the message type to create. + * @param data An InputStream to read message data from. + * @return A new Message object, or null if the urn is not recognised. + * @throws IOException If there is a problem decoding the message. + */ + public Message createMessage(Integer urn, InputStream data) throws IOException { + MessageFactory factory = getMessageFactory(urn); + if (factory == null) { + Logger.warn(getName() + ": Message " + urn + " not recognised."); + return null; + } + return factory.makeMessage(urn, data); + } + + public Message createMessage(Integer urn, MessageProto data) { + MessageFactory factory = getMessageFactory(urn); + if (factory == null) { + Logger.warn(getName() + ": Message " + urn + " not recognised."); + return null; + } + return factory.makeMessage(urn, data); + } + + /** + * Get the entity factory for a URN, delegating to the parent if required. + * + * @param urn The URN to look up. + * @return An EntityFactory, or null if the URN is not recognised. + */ + protected EntityFactory getEntityFactory(int urn) { + EntityFactory result = null; + synchronized (entityFactories) { + result = entityFactories.get(urn); + } + if (result == null && parent != null) { + result = parent.getEntityFactory(urn); + } + return result; + } + + /** + * Get the property factory for a URN, delegating to the parent if required. + * + * @param urn The URN to look up. + * @return A PropertyFactory, or null if the URN is not recognised. + */ + protected PropertyFactory getPropertyFactory(Integer urn) { + PropertyFactory result = null; + synchronized (propertyFactories) { + result = propertyFactories.get(urn); + } + if (result == null && parent != null) { + result = parent.getPropertyFactory(urn); + } + return result; + } + + /** + * Get the message factory for a URN, delegating to the parent if required. + * + * @param urn The URN to look up. + * @return A MessageFactory, or null if the URN is not recognised. + */ + protected MessageFactory getMessageFactory(Integer urn) { + MessageFactory result = null; + synchronized (messageFactories) { + result = messageFactories.get(urn); + } + if (result == null && parent != null) { + result = parent.getMessageFactory(urn); + } + return result; + } + + public String toURN_Str(int urnId) { + return this.urn_map_id_str.get(urnId); + } + + public int toURN_Id(String urnStr) { + return this.urn_map_str_id.get(urnStr); + } + + public String toPrettyName(int urn) { + return urn_prettyName.get(urn); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/scenario/Scenario.java b/modules/rescuecore2/src/rescuecore2/scenario/Scenario.java new file mode 100644 index 0000000000000000000000000000000000000000..d90005d9a8fcd292f740c2b136f45150448a6737 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/scenario/Scenario.java @@ -0,0 +1,14 @@ +package rescuecore2.scenario; + + + + +/** + * Abstract Scenario interface + * + * @author Salim + * + */ +public interface Scenario { + +} diff --git a/modules/rescuecore2/src/rescuecore2/scenario/compatibilities/CollapseSimCompatibaleScenarioV1_1.java b/modules/rescuecore2/src/rescuecore2/scenario/compatibilities/CollapseSimCompatibaleScenarioV1_1.java new file mode 100644 index 0000000000000000000000000000000000000000..d97e0223c38e2da02bba9b153e44750732074134 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/scenario/compatibilities/CollapseSimCompatibaleScenarioV1_1.java @@ -0,0 +1,14 @@ +package rescuecore2.scenario.compatibilities; + +import java.util.HashMap; + +/** + * All scenarios working with Collapse Simulator V1.1 should implement this + * interface + * + * @author Salim + * + */ +public interface CollapseSimCompatibaleScenarioV1_1 { + public HashMap<Integer, Float> getAftershocks(); +} diff --git a/modules/rescuecore2/src/rescuecore2/scenario/exceptions/ScenarioException.java b/modules/rescuecore2/src/rescuecore2/scenario/exceptions/ScenarioException.java new file mode 100644 index 0000000000000000000000000000000000000000..5ab8e3625f8573e46b5d21039e060dd2edbd5021 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/scenario/exceptions/ScenarioException.java @@ -0,0 +1,38 @@ +package rescuecore2.scenario.exceptions; + +/** + Exception class for problems with scenarios. + */ +public class ScenarioException extends Exception { + /** + Construct a scenario exception with no information. + */ + public ScenarioException() { + super(); + } + + /** + Construct a scenario exception with an error message. + @param msg The error message. + */ + public ScenarioException(String msg) { + super(msg); + } + + /** + Construct a scenario exception that was caused by another exception. + @param cause The cause of this exception. + */ + public ScenarioException(Throwable cause) { + super(cause); + } + + /** + Construct a scenario exception with an error message and an underlying cause. + @param msg The error message. + @param cause The cause of this exception. + */ + public ScenarioException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/scenario/exceptions/UncompatibleScenarioException.java b/modules/rescuecore2/src/rescuecore2/scenario/exceptions/UncompatibleScenarioException.java new file mode 100644 index 0000000000000000000000000000000000000000..59f17cbcbce55752009ca38e512a5af03b3ff5ab --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/scenario/exceptions/UncompatibleScenarioException.java @@ -0,0 +1,15 @@ +package rescuecore2.scenario.exceptions; + +public class UncompatibleScenarioException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public UncompatibleScenarioException() { + super( + "Uncompatible Scenario Exception: Scenario is not implementing CollapseSimCompatibaleScenarioV1_1 interface"); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/score/AbstractScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/AbstractScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..ae5869c37dab4ca068c90b2644b534d954d13269 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/AbstractScoreFunction.java @@ -0,0 +1,42 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +/** + Abstract base class for a score function. + */ +public abstract class AbstractScoreFunction implements ScoreFunction { + private String name; + + /** + Construct an AbstractScoreFunction. + @param name The name of this function. + */ + protected AbstractScoreFunction(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + } + + /** + Change the name of this score function. + @param newName The new name. + */ + public void setName(String newName) { + this.name = newName; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/CompositeScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/CompositeScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..52227e549720d4de89051a1d2c15682118eee857 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/CompositeScoreFunction.java @@ -0,0 +1,118 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; + +/** + A score function that applies some function to the scores of a set of child score functions. + */ +public abstract class CompositeScoreFunction extends AbstractScoreFunction { + /** The child score functions. */ + protected Set<ScoreFunction> children; + + /** + Create a CompositeScoreFunction with no children. + @param name The name of this function. + */ + public CompositeScoreFunction(String name) { + super(name); + children = new HashSet<ScoreFunction>(); + } + + /** + Create a CompositeScoreFunction with a collection of children. + @param name The name of this function. + @param c The child score functions. + */ + public CompositeScoreFunction(String name, Collection<ScoreFunction> c) { + this(name); + addChildFunctions(c); + } + + /** + Create a CompositeScoreFunction with a collection of children. + @param name The name of this function. + @param c The child score functions. + */ + public CompositeScoreFunction(String name, ScoreFunction... c) { + this(name); + addChildFunctions(c); + } + + /** + Add a child score function. + @param child The child function to add. + */ + public void addChildFunction(ScoreFunction child) { + children.add(child); + } + + /** + Add a collection of child score functions. + @param c The child functions to add. + */ + public final void addChildFunctions(Collection<ScoreFunction> c) { + for (ScoreFunction next : c) { + addChildFunction(next); + } + } + + /** + Add a collection of child score functions. + @param c The child functions to add. + */ + public final void addChildFunctions(ScoreFunction... c) { + for (ScoreFunction next : c) { + addChildFunction(next); + } + } + + /** + Remove a child score function. + @param child The child function to remove. + */ + public void removeChildFunction(ScoreFunction child) { + children.remove(child); + } + + /** + Remove a collection of child score functions. + @param c The child functions to remove. + */ + public final void removeChildFunctions(Collection<ScoreFunction> c) { + for (ScoreFunction next : c) { + removeChildFunction(next); + } + } + + /** + Remove a collection of child score functions. + @param c The child functions to remove. + */ + public final void removeChildFunctions(ScoreFunction... c) { + for (ScoreFunction next : c) { + removeChildFunction(next); + } + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + for (ScoreFunction next : children) { + next.initialise(world, config); + } + } + + /** + Get all the child functions. + @return All child functions. + */ + public Set<ScoreFunction> getChildFunctions() { + return Collections.unmodifiableSet(children); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/ConstantScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/ConstantScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..3de14a16bc265e2024a0614370bbbe6d6c0362e8 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/ConstantScoreFunction.java @@ -0,0 +1,37 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +/** + A score function that returns a constant score. + */ +public class ConstantScoreFunction extends AbstractScoreFunction { + private double score; + + /** + Create a ConstantScoreFunction. + @param name The name of this function. + @param score The constant score. + */ + public ConstantScoreFunction(String name, double score) { + super(name); + this.score = score; + } + + @Override + public String toString() { + return "Constant score"; + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + return score; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/CumulativeScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/CumulativeScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..de770b362168a35090fe232dd621e2df321d1a66 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/CumulativeScoreFunction.java @@ -0,0 +1,63 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import java.util.Collection; +import java.util.Map; +import java.util.HashMap; + +/** + A score function that accumulates scores from a set of child score functions. + */ +public class CumulativeScoreFunction extends CompositeScoreFunction { + private Map<Integer, Double> scores; + + /** + Create a CumulativeScoreFunction with no children. + @param name The name of this function. + */ + public CumulativeScoreFunction(String name) { + super(name); + } + + /** + Create a CumulativeScoreFunction with a collection of children. + @param name The name of this function. + @param children The child score functions. + */ + public CumulativeScoreFunction(String name, Collection<ScoreFunction> children) { + super(name, children); + } + + /** + Create a CumulativeScoreFunction with a collection of children. + @param name The name of this function. + @param children The child score functions. + */ + public CumulativeScoreFunction(String name, ScoreFunction... children) { + super(name, children); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + super.initialise(world, config); + scores = new HashMap<Integer, Double>(); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + for (ScoreFunction next : children) { + sum += next.score(world, timestep); + } + Double previous = scores.get(timestep.getTime() - 1); + if (previous != null) { + sum += previous; + } + scores.put(timestep.getTime(), sum); + return sum; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/DelegatingScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/DelegatingScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..35fdff7f83dfa538e9a7b1c3232ed341221338ec --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/DelegatingScoreFunction.java @@ -0,0 +1,42 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +/** + A score function that applies some function to the score of a child score function. + */ +public abstract class DelegatingScoreFunction extends AbstractScoreFunction { + /** The child score function. */ + protected ScoreFunction child; + + /** + Create a DelegatingScoreFunction with a child function. + @param name The name of this function. + @param c The child score function. + */ + public DelegatingScoreFunction(String name, ScoreFunction c) { + super(name); + child = c; + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + child.initialise(world, config); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + return child.score(world, timestep); + } + + /** + Get the child function. + @return The child function. + */ + public ScoreFunction getChildFunction() { + return child; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/InverseScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/InverseScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..8c5be3a9c6099628e073adca659e6c79dfeec45e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/InverseScoreFunction.java @@ -0,0 +1,29 @@ +package rescuecore2.score; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +/** + A score function that returns the inverse of a child score function. + */ +public class InverseScoreFunction extends DelegatingScoreFunction { + /** + Create an InverseScoreFunction. + @param name The name of this function. + @param child The child function to invert. + */ + public InverseScoreFunction(String name, ScoreFunction child) { + super(name, child); + } + + @Override + public String toString() { + return "Inverse"; + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + return 1.0 / child.score(world, timestep); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/MultiplicativeScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/MultiplicativeScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..29f74c53768cf643fd47c64c14877ad46e0ab128 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/MultiplicativeScoreFunction.java @@ -0,0 +1,52 @@ +package rescuecore2.score; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import java.util.Collection; + +/** + A score function that returns a set child score functions multiplied together. + */ +public class MultiplicativeScoreFunction extends CompositeScoreFunction { + /** + Create a MultiplicativeScoreFunction with no children. + @param name The name of this function. + */ + public MultiplicativeScoreFunction(String name) { + super(name); + } + + /** + Create a MultiplicativeScoreFunction with a collection of children. + @param name The name of this function. + @param children The child score functions. + */ + public MultiplicativeScoreFunction(String name, Collection<ScoreFunction> children) { + super(name, children); + } + + /** + Create a MultiplicativeScoreFunction with a collection of children. + @param name The name of this function. + @param children The child score functions. + */ + public MultiplicativeScoreFunction(String name, ScoreFunction... children) { + super(name, children); + } + + @Override + public String toString() { + return "Multiplier"; + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double result = 1; + for (ScoreFunction next : children) { + result *= next.score(world, timestep); + } + return result; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/ScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/ScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..7ab8f2b44cca3aa2b5d9408f465e856ed9b35d3b --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/ScoreFunction.java @@ -0,0 +1,32 @@ +package rescuecore2.score; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +/** + Interface for a score function. + */ +public interface ScoreFunction { + /** + Initialise this score function. + @param world The state of the world at the start of the simulation. + @param config The system configuration. + */ + void initialise(WorldModel<? extends Entity> world, Config config); + + /** + Calculate the score for a timestep. + @param world The state of the world at the end of the timestep. + @param timestep The record of perception, commands and changes for the timestep. + @return The score for this timestep. + */ + double score(WorldModel<? extends Entity> world, Timestep timestep); + + /** + Get the name of this score function. + @return The name. + */ + String getName(); +} diff --git a/modules/rescuecore2/src/rescuecore2/score/UnaryOperatorScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/UnaryOperatorScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..6988b3a818a92414ff55b6bb68f8360ad056b3b4 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/UnaryOperatorScoreFunction.java @@ -0,0 +1,55 @@ +package rescuecore2.score; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +/** + A score function that performs a unary operation on a child score function. + */ +public class UnaryOperatorScoreFunction extends DelegatingScoreFunction { + private Operator op; + + /** + Create a UnaryOperatorScoreFunction. + @param name The name of this function. + @param op The operation to perform. + @param child The child function to invert. + */ + public UnaryOperatorScoreFunction(String name, Operator op, ScoreFunction child) { + super(name, child); + this.op = op; + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + return op.perform(child.score(world, timestep)); + } + + /** + Enum constants for the possible operations this class supports. + */ + public static enum Operator { + /** The inversion operator. Returns 1 / <child score>. */ + INVERSE { + @Override + protected double perform(double in) { + return 1.0 / in; + } + }, + /** The square root operator. Returns Math.sqrt(<child score>). */ + SQUARE_ROOT { + @Override + protected double perform(double in) { + return Math.sqrt(in); + } + }; + + /** + Perform the unary operation. + @param in The input value. + @return The output value. + */ + protected abstract double perform(double in); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/score/WeightedScoreFunction.java b/modules/rescuecore2/src/rescuecore2/score/WeightedScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..9de03db543b210611140c3b2ce3564f4e5c58e91 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/score/WeightedScoreFunction.java @@ -0,0 +1,55 @@ +package rescuecore2.score; + +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import java.util.Map; +import java.util.HashMap; + +/** + A score function that returns a weighted sum of child score functions. + */ +public class WeightedScoreFunction extends CompositeScoreFunction { + private Map<ScoreFunction, Double> weights; + + /** + Create a WeightedScoreFunction with no children. + @param name The name of this function. + */ + public WeightedScoreFunction(String name) { + super(name); + weights = new HashMap<ScoreFunction, Double>(); + } + + @Override + public String toString() { + return "Weighted sum"; + } + + /** + Add a child score function with a weight. + @param child The child score function to add. + @param weight The weight of this child function. + */ + public void addChildFunction(ScoreFunction child, double weight) { + addChildFunction(child); + weights.put(child, weight); + } + + @Override + public void removeChildFunction(ScoreFunction child) { + super.removeChildFunction(child); + weights.remove(child); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + for (ScoreFunction next : children) { + double weight = weights.containsKey(next) ? weights.get(next) : 1; + sum += weight * next.score(world, timestep); + } + return sum; + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/AbstractViewLayer.java b/modules/rescuecore2/src/rescuecore2/view/AbstractViewLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..13c4a1e1e59d876514813ab9dc04ddb0a948743e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/AbstractViewLayer.java @@ -0,0 +1,87 @@ +package rescuecore2.view; + +import java.util.Collection; +import java.util.List; + +import javax.swing.JMenuItem; + +import rescuecore2.config.Config; + +/** + An abstract ViewLayer implementation. + */ +public abstract class AbstractViewLayer implements ViewLayer { + /** The LayerViewComponent this layer is part of. */ + protected LayerViewComponent component; + + private boolean visible; + + /** + Construct a new, visible AbstractViewLayer. + */ + protected AbstractViewLayer() { + visible = true; + } + + @Override + public void setLayerViewComponent(LayerViewComponent c) { + component = c; + } + + @Override + public List<JMenuItem> getPopupMenuItems() { + return null; + } + + @Override + public void initialise(Config config) { + } + + @Override + public void setVisible(boolean b) { + visible = b; + } + + @Override + public boolean isVisible() { + return visible; + } + + /** + Process a set of objects and recursively inspect collections and arrays. + @param objects The objects to process. + */ + protected void processView(Object... objects) { + if (objects == null) { + return; + } + for (Object next : objects) { + process(next); + } + } + + /** + Callback function for processing a concrete viewable object. + @param o The object to process. + */ + protected abstract void viewObject(Object o); + + private void process(Object o) { + if (o == null) { + return; + } + if (o instanceof Collection) { + for (Object next : ((Collection)o)) { + process(next); + } + } + else if (o.getClass().isArray()) { + for (Object next : (Object[])o) { + process(next); + } + } + else { + viewObject(o); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/EntityInspector.java b/modules/rescuecore2/src/rescuecore2/view/EntityInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..018a6cdba1ad7938ac47b99d03830a2fe8043c1d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/EntityInspector.java @@ -0,0 +1,126 @@ +package rescuecore2.view; + +import static rescuecore2.misc.collections.ArrayTools.convertArrayObjectToString; + +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Comparator; + +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.Property; + +/** + A component for inspecting Entities. +*/ +public class EntityInspector extends JTable { + private static final Comparator<Property> PROPERTY_NAME_COMPARATOR = new Comparator<Property>() { + @Override + public int compare(Property p1, Property p2) { + return Integer.compare(p1.getURN(),p2.getURN()); + } + }; + + private EntityTableModel model; + + /** + Create a new EntityInspector. + */ + public EntityInspector() { + model = new EntityTableModel(); + setModel(model); + } + + /** + Inspect an entity. + @param e The entity to inspect. + */ + public void inspect(Entity e) { + model.setEntity(e); + } + + private static class EntityTableModel extends AbstractTableModel { + private Entity e; + private List<Property> props; + + public EntityTableModel() { + e = null; + props = new ArrayList<Property>(); + } + + public void setEntity(Entity entity) { + e = entity; + props.clear(); + if (e != null) { + props.addAll(e.getProperties()); + Collections.sort(props, PROPERTY_NAME_COMPARATOR); + } + fireTableStructureChanged(); + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return props.size() + 2; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int row, int col) { + switch (col) { + case 0: + if (row == 0) { + return "ID"; + } + else if (row == 1) { + return "Type"; + } + else { + return Registry.getCurrentRegistry().toPrettyName(props.get(row - 2).getURN()); + } + case 1: + if (row == 0) { + return e == null ? "" : e.getID(); + } + else if (row == 1) { + return e == null ? "" : e.getURN(); + } + else { + Property prop = props.get(row - 2); + if (prop.isDefined()) { + Object value = prop.getValue(); + if (value.getClass().isArray()) { + return convertArrayObjectToString(value); + } + return value; + } + else { + return "Undefined"; + } + } + default: + throw new IllegalArgumentException("Invalid column: " + col); + } + } + + @Override + public String getColumnName(int col) { + switch (col) { + case 0: + return "Property"; + case 1: + return "Value"; + default: + throw new IllegalArgumentException("Invalid column: " + col); + } + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/Icons.java b/modules/rescuecore2/src/rescuecore2/view/Icons.java new file mode 100644 index 0000000000000000000000000000000000000000..7a4d5a42bed91e90b878ddced469e568c027b779 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/Icons.java @@ -0,0 +1,32 @@ +package rescuecore2.view; + +import java.net.URL; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import rescuecore2.log.Logger; + +/** + Some common icons. +*/ +public final class Icons { + /** A tick icon. */ + public static final Icon TICK = Icons.get(Icons.class,"rescuecore2/view/tick.png"); + + /** A cross icon. */ + public static final Icon CROSS = Icons.get(Icons.class,"rescuecore2/view/cross.png"); + + private Icons() {} + + public static ImageIcon get(Class<?> clas, String path) { + try { + URL iconPath = clas.getClassLoader().getResource(path); + return new ImageIcon(iconPath); + }catch(Exception e) { + Logger.error(clas+" : " + path +" not find"); + return new ImageIcon(); + } + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/view/LayerViewComponent.java b/modules/rescuecore2/src/rescuecore2/view/LayerViewComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..58de19bfb960108a88b5d7c67c810444ef8e6066 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/LayerViewComponent.java @@ -0,0 +1,225 @@ +package rescuecore2.view; + +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.awt.event.MouseListener; +import java.awt.event.MouseEvent; +import java.awt.event.ActionEvent; + +import javax.swing.JPopupMenu; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.Action; +import javax.swing.AbstractAction; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.config.Config; + +/** + A ViewComponent that uses layers. + */ +public class LayerViewComponent extends ViewComponent { + private Config config; + private List<ViewLayer> layers; + private Map<ViewLayer, Action> layerActions; + private Object[] data; + private Rectangle2D bounds; + + /** + Construct a new LayerViewComponent. + */ + public LayerViewComponent() { + layers = new ArrayList<ViewLayer>(); + layerActions = new HashMap<ViewLayer, Action>(); + addMouseListener(new MouseListener() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e.getX(), e.getY()); + } + } + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e.getX(), e.getY()); + } + } + @Override + public void mouseClicked(MouseEvent e) { + } + @Override + public void mouseEntered(MouseEvent e) { + } + @Override + public void mouseExited(MouseEvent e) { + } + }); + } + + @Override + public void initialise(Config c) { + this.config = c; + for (ViewLayer next : layers) { + next.initialise(config); + } + } + + /** + Add a view layer. + @param layer The layer to add. + */ + public void addLayer(ViewLayer layer) { + layers.add(layer); + layer.setLayerViewComponent(this); + layerActions.put(layer, new LayerAction(layer)); + if (config != null) { + layer.initialise(config); + } + computeBounds(); + } + + /** + Remove a view layer. + @param layer The layer to remove. + */ + public void removeLayer(ViewLayer layer) { + int index = layers.indexOf(layer); + if (index != -1) { + layers.remove(index); + layerActions.remove(layer); + layer.setLayerViewComponent(null); + computeBounds(); + } + } + + /** + Remove all view layers. + */ + public void removeAllLayers() { + for (ViewLayer next : layers) { + next.setLayerViewComponent(null); + } + layers.clear(); + layerActions.clear(); + computeBounds(); + } + + @Override + public void view(Object... objects) { + data = objects; + computeBounds(); + super.view(objects); + } + + @Override + protected Collection<RenderedObject> render(Graphics2D g, ScreenTransform transform, int width, int height) { + Collection<RenderedObject> result = new HashSet<RenderedObject>(); + prepaint(); + for (ViewLayer next : layers) { + if (next.isVisible()) { + Graphics2D copy = (Graphics2D)g.create(); + result.addAll(next.render(copy, transform, width, height)); + } + } + postpaint(); + return result; + } + + /** + Get all installed layers. + @return All installed layers. + */ + protected List<ViewLayer> getLayers() { + return Collections.unmodifiableList(layers); + } + + /** + Do whatever needs doing before the layers are painted. The default implementation does nothing. + */ + protected void prepaint() { + } + + /** + Do whatever needs doing after the layers are painted. The default implementation does nothing. + */ + protected void postpaint() { + } + + private void computeBounds() { + Rectangle2D oldBounds = bounds; + bounds = null; + for (ViewLayer next : layers) { + expandBounds(next.view(data)); + } + if (bounds == null) { + updateBounds(0, 0, 1, 1); + } + else if (oldBounds == null + || oldBounds.getMinX() != bounds.getMinX() + || oldBounds.getMinY() != bounds.getMinY() + || oldBounds.getMaxX() != bounds.getMaxX() + || oldBounds.getMaxY() != bounds.getMaxY()) { + updateBounds(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY()); + } + } + + private void expandBounds(Rectangle2D next) { + if (next == null) { + return; + } + if (bounds == null) { + bounds = next; + } + else { + Rectangle2D.union(bounds, next, bounds); + } + } + + private void showPopupMenu(int x, int y) { + JPopupMenu menu = new JPopupMenu(); + for (ViewLayer next : layers) { + Action action = layerActions.get(next); + JMenu layerMenu = new JMenu(next.getName()); + layerMenu.add(new JMenuItem(action)); + if (next.isVisible()) { + List<JMenuItem> items = next.getPopupMenuItems(); + if (items != null && !items.isEmpty()) { + layerMenu.addSeparator(); + for (JMenuItem item : items) { + layerMenu.add(item); + } + } + } + menu.add(layerMenu); + } + menu.show(this, x, y); + } + + private class LayerAction extends AbstractAction { + private ViewLayer layer; + + public LayerAction(ViewLayer layer) { + super("Visible"); + this.layer = layer; + putValue(Action.SELECTED_KEY, Boolean.valueOf(layer.isVisible())); + putValue(Action.SMALL_ICON, layer.isVisible() ? Icons.TICK : Icons.CROSS); + } + + @Override + public void actionPerformed(ActionEvent e) { + boolean selected = ((Boolean)getValue(Action.SELECTED_KEY)).booleanValue(); + putValue(Action.SELECTED_KEY, Boolean.valueOf(!selected)); + putValue(Action.SMALL_ICON, !selected ? Icons.TICK : Icons.CROSS); + layer.setVisible(!selected); + LayerViewComponent.this.repaint(); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/RenderedObject.java b/modules/rescuecore2/src/rescuecore2/view/RenderedObject.java new file mode 100644 index 0000000000000000000000000000000000000000..799c6d0b16dbd0ed8c8134c9fa97cb7079e406a3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/RenderedObject.java @@ -0,0 +1,42 @@ +package rescuecore2.view; + +import java.awt.Shape; + +/** + A representation of something that has been rendered on screen. + */ +public class RenderedObject { + private Shape shape; + private Object object; + + /** + Construct a new rendered object. + @param object The thing that was rendered. + @param shape The on-screen shape of the thing that was rendered. + */ + public RenderedObject(Object object, Shape shape) { + this.object = object; + this.shape = shape; + } + + /** + Get the on-screen shape of the item that was rendered. + @return The on-screen shape. + */ + public Shape getShape() { + return shape; + } + + /** + Get the item that was rendered. + @return The item that was rendered. + */ + public Object getObject() { + return object; + } + + @Override + public String toString() { + return object.toString(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/ViewComponent.java b/modules/rescuecore2/src/rescuecore2/view/ViewComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..3cb20e00ed2d3c8ddfae2cd5fef69dffdfe40cb8 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/ViewComponent.java @@ -0,0 +1,217 @@ +package rescuecore2.view; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Collections; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Color; +import java.awt.Insets; +import java.awt.Shape; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseEvent; +import javax.swing.JComponent; + +import rescuecore2.config.Config; +import rescuecore2.misc.gui.PanZoomListener; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A JComponent that shows a view of a world model. + */ +public abstract class ViewComponent extends JComponent { + private static final Color BACKGROUND = new Color(120, 120, 120); + + private PanZoomListener panZoom; + private ScreenTransform transform; + + private List<RenderedObject> renderedObjects; + private Set<ViewListener> listeners; + + /** + Construct a new ViewComponent. + */ + protected ViewComponent() { + renderedObjects = new ArrayList<RenderedObject>(); + listeners = new HashSet<ViewListener>(); + ViewerMouseListener l = new ViewerMouseListener(); + addMouseListener(l); + addMouseMotionListener(l); + panZoom = new PanZoomListener(this); + setBackground(BACKGROUND); + setOpaque(true); + } + + /** + Initialise this view component. Default implementation does nothing. + @param config The system configuration. + */ + public void initialise(Config config) { + } + + /** + Get the name of this view component. Default implementation calls toString(). + @return The name of this view component. + */ + public String getViewerName() { + return toString(); + } + + /** + Disable the pan-zoom feature. + */ + public void disablePanZoom() { + removeMouseListener(panZoom); + removeMouseMotionListener(panZoom); + removeMouseWheelListener(panZoom); + transform.resetZoom(); + repaint(); + } + + /** + Enable the pan-zoom feature. + */ + public void enablePanZoom() { + addMouseListener(panZoom); + addMouseMotionListener(panZoom); + addMouseWheelListener(panZoom); + } + + /** + Add a view listener. + @param l The listener to add. + */ + public void addViewListener(ViewListener l) { + synchronized (listeners) { + listeners.add(l); + } + } + + /** + Remove a view listener. + @param l The listener to remove. + */ + public void removeViewListener(ViewListener l) { + synchronized (listeners) { + listeners.remove(l); + } + } + + /** + View a set of objects. Default implementation just calls repaint. + @param objects The objects to view. + */ + public void view(Object... objects) { + repaint(); + } + + @Override + public final void paintComponent(Graphics g) { + super.paintComponent(g); + int width = getWidth(); + int height = getHeight(); + Insets insets = getInsets(); + width -= insets.left + insets.right; + height -= insets.top + insets.bottom; + renderedObjects.clear(); + transform.rescale(width, height); + Graphics2D copy = (Graphics2D)g.create(insets.left, insets.top, width, height); + if (isOpaque()) { + copy.setColor(getBackground()); + copy.fillRect(0, 0, width, height); + } + renderedObjects.addAll(render(copy, transform, width, height)); + } + + /** + Render the world and return the set of RenderedObjects. + @param graphics The graphics to draw on. The origin is guaranteed to be at 0, 0. + @param transform The ScreenTransform that will convert world coordinates to screen coordinates. + @param width The width of the viewport. + @param height The height of the viewport. + @return The set of RenderedObjects. + */ + // CHECKSTYLE:OFF:HiddenField Supress bogus warning about transform hiding a field: it's not really hidden since this is an abstract method and transform is private. + protected abstract Collection<RenderedObject> render(Graphics2D graphics, ScreenTransform transform, int width, int height); + // CHECKSTYLE:ON:HiddenField + + /** + Update the bounds of the view in world coordinates. + @param xMin The minimum x coordinate. + @param yMin The minimum y coordinate. + @param xMax The maximum x coordinate. + @param yMax The maximum y coordinate. + */ + protected void updateBounds(double xMin, double yMin, double xMax, double yMax) { + transform = new ScreenTransform(xMin, yMin, xMax, yMax); + panZoom.setScreenTransform(transform); + } + + private List<RenderedObject> getObjectsAtPoint(int x, int y) { + List<RenderedObject> result = new ArrayList<RenderedObject>(); + for (RenderedObject next : renderedObjects) { + Shape shape = next.getShape(); + if (shape != null && shape.contains(x, y)) { + result.add(next); + } + } + Collections.reverse(result); + return result; + } + + private void fireObjectsClicked(List<RenderedObject> clicked) { + List<ViewListener> all = new ArrayList<ViewListener>(); + synchronized (listeners) { + all.addAll(listeners); + } + for (ViewListener next : all) { + next.objectsClicked(this, clicked); + } + } + + private void fireObjectsRollover(List<RenderedObject> objects) { + List<ViewListener> all = new ArrayList<ViewListener>(); + synchronized (listeners) { + all.addAll(listeners); + } + for (ViewListener next : all) { + next.objectsRollover(this, objects); + } + } + + private class ViewerMouseListener implements MouseListener, MouseMotionListener { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + fireObjectsClicked(getObjects(e)); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + fireObjectsRollover(getObjects(e)); + } + + @Override + public void mousePressed(MouseEvent e) {} + @Override + public void mouseReleased(MouseEvent e) {} + @Override + public void mouseEntered(MouseEvent e) {} + @Override + public void mouseExited(MouseEvent e) {} + @Override + public void mouseDragged(MouseEvent e) {} + + private List<RenderedObject> getObjects(MouseEvent e) { + Point p = e.getPoint(); + Insets insets = getInsets(); + return getObjectsAtPoint(p.x - insets.left, p.y - insets.top); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/view/ViewLayer.java b/modules/rescuecore2/src/rescuecore2/view/ViewLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..7713b6a760f6068ade42295b4a575cbe9e566312 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/ViewLayer.java @@ -0,0 +1,70 @@ +package rescuecore2.view; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +import javax.swing.JMenuItem; + +import java.util.Collection; +import java.util.List; + +import rescuecore2.config.Config; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A layer of a view. The LayerViewComponent is composed of a list of ViewLayer objects. + */ +public interface ViewLayer { + /** + Initialise this view layer. + @param config The system configuration. + */ + void initialise(Config config); + + /** + Set the LayerViewComponent for this layer. + @param component The LayerViewComponent that this layer is part of. + */ + void setLayerViewComponent(LayerViewComponent component); + + /** + Get the menu items this layer wants added to the LayerViewComponent popup menu. + @return A list of menu items, or null if no menu items are required. + */ + List<JMenuItem> getPopupMenuItems(); + + /** + Set the list of objects this layer should view. The layer should record the objects it understands and draw them when {@link #render(Graphics2D, ScreenTransform, int, int)} is called. + @param objects The objects to view. + @return A Rectangle2D with the bounds of the area this layer wants to draw. This may be null if this layer has nothing to draw. + */ + Rectangle2D view(Object... objects); + + /** + Render this layer and return a collection of RenderedObjects. + @param g The graphics to render to. + @param transform The ScreenTransform that will convert world coordinates to screen coordinates. + @param width The width of the screen in pixels. + @param height The height of the screen in pixels. + @return A set of RenderedObjects representing the things that were actually rendered. + */ + Collection<RenderedObject> render(Graphics2D g, ScreenTransform transform, int width, int height); + + /** + Set whether this layer should be rendered or not. + @param b True if this layer should be rendered, false otherwise. + */ + void setVisible(boolean b); + + /** + Find out if this layer should be rendered or not. + @return True if this layer should be rendered, false otherwise. + */ + boolean isVisible(); + + /** + Get the name of this layer. + @return The name of the layer. + */ + String getName(); +} diff --git a/modules/rescuecore2/src/rescuecore2/view/ViewListener.java b/modules/rescuecore2/src/rescuecore2/view/ViewListener.java new file mode 100644 index 0000000000000000000000000000000000000000..30fd88d80b09e3338a4982ee43b43393377edfd0 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/view/ViewListener.java @@ -0,0 +1,22 @@ +package rescuecore2.view; + +import java.util.List; + +/** + A listener for view events. + */ +public interface ViewListener { + /** + Notification that a set of objects were clicked. + @param view The ViewComponent that was clicked. + @param objects The list of objects that were under the click point. + */ + void objectsClicked(ViewComponent view, List<RenderedObject> objects); + + /** + Notification that a set of objects were rolled over. + @param view The ViewComponent that was rolled over. + @param objects The list of objects that were under the mouse point. + */ + void objectsRollover(ViewComponent view, List<RenderedObject> objects); +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractEntity.java b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..6535bbf6ae9e0efa1f11b078e6f53bb093452497 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractEntity.java @@ -0,0 +1,236 @@ +package rescuecore2.worldmodel; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readProperty; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeProperty; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.messages.protobuf.RCRSProto.EntityProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; + +/** + * Abstract base class for concrete Entity implementations. + */ +public abstract class AbstractEntity implements Entity { + + private final EntityID id; + private final Set<EntityListener> listeners; + private final Set<Property> properties; + + /** + * Construct an AbstractEntity with a set of properties. + * + * @param id The ID of this entity. + */ + protected AbstractEntity(EntityID id) { + this.id = id; + listeners = new HashSet<EntityListener>(); + properties = new HashSet<Property>(); + } + + /** + * AbstractEntity copy constructor. + * + * @param other The AbstractEntity to copy. + */ + protected AbstractEntity(AbstractEntity other) { + this(other.getID()); + } + + @Override + public void addEntityListener(EntityListener l) { + synchronized (listeners) { + listeners.add(l); + } + } + + @Override + public void removeEntityListener(EntityListener l) { + synchronized (listeners) { + listeners.remove(l); + } + } + + @Override + public Entity copy() { + Entity result = copyImpl(); + for (Property original : getProperties()) { + Property copy = result.getProperty(original.getURN()); + copy.takeValue(original); + } + return result; + } + + /** + * Create a copy of this entity. Property values do not need to be copied. + * + * @return A new Entity of the same type as this and with the same ID. + */ + protected abstract Entity copyImpl(); + + @Override + public final Set<Property> getProperties() { + return properties; + } + + @Override + public Property getProperty(int propertyURN) { + return null; + } + + @Override + public EntityID getID() { + return id; + } + + @Override + public void write(OutputStream out) throws IOException { + int count = 0; + for (Property next : getProperties()) { + if (next.isDefined()) { + ++count; + } + } + writeInt32(count, out); + for (Property next : getProperties()) { + if (next.isDefined()) { + writeProperty(next, out); + } + } + } + + @Override + public void read(InputStream in) throws IOException { + int count = readInt32(in); + for (int i = 0; i < count; ++i) { + Property prop = readProperty(in); + if (prop == null) { + continue; + } + Property existing = getProperty(prop.getURN()); + existing.takeValue(prop); + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(getEntityName()); + result.append(" ("); + result.append(id); + result.append(")"); + return result.toString(); + } + + /** + * Get the full description of this object. + * + * @return The full description. + */ + public String getFullDescription() { + StringBuilder result = new StringBuilder(); + String name = getEntityName(); + int urn = getURN(); + result.append(name); + result.append(" ["); + result.append(urn); + result.append("]"); + result.append(" ("); + result.append(id); + result.append(") ["); + for (Iterator<Property> it = getProperties().iterator(); it.hasNext();) { + result.append(it.next().toString()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof AbstractEntity) { + AbstractEntity a = (AbstractEntity) o; + return this.id.equals(a.id); + } + return false; + } + + /** + * Get the name of this entity. Default implementation returns the entity URN. + * + * @return The name of this entity. + */ + protected String getEntityName() { + return this.getClass().getSimpleName(); + } + + /** + * Register a set of properties. + * + * @param props The properties to register. + */ + protected void registerProperties(Property... props) { + for (Property p : props) { + properties.add(p); + if (p instanceof AbstractProperty) { + ((AbstractProperty) p).setEntity(this); + } + } + } + + /** + * Notify all listeners that a property has changed. + * + * @param p The changed property. + * @param oldValue The old value. + * @param newValue The new value. + */ + protected void firePropertyChanged(Property p, Object oldValue, Object newValue) { + Collection<EntityListener> copy; + synchronized (listeners) { + copy = new HashSet<EntityListener>(listeners); + } + for (EntityListener next : copy) { + next.propertyChanged(this, p, oldValue, newValue); + } + } + + @Override + public EntityProto toEntityProto() { + EntityProto.Builder builder = EntityProto.newBuilder().setEntityID(id.getValue()).setUrn(getURN()); + for (Property next : getProperties()) { + if (next.isDefined()) { + builder.addProperties(next.toPropertyProto()); + } + } + return builder.build(); + } + + @Override + public void fromEntityProto(EntityProto proto) { + for (PropertyProto propertyProto : proto.getPropertiesList()) { + Property prop = MsgProtoBuf.propertyProto2Property(propertyProto); + if (prop == null) { + continue; + } + Property existing = getProperty(prop.getURN()); + existing.takeValue(prop); + } + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..ca47f4c8ee3e9a6d709fc47c5c7405fa06951294 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractProperty.java @@ -0,0 +1,129 @@ +package rescuecore2.worldmodel; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; + +/** + * Abstract base class for Property implementations. + */ +public abstract class AbstractProperty implements Property { + private boolean defined; + private final int urn; + private AbstractEntity entity; + + /** + * Construct a property with a given type and assume that the value of this + * property is initially undefined. + * + * @param urn The urn of the property. + */ + protected AbstractProperty(int urn) { + this(urn, false); + } + + /** + * Construct a property with a given type and assume that the value of this + * property is initially undefined. + * + * @param urn The urn of the property. + */ + protected AbstractProperty(URN urn) { + this(urn.getURNId(), false); + } + + /** + * Construct a property with a given type and whether the value of this property + * is initially defined or not. + * + * @param urn The urn of the property. + * @param defined Whether the value is initially defined or not. + */ + protected AbstractProperty(int urn, boolean defined) { + this.urn = urn; + this.defined = defined; + entity = null; + } + + /** + * Construct a property with a given type and whether the value of this property + * is initially defined or not. + * + * @param urn The urn of the property. + * @param defined Whether the value is initially defined or not. + */ + protected AbstractProperty(URN urn, boolean defined) { + this(urn.getURNId(), defined); + } + + /** + * AbstractProperty copy constructor. + * + * @param other The AbstractProperty to copy. + */ + protected AbstractProperty(AbstractProperty other) { + this(other.getURN(), other.isDefined()); + } + + /** + * Set the property status to defined. + */ + protected void setDefined() { + this.defined = true; + } + + /** + * Set this property's containing Entity. + * + * @param e The AbstractEntity that holds this property. + */ + protected void setEntity(AbstractEntity e) { + this.entity = e; + } + + @Override + public boolean isDefined() { + return this.defined; + } + + @Override + public void undefine() { + Object old = getValue(); + this.defined = false; + fireChange(old, null); + } + + @Override + public int getURN() { + return this.urn; + } + + /** + * Notify the entity that this property has changed. + * + * @param oldValue The old value of this property. + * @param newValue The new value of this property. + */ + protected void fireChange(Object oldValue, Object newValue) { + if (this.entity != null) { + this.entity.firePropertyChanged(this, oldValue, newValue); + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(getURN()); + if (isDefined()) { + result.append(" = "); + result.append(getValue()); + } else { + result.append(" (undefined)"); + } + return result.toString(); + } + + protected PropertyProto.Builder basePropertyProto() { + PropertyProto.Builder builder = PropertyProto.newBuilder().setUrn(getURN()).setDefined(isDefined()); + return builder; + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractWorldModel.java b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractWorldModel.java new file mode 100644 index 0000000000000000000000000000000000000000..6b45b65bc2743a85885d20c8774fd4ffffdb24e7 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/AbstractWorldModel.java @@ -0,0 +1,161 @@ +package rescuecore2.worldmodel; + +import java.util.Set; +import java.util.HashSet; +import java.util.Collection; + +import rescuecore2.registry.Registry; + +/** + Abstract base class for WorldModel implementations. + @param <T> The subclass of Entity that this world model holds. +*/ +public abstract class AbstractWorldModel<T extends Entity> implements WorldModel<T> { + private Set<WorldModelListener<? super T>> listeners; + private Collection<Class<? extends T>> allowedClasses; + + /** + Construct a new abstract world model. + */ + protected AbstractWorldModel() { + listeners = new HashSet<WorldModelListener<? super T>>(); + allowedClasses = new HashSet<Class<? extends T>>(); + } + + @Override + public final void addWorldModelListener(WorldModelListener<? super T> l) { + synchronized (listeners) { + listeners.add(l); + } + } + + @Override + public final void removeWorldModelListener(WorldModelListener<? super T> l) { + synchronized (listeners) { + listeners.remove(l); + } + } + + @Override + @SuppressWarnings("unchecked") + public final void addEntity(Entity e) { + if (isAllowed(e)) { + addEntityImpl((T)e); + } + else { + throw new IllegalArgumentException(this + " does not accept entities of type " + e.getClass().getName()); + } + } + + @Override + public final void addEntities(Collection<? extends Entity> e) { + for (Entity next : e) { + addEntity(next); + } + } + + @Override + public final void removeEntity(T e) { + removeEntity(e.getID()); + } + + /** + Subclasses should provide their implementation of addEntity here. + @param t The entity to add. + */ + protected abstract void addEntityImpl(T t); + + @Override + public void merge(Collection<? extends Entity> toMerge) { + for (Entity next : toMerge) { + T existing = getEntity(next.getID()); + if (existing == null) { + addEntity(next); + } + else { + Set<Property> props = existing.getProperties(); + for (Property prop : props) { + Property other = next.getProperty(prop.getURN()); + if (other.isDefined()) { + prop.takeValue(other); + } + } + } + } + } + + @Override + public void merge(ChangeSet changeSet) { + for (EntityID e : changeSet.getChangedEntities()) { + Entity existingEntity = getEntity(e); + boolean add = false; + if (existingEntity == null) { + // Construct a new entity + existingEntity = Registry.getCurrentRegistry().createEntity(changeSet.getEntityURN(e), e); + if (existingEntity == null) { + // Bail out + continue; + } + add = true; + } + for (Property p : changeSet.getChangedProperties(e)) { + Property existingProperty = existingEntity.getProperty(p.getURN()); + existingProperty.takeValue(p); + } + if (add) { + addEntity(existingEntity); + } + } + for (EntityID next : changeSet.getDeletedEntities()) { + removeEntity(next); + } + } + + /** + Notify listeners that an entity has been added. + @param e The new entity. + */ + protected final void fireEntityAdded(T e) { + for (WorldModelListener<? super T> l : getListeners()) { + l.entityAdded(this, e); + } + } + + /** + Notify listeners that an entity has been removed. + @param e The entity that has been removed. + */ + protected final void fireEntityRemoved(T e) { + for (WorldModelListener<? super T> l : getListeners()) { + l.entityRemoved(this, e); + } + } + + /** + Find out if a particular Entity is allowed into this world model. The default implementation checks that the object is assignable to at least one class previously registered with {@link #registerAllowedClass(Class)}. + @param e The entity to check. + @return True if the entity is allowed, false otherwise. + */ + protected boolean isAllowed(Entity e) { + for (Class<? extends T> next : allowedClasses) { + if (next.isAssignableFrom(e.getClass())) { + return true; + } + } + return false; + } + + /** + Register an allowed class. + @param clazz The subclass of Entity that is allowed in this world model. + */ + protected void registerAllowedClass(Class<? extends T> clazz) { + allowedClasses.add(clazz); + } + + private Collection<WorldModelListener<? super T>> getListeners() { + synchronized (listeners) { + return new HashSet<WorldModelListener<? super T>>(listeners); + } + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/ChangeSet.java b/modules/rescuecore2/src/rescuecore2/worldmodel/ChangeSet.java new file mode 100644 index 0000000000000000000000000000000000000000..7add03129ddd0bc718ffa403f019f095fdd68f1c --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/ChangeSet.java @@ -0,0 +1,360 @@ +package rescuecore2.worldmodel; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.readProperty; +import static rescuecore2.misc.EncodingTools.readString; +import static rescuecore2.misc.EncodingTools.writeInt32; +import static rescuecore2.misc.EncodingTools.writeProperty; +import static rescuecore2.misc.EncodingTools.writeString; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import rescuecore2.log.Logger; +import rescuecore2.messages.protobuf.MsgProtoBuf; +import rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto; +import rescuecore2.messages.protobuf.RCRSProto.ChangeSetProto.EntityChangeProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.registry.Registry; +//import rescuecore2.standard.entities.StandardPropertyURN; +import rescuecore2.worldmodel.properties.EntityRefListProperty; + +/** + * This class is used for accumulating changes to entities. + */ +public class ChangeSet { + + private Map<EntityID, Map<Integer, Property>> changes; + private Set<EntityID> deleted; + private Map<EntityID, Integer> entityURNs; + + /** + * Create an empty ChangeSet. + */ + public ChangeSet() { + changes = new LazyMap<EntityID, Map<Integer, Property>>() { + + @Override + public Map<Integer, Property> createValue() { + return new HashMap<Integer, Property>(); + } + }; + entityURNs = new HashMap<EntityID, Integer>(); + deleted = new HashSet<EntityID>(); + } + + /** + * Copy constructor. + * + * @param other The ChangeSet to copy. + */ + public ChangeSet(ChangeSet other) { + this(); + merge(other); + } + + /** + * Add a change. + * + * @param e The entity that has changed. + * @param p The property that has changed. + */ + public void addChange(Entity e, Property p) { + addChange(e.getID(), e.getURN(), p); + } + + /** + * Add a change. + * + * @param e The ID of the entity that has changed. + * @param urn The URN of the entity that has changed. + * @param p The property that has changed. + */ + public void addChange(EntityID e, int urn, Property p) { + if (deleted.contains(e)) { + return; + } + Property prop = p.copy(); + changes.get(e).put(prop.getURN(), prop); + entityURNs.put(e, urn); + } + + /** + * Register a deleted entity. + * + * @param e The ID of the entity that has been deleted. + */ + public void entityDeleted(EntityID e) { + deleted.add(e); + changes.remove(e); + } + + /** + * Get the properties that have changed for an entity. + * + * @param e The entity ID to look up. + * @return The set of changed properties. This may be empty but will never be + * null. + */ + public Set<Property> getChangedProperties(EntityID e) { + return new HashSet<Property>(changes.get(e).values()); + } + + /** + * Look up a property change for an entity by property URN. + * + * @param e The entity ID to look up. + * @param urn The property URN to look up. + * @return The changed property with the right URN, or null if the property is + * not found or has not changed. + */ + public Property getChangedProperty(EntityID e, int urn) { + Map<Integer, Property> props = changes.get(e); + if (props != null) { + return props.get(urn); + } + return null; + } + + /** + * Get the IDs of all changed entities. + * + * @return A set of IDs of changed entities. + */ + public Set<EntityID> getChangedEntities() { + return new HashSet<EntityID>(changes.keySet()); + } + + /** + * Get the IDs of all deleted entities. + * + * @return A set of IDs of deleted entities. + */ + public Set<EntityID> getDeletedEntities() { + return new HashSet<EntityID>(deleted); + } + + /** + * Get the URN of a changed entity. + * + * @param id The ID of the entity. + * @return The URN of the changed entity. + */ + public int getEntityURN(EntityID id) { + return entityURNs.get(id); + } + + /** + * Merge another ChangeSet into this one. + * + * @param other The other ChangeSet. + */ + // private static final String BLOCKADES_URN = StandardPropertyURN.BLOCKADES + // .toString(); + + public void merge(ChangeSet other) { + for (Map.Entry<EntityID, Map<Integer, Property>> next : other.changes.entrySet()) { + EntityID e = next.getKey(); + int urn = other.getEntityURN(e); + for (Property p : next.getValue().values()) { + + // if ( p.getURN().equals( BLOCKADES_URN ) + // && changes.get( e ).containsKey( BLOCKADES_URN ) ) { + + if ((p instanceof EntityRefListProperty) + && (changes.get(e).containsKey(urn) && (changes.get(e).get(urn) instanceof EntityRefListProperty))) { + + EntityRefListProperty bp1 = (EntityRefListProperty) p.copy(); + // EntityRefListProperty bp2 = (EntityRefListProperty) + // changes.get( e ) + // .get( BLOCKADES_URN ); + EntityRefListProperty bp2 = (EntityRefListProperty) changes.get(e).get(urn); + + if (bp2.isDefined()) { + for (EntityID id : bp2.getValue()) + bp1.addValue(id); + } + + for (EntityID id : deleted) { + bp1.removeValue(id); + } + + for (EntityID id : other.deleted) { + bp1.removeValue(id); + } + + p = bp1; + } + + addChange(e, urn, p); + } + } + deleted.addAll(other.deleted); + } + + /** + * Add all defined properties from a collection. + * + * @param c The collection to copy changes from. + */ + public void addAll(Collection<? extends Entity> c) { + for (Entity entity : c) { + for (Property property : entity.getProperties()) { + if (property.isDefined()) { + addChange(entity, property); + } + } + } + } + + /** + * Write this ChangeSet to a stream. + * + * @param out The stream to write to. + * @throws IOException If there is a problem. + */ + public void write(OutputStream out) throws IOException { + // Number of entity IDs + writeInt32(changes.size(), out); + for (Map.Entry<EntityID, Map<Integer, Property>> next : changes.entrySet()) { + EntityID id = next.getKey(); + Collection<Property> props = next.getValue().values(); + // EntityID, URN, number of properties + writeInt32(id.getValue(), out); + writeString(Registry.getCurrentRegistry().toURN_Str(getEntityURN(id)), out); + writeInt32(props.size(), out); + for (Property prop : props) { + writeProperty(prop, out); + } + } + writeInt32(deleted.size(), out); + for (EntityID next : deleted) { + writeInt32(next.getValue(), out); + } + } + + /** + * Read this ChangeSet from a stream. + * + * @param in The stream to read from. + * @throws IOException If there is a problem. + */ + public void read(InputStream in) throws IOException { + changes.clear(); + deleted.clear(); + int entityCount = readInt32(in); + for (int i = 0; i < entityCount; ++i) { + EntityID id = new EntityID(readInt32(in)); + int urn = Registry.getCurrentRegistry().toURN_Id(readString(in)); + int propCount = readInt32(in); + for (int j = 0; j < propCount; ++j) { + Property p = readProperty(in); + if (p != null) { + addChange(id, urn, p); + } + } + } + int deletedCount = readInt32(in); + for (int i = 0; i < deletedCount; ++i) { + EntityID id = new EntityID(readInt32(in)); + deleted.add(id); + } + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("ChangeSet:"); + for (Entry<EntityID, Map<Integer, Property>> next : changes.entrySet()) { + result.append(" Entity "); + result.append(next.getKey()); + result.append(" ("); + result.append(getEntityURN(next.getKey())); + result.append(") ["); + for (Iterator<Property> it = next.getValue().values().iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("]"); + } + result.append(" {Deleted "); + for (Iterator<EntityID> it = deleted.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("}"); + return result.toString(); + } + + /** + * Write this changeset to Logger.debug in a readable form. + */ + public void debug() { + Logger.debug("ChangeSet"); + for (Map.Entry<EntityID, Map<Integer, Property>> next : changes.entrySet()) { + Logger.debug(" Entity " + next.getKey() + "(" + getEntityURN(next.getKey()) + ")"); + for (Iterator<Property> it = next.getValue().values().iterator(); it.hasNext();) { + Logger.debug(" " + it.next()); + } + } + for (Iterator<EntityID> it = deleted.iterator(); it.hasNext();) { + Logger.debug(" Deleted: " + it.next()); + } + } + + public void fromChangeSetProto(ChangeSetProto changeSetProto) { + changes.clear(); + deleted.clear(); + List<EntityChangeProto> changesList = changeSetProto.getChangesList(); + for (EntityChangeProto entityChange : changesList) { + EntityID entityID = new EntityID(entityChange.getEntityID()); + int urn = entityChange.getUrn(); + + List<PropertyProto> propertyProtoList = entityChange.getPropertiesList(); + for (PropertyProto propertyProto : propertyProtoList) { + Property prop = MsgProtoBuf.propertyProto2Property(propertyProto); + if (prop != null) { + this.addChange(entityID, urn, prop); + } + } + } + // Add deleted entities + for (Integer entityID : changeSetProto.getDeletesList()) { + this.entityDeleted(new EntityID(entityID)); + } + + } + + public ChangeSetProto toChangeSetProto() { + ChangeSetProto.Builder builder = ChangeSetProto.newBuilder(); + for (Entry<EntityID, Map<Integer, Property>> next : changes.entrySet()) { + EntityID id = next.getKey(); + Collection<Property> props = next.getValue().values(); + // EntityID, URN, number of properties + EntityChangeProto.Builder entityChangeBuilder = EntityChangeProto.newBuilder().setEntityID(id.getValue()) + .setUrn(getEntityURN(id)); + for (Property prop : props) { + entityChangeBuilder.addProperties(prop.toPropertyProto()); + } + builder.addChanges(entityChangeBuilder); + } + for (EntityID next : deleted) { + builder.addDeletes(next.getValue()); + } + return builder.build(); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/DefaultWorldModel.java b/modules/rescuecore2/src/rescuecore2/worldmodel/DefaultWorldModel.java new file mode 100644 index 0000000000000000000000000000000000000000..90db4399650d3bc45e9e808699020d36a137bae2 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/DefaultWorldModel.java @@ -0,0 +1,72 @@ +package rescuecore2.worldmodel; + +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +/** + Default implementation of a WorldModel. + @param <T> The subclass of Entity that this world model holds. +*/ +public class DefaultWorldModel<T extends Entity> extends AbstractWorldModel<T> { + private Map<EntityID, T> entities; + + /** + Construct an empty world model. + @param clazz The class of objects that are allowed in this world model. This approach is a workaround for the limitations of Java generics. + */ + public DefaultWorldModel(Class<? extends T> clazz) { + entities = new HashMap<EntityID, T>(); + registerAllowedClass(clazz); + } + + /** + Construct an empty world model. + @return A new DefaultWorldModel that accepts any type of Entity. + */ + public static DefaultWorldModel<Entity> create() { + return new DefaultWorldModel<Entity>(Entity.class); + } + + @Override + public final Collection<T> getAllEntities() { + return Collections.unmodifiableCollection(entities.values()); + } + + @Override + public final void addEntityImpl(T e) { + entities.put(e.getID(), e); + fireEntityAdded(e); + } + + @Override + public final void removeEntity(EntityID id) { + T removed = entities.remove(id); + if (removed != null) { + fireEntityRemoved(removed); + } + } + + @Override + public final void removeAllEntities() { + Set<T> all = new HashSet<T>(entities.values()); + entities.clear(); + for (T next : all) { + fireEntityRemoved(next); + } + } + + @Override + public final T getEntity(EntityID id) { + return entities.get(id); + } + + @Override + public final Iterator<T> iterator() { + return entities.values().iterator(); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/Entity.java b/modules/rescuecore2/src/rescuecore2/worldmodel/Entity.java new file mode 100644 index 0000000000000000000000000000000000000000..e3be81febaa285803bf686296e8255cd424cca9e --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/Entity.java @@ -0,0 +1,95 @@ +package rescuecore2.worldmodel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Set; + +import org.json.JSONObject; + +import rescuecore2.messages.protobuf.RCRSProto.EntityProto; + +/** + * Interface for all objects that live in a WorldModel. Entities are made up of + * a fixed set of properties. The values of those properties may change but the + * set of properties may not. + */ +public interface Entity { + + /** + * Add an EntityListener. + * + * @param l The listener to add. + */ + void addEntityListener(EntityListener l); + + /** + * Remove an EntityListener. + * + * @param l The listener to remove. + */ + void removeEntityListener(EntityListener l); + + /** + * Get the ID of this Entity. + * + * @return The ID. + */ + EntityID getID(); + + /** + * Get the urn of this Entity. + * + * @return The type. + */ + int getURN(); + + /** + * Get all the properties that this entity has. + * + * @return A set of Properties. This will never be null, but may be empty. + */ + Set<Property> getProperties(); + + /** + * Get a property by urn. + * + * @param urn The urn to look up. + * @return The property with the given urn or null if no such property exists. + */ + Property getProperty(int urn); + + /** + * Write this Entity to a stream. + * + * @param out The stream to write to. + * @throws IOException If the write fails. + */ + void write(OutputStream out) throws IOException; + + /** + * Read this Entity from a stream. + * + * @param in The stream to read from. + * @throws IOException If the read fails. + */ + void read(InputStream in) throws IOException; + + /** + * Create a copy of this entity. + * + * @return A new Entity with the same ID and property values. + */ + Entity copy(); + + /** + * Return the Entity state in JSON format + * + * @return Entity states + */ + JSONObject toJson(); + + EntityProto toEntityProto(); + + void fromEntityProto(EntityProto proto); +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/EntityID.java b/modules/rescuecore2/src/rescuecore2/worldmodel/EntityID.java new file mode 100644 index 0000000000000000000000000000000000000000..6e693542c04c8f30f6e04002d3eb87cc3b7d6add --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/EntityID.java @@ -0,0 +1,42 @@ +package rescuecore2.worldmodel; + +/** + A type-safe ID class for entities. IDs are really just integers. + */ +public final class EntityID { + private final int id; + + /** + Construct a new EntityID object. + @param id The numeric ID to use. + */ + public EntityID(int id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (o instanceof EntityID) { + return this.id == ((EntityID)o).id; + } + return false; + } + + @Override + public int hashCode() { + return id; + } + + /** + Get the numeric ID for this object. + @return The numeric ID. + */ + public int getValue() { + return id; + } + + @Override + public String toString() { + return String.valueOf(id); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/EntityListener.java b/modules/rescuecore2/src/rescuecore2/worldmodel/EntityListener.java new file mode 100644 index 0000000000000000000000000000000000000000..cb83095d1d5f4b6085f459760448681d094e47cd --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/EntityListener.java @@ -0,0 +1,15 @@ +package rescuecore2.worldmodel; + +/** + Interface for objects that are interested in hearing about changes to entities. + */ +public interface EntityListener { + /** + Notification that a property has changed. + @param e The entity that has changed. + @param p The property that has changed. + @param oldValue The old value of the property. This may be null if the property was undefined. + @param newValue The new value of the property. This may be null if the property is now undefined. + */ + void propertyChanged(Entity e, Property p, Object oldValue, Object newValue); +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/Property.java b/modules/rescuecore2/src/rescuecore2/worldmodel/Property.java new file mode 100644 index 0000000000000000000000000000000000000000..d9003540b1132e86379b9b62b3287aba0993aba5 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/Property.java @@ -0,0 +1,67 @@ +package rescuecore2.worldmodel; + +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; + +import java.io.IOException; + +/** + Interface for the properties that make up an entity. + */ +public interface Property { + /** + Get the urn of this property. + @return The urn of this property. + */ + int getURN(); + + /** + Does this property have a defined value? + @return True if a value has been set for this property, false otherwise. + */ + boolean isDefined(); + + /** + Undefine the value of this property. Future calls to {@link #isDefined()} will return false. + */ + void undefine(); + + /** + Take on the value of another property. + @param other The other property to inspect. + @throws IllegalArgumentException If the other property is the wrong type. + */ + void takeValue(Property other); + + /** + Write this property to a stream. + @param out The stream to write to. + @throws IOException If the write fails. + */ + void write(OutputStream out) throws IOException; + + /** + Read this property from a stream. + @param in The stream to read from. + @throws IOException If the read fails. + */ + void read(InputStream in) throws IOException; + + /** + Get the value of this property. If the property is undefined then the return value should be null. + @return The value of this property. + */ + Object getValue(); + + /** + Create a copy of this property. + @return A copy of this property. + */ + Property copy(); + + PropertyProto toPropertyProto(); + void fromPropertyProto(PropertyProto proto); +} + diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModel.java b/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModel.java new file mode 100644 index 0000000000000000000000000000000000000000..e23dd46ef34eb18a70f8d4b10b46fe004e1169a4 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModel.java @@ -0,0 +1,75 @@ +package rescuecore2.worldmodel; + +import java.util.Collection; + +/** + This class encapsulates everything about a world model. The world model can be parameterised on a subclass of Entity if required. + @param <T> The subclass of Entity that this world model holds. +*/ +public interface WorldModel<T extends Entity> extends Iterable<T> { + /** + Add a listener for world model events. + @param l The listener to add. + */ + void addWorldModelListener(WorldModelListener<? super T> l); + + /** + Remove a listener for world model events. + @param l The listener to remove. + */ + void removeWorldModelListener(WorldModelListener<? super T> l); + + /** + Get all entities in the world. + @return An immutable view of all entities in the world. + */ + Collection<T> getAllEntities(); + + /** + Add an entity to the world. Note that this method is not parameterised by T. + @param e The entity to add. + */ + void addEntity(Entity e); + + /** + Add a set of entities to the world. Note that this method is not parameterised by T. + @param e The entities to add. + */ + void addEntities(Collection<? extends Entity> e); + + /** + Remove an entity from the world. + @param e The entity to remove. + */ + void removeEntity(T e); + + /** + Remove an entity from the world. + @param id The entityID to remove. + */ + void removeEntity(EntityID id); + + /** + Remove all entities from the world. + */ + void removeAllEntities(); + + /** + Look up an entity by ID. + @param id The EntityID to look up. + @return The entity with the given ID, or null if no such entity exists. + */ + T getEntity(EntityID id); + + /** + Merge a set of entities into this world. New entities will be added; existing entities will have properties replaced with those taken from the given objects. Any undefined properties in the given objects will NOT cause existing properties to become undefined. Note that this method is not parameterised by T. + @param toMerge The set of entities to merge into this world model. + */ + void merge(Collection<? extends Entity> toMerge); + + /** + Merge a set of changes into this world. If the ChangeSet specifies that a property has become undefined then the existing property WILL become undefined. This behaviour is different to the {@link #merge(Collection)} method because an undefined value inside a ChangeSet means that the property value has become undefined, whereas in the other version of merge an undefined property means there is no value for that property. + @param changeSet The set of changes to merge into this world model. + */ + void merge(ChangeSet changeSet); +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModelListener.java b/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModelListener.java new file mode 100644 index 0000000000000000000000000000000000000000..a2d6eb2af5e15f3199470ce594a9fe73bb725c92 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/WorldModelListener.java @@ -0,0 +1,21 @@ +package rescuecore2.worldmodel; + +/** + Interface for objects that are interested in changes to the world model. + @param <T> The subclass of Entity that this world model listener understands. + */ +public interface WorldModelListener<T extends Entity> { + /** + Notification that an Entity was added to the world. + @param model The WorldModel that was updated. + @param e The entity that was added. + */ + void entityAdded(WorldModel<? extends T> model, T e); + + /** + Notification that an Entity was removed from the world. + @param model The WorldModel that was updated. + @param e The entity that was removed. + */ + void entityRemoved(WorldModel<? extends T> model, T e); +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/BooleanProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/BooleanProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..144e240ad8ec169f32f83d2e1e6804738b239964 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/BooleanProperty.java @@ -0,0 +1,139 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; +import rescuecore2.worldmodel.Property; + +/** + * A boolean property. + */ +public class BooleanProperty extends AbstractProperty { + private boolean value; + + /** + * Construct a BooleanProperty with no defined value. + * + * @param urn The urn of this property. + */ + public BooleanProperty(int urn) { + super(urn); + } + + /** + * Construct a BooleanProperty with no defined value. + * + * @param urn The urn of this property. + */ + public BooleanProperty(URN urn) { + super(urn); + } + + /** + * Construct a BooleanProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public BooleanProperty(int urn, boolean value) { + super(urn, true); + this.value = value; + } + + /** + * Construct a BooleanProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public BooleanProperty(URN urn, boolean value) { + super(urn, true); + this.value = value; + } + + /** + * BooleanProperty copy constructor. + * + * @param other The BooleanProperty to copy. + */ + public BooleanProperty(BooleanProperty other) { + super(other); + this.value = other.value; + } + + @Override + public Boolean getValue() { + if (!isDefined()) { + return null; + } + return value; + } + + /** + * Set the value of this property. Future calls to {@link #isDefined()} will + * return true. + * + * @param value The new value. + */ + public void setValue(boolean value) { + boolean old = this.value; + boolean wasDefined = isDefined(); + this.value = value; + setDefined(); + if (!wasDefined || old != value) { + fireChange(old, value); + } + } + + @Override + public void takeValue(Property p) { + if (p instanceof BooleanProperty) { + BooleanProperty b = (BooleanProperty) p; + if (b.isDefined()) { + setValue(b.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException(this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(value ? 1 : 0, out); + } + + @Override + public void read(InputStream in) throws IOException { + setValue(readInt32(in) != 0); + } + + @Override + public BooleanProperty copy() { + return new BooleanProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder.setBoolValue(value); + } + return builder.build(); + } + + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + setValue(proto.getBoolValue()); + } +} \ No newline at end of file diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/DoubleProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/DoubleProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..34bfdd376a1fd40a881703bd386ebeb8926eb432 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/DoubleProperty.java @@ -0,0 +1,134 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readDouble; +import static rescuecore2.misc.EncodingTools.writeDouble; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import rescuecore2.worldmodel.Property; +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; + +/** + A single double-precision floating point number property. + */ +public class DoubleProperty extends AbstractProperty { + private double value; + + /** + Construct a DoubleProperty with no defined value. + @param urn The urn of this property. + */ + public DoubleProperty(int urn) { + super(urn); + } + + /** + Construct a DoubleProperty with no defined value. + @param urn The urn of this property. + */ + public DoubleProperty(URN urn) { + super(urn); + } + + /** + Construct a DoubleProperty with a defined value. + @param urn The urn of this property. + @param value The initial value of the property. + */ + public DoubleProperty(int urn, double value) { + super(urn, true); + this.value = value; + } + + /** + Construct a DoubleProperty with a defined value. + @param urn The urn of this property. + @param value The initial value of the property. + */ + public DoubleProperty(URN urn, double value) { + super(urn, true); + this.value = value; + } + + /** + DoubleProperty copy constructor. + @param other The DoubleProperty to copy. + */ + public DoubleProperty(DoubleProperty other) { + super(other); + this.value = other.value; + } + + @Override + public Double getValue() { + if (!isDefined()) { + return null; + } + return value; + } + + /** + Set the value of this property. Future calls to {@link #isDefined()} will return true. + @param value The new value. + */ + public void setValue(double value) { + double old = this.value; + boolean wasDefined = isDefined(); + this.value = value; + setDefined(); + if (!wasDefined || old != value) { + fireChange(old, value); + } + } + + @Override + public void takeValue(Property p) { + if (p instanceof DoubleProperty) { + DoubleProperty d = (DoubleProperty)p; + if (d.isDefined()) { + setValue(d.getValue()); + } + else { + undefine(); + } + } + else { + throw new IllegalArgumentException(this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeDouble(value, out); + } + + @Override + public void read(InputStream in) throws IOException { + setValue(readDouble(in)); + } + + @Override + public DoubleProperty copy() { + return new DoubleProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder.setDoubleValue(value); + } + return builder.build(); + } + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + setValue(proto.getDoubleValue()); + } + +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefListProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefListProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..9fba4272f09ce6f0abc143a0e8db1bb72cd37849 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefListProperty.java @@ -0,0 +1,207 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.IntListProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +/** + * A property that refers to a list of entity IDs. + */ +public class EntityRefListProperty extends AbstractProperty { + + private List<EntityID> ids; + + /** + * Construct an EntityRefListProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EntityRefListProperty(int urn) { + super(urn); + ids = new ArrayList<EntityID>(); + } + + /** + * Construct an EntityRefListProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EntityRefListProperty(URN urn) { + super(urn); + ids = new ArrayList<EntityID>(); + } + + /** + * Construct an EntityRefListProperty with a defined value. + * + * @param urn The urn of this property. + * @param ids The initial value of the property. + */ + public EntityRefListProperty(int urn, List<EntityID> ids) { + super(urn, true); + this.ids = new ArrayList<EntityID>(ids); + } + + /** + * Construct an EntityRefListProperty with a defined value. + * + * @param urn The urn of this property. + * @param ids The initial value of the property. + */ + public EntityRefListProperty(URN urn, List<EntityID> ids) { + super(urn, true); + this.ids = new ArrayList<EntityID>(ids); + } + + /** + * EntityRefListProperty copy constructor. + * + * @param other The EntityRefListProperty to copy. + */ + public EntityRefListProperty(EntityRefListProperty other) { + super(other); + this.ids = new ArrayList<EntityID>(other.ids); + } + + @Override + public List<EntityID> getValue() { + if (!isDefined()) { + return null; + } + return Collections.unmodifiableList(ids); + } + + /** + * Set the list of ids. Future calls to {@link #isDefined()} will return + * true. + * + * @param newIDs The new id list. + */ + public void setValue(List<EntityID> newIDs) { + List<EntityID> old = new ArrayList<EntityID>(ids); + ids.clear(); + ids.addAll(newIDs); + setDefined(); + fireChange(old, Collections.unmodifiableList(ids)); + } + + /** + * Add a value to the list. + * + * @param id The id to add. + */ + public void addValue(EntityID id) { + List<EntityID> old = new ArrayList<EntityID>(ids); + ids.add(id); + setDefined(); + fireChange(old, Collections.unmodifiableList(ids)); + } + + /** + * Remove a value from the list. + * + * @param id The id to remove. + */ + public void removeValue(EntityID id) { + List<EntityID> old = new ArrayList<EntityID>(ids); + ids.remove(id); + + if (ids.isEmpty()) + undefine(); + + fireChange(old, Collections.unmodifiableList(ids)); + } + + /** + * Remove all entries from this list but keep it defined. + */ + public void clearValues() { + List<EntityID> old = new ArrayList<EntityID>(ids); + ids.clear(); + fireChange(old, Collections.unmodifiableList(ids)); + } + + @Override + public void takeValue(Property p) { + if (p instanceof EntityRefListProperty) { + EntityRefListProperty e = (EntityRefListProperty) p; + if (e.isDefined()) { + setValue(e.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException( + this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(ids.size(), out); + for (EntityID next : ids) { + writeInt32(next.getValue(), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + int count = readInt32(in); + List<EntityID> newIDs = new ArrayList<EntityID>(count); + for (int i = 0; i < count; ++i) { + newIDs.add(new EntityID(readInt32(in))); + } + setValue(newIDs); + } + + /* + * @Override public String toString() { StringBuilder result = new + * StringBuilder(); result.append(getURN()); if (isDefined()) { + * result.append(" = {"); for (Iterator<EntityID> it = ids.iterator(); + * it.hasNext();) { result.append(it.next()); if (it.hasNext()) { + * result.append(", "); } } result.append("}"); } else { + * result.append(" (undefined)"); } return result.toString(); } + */ + + @Override + public EntityRefListProperty copy() { + return new EntityRefListProperty(this); + } + + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + IntListProto.Builder intListBuilder = IntListProto.newBuilder(); + for (EntityID next : ids) { + intListBuilder.addValues(next.getValue()); + } + builder.setIntList(intListBuilder); + } + return builder.build(); + } + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + List<Integer> values = proto.getIntList().getValuesList(); + List<EntityID> newIDs = new ArrayList<EntityID>(values.size()); + for (Integer val : values) { + newIDs.add(new EntityID(val)); + } + setValue(newIDs); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..c9f91ad653b1b521ad1ef48b20def4c154969c1d --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/EntityRefProperty.java @@ -0,0 +1,141 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + * A property that refers to an entity ID. + */ +public class EntityRefProperty extends AbstractProperty { + private EntityID value; + + /** + * Construct an EntityRefProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EntityRefProperty(int urn) { + super(urn); + } + + /** + * Construct an EntityRefProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EntityRefProperty(URN urn) { + super(urn); + } + + /** + * Construct an EntityRefProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public EntityRefProperty(int urn, EntityID value) { + super(urn, true); + this.value = value; + } + + /** + * Construct an EntityRefProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public EntityRefProperty(URN urn, EntityID value) { + super(urn, true); + this.value = value; + } + + /** + * EntityRefProperty copy constructor. + * + * @param other The EntityRefProperty to copy. + */ + public EntityRefProperty(EntityRefProperty other) { + super(other); + this.value = other.value; + } + + @Override + public EntityID getValue() { + if (!isDefined()) { + return null; + } + return value; + } + + /** + * Set the value of this property. Future calls to {@link #isDefined()} will + * return true. + * + * @param value The new value. + */ + public void setValue(EntityID value) { + EntityID old = this.value; + boolean wasDefined = isDefined(); + this.value = value; + setDefined(); + if (!wasDefined || !old.equals(value)) { + fireChange(old, value); + } + } + + @Override + public void takeValue(Property p) { + if (p instanceof EntityRefProperty) { + EntityRefProperty e = (EntityRefProperty) p; + if (e.isDefined()) { + setValue(e.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException( + this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(value.getValue(), out); + } + + @Override + public void read(InputStream in) throws IOException { + setValue(new EntityID(readInt32(in))); + } + + @Override + public EntityRefProperty copy() { + return new EntityRefProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder.setIntValue(value.getValue()); + } + return builder.build(); + } + + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + setValue(new EntityID(proto.getIntValue())); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntArrayProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntArrayProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..b9b343343641c46125406c70fc6dd806581fd7ad --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntArrayProperty.java @@ -0,0 +1,205 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import rescuecore2.worldmodel.Property; +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.IntListProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; + +/** + * An integer-array property. + */ +public class IntArrayProperty extends AbstractProperty { + /** Implement as a list to allow for growth. */ + private List<Integer> data; + + /** + * Construct an IntArrayProperty with no defined value. + * + * @param urn The urn of this property. + */ + public IntArrayProperty(int urn) { + super(urn); + data = new ArrayList<Integer>(); + } + + /** + * Construct an IntArrayProperty with no defined value. + * + * @param urn The urn of this property. + */ + public IntArrayProperty(URN urn) { + super(urn); + data = new ArrayList<Integer>(); + } + + /** + * Construct an IntArrayProperty with a defined value. + * + * @param urn The urn of this property. + * @param values The initial values of the property. + */ + public IntArrayProperty(int urn, int[] values) { + super(urn, true); + data = new ArrayList<Integer>(values.length); + for (Integer next : values) { + data.add(next); + } + } + + /** + * Construct an IntArrayProperty with a defined value. + * + * @param urn The urn of this property. + * @param values The initial values of the property. + */ + public IntArrayProperty(URN urn, int[] values) { + super(urn, true); + data = new ArrayList<Integer>(values.length); + for (Integer next : values) { + data.add(next); + } + } + + /** + * IntArrayProperty copy constructor. + * + * @param other The IntArrayProperty to copy. + */ + public IntArrayProperty(IntArrayProperty other) { + super(other); + this.data = new ArrayList<Integer>(other.data); + } + + @Override + public int[] getValue() { + if (!isDefined()) { + return null; + } + Integer[] result = new Integer[data.size()]; + data.toArray(result); + int[] out = new int[result.length]; + for (int i = 0; i < out.length; ++i) { + out[i] = result[i].intValue(); + } + return out; + } + + /** + * Set the value of this property. Future calls to {@link #isDefined()} will + * return true. + * + * @param values The new values. + */ + public void setValue(int[] values) { + int[] old = getValue(); + this.data = new ArrayList<Integer>(values.length); + for (Integer next : values) { + data.add(next); + } + setDefined(); + fireChange(old, getValue()); + } + + /** + * Add a value to the array. + * + * @param i The value to add. + */ + public void push(int i) { + int[] old = getValue(); + setDefined(); + data.add(i); + fireChange(old, getValue()); + } + + @Override + public void takeValue(Property p) { + if (p instanceof IntArrayProperty) { + IntArrayProperty i = (IntArrayProperty) p; + if (i.isDefined()) { + setValue(i.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException( + this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(data.size(), out); + for (Integer next : data) { + writeInt32(next.intValue(), out); + } + } + + @Override + public void read(InputStream in) throws IOException { + int size = readInt32(in); + int[] result = new int[size]; + for (int i = 0; i < size; ++i) { + result[i] = readInt32(in); + } + setValue(result); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(getURN()); + if (isDefined()) { + result.append(" = {"); + for (Iterator<Integer> it = data.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + result.append("}"); + } else { + result.append(" (undefined)"); + } + return result.toString(); + } + + @Override + public IntArrayProperty copy() { + return new IntArrayProperty(this); + } + + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder + .setIntList(IntListProto.newBuilder().addAllValues(data)); + } + return builder.build(); + } + + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + List<Integer> list = proto.getIntList().getValuesList(); + int[] result = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + result[i] = list.get(i); + } + setValue(result); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..f933089d78602a09c71035d3553a3d3824928fc3 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/IntProperty.java @@ -0,0 +1,139 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import rescuecore2.worldmodel.Property; +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; + +/** + * A single integer property. + */ +public class IntProperty extends AbstractProperty { + private int value; + + /** + * Construct an IntProperty with no defined value. + * + * @param urn The urn of this property. + */ + public IntProperty(int urn) { + super(urn); + } + + /** + * Construct an IntProperty with no defined value. + * + * @param urn The urn of this property. + */ + public IntProperty(URN urn) { + super(urn); + } + + /** + * Construct an IntProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public IntProperty(int urn, int value) { + super(urn, true); + this.value = value; + } + + /** + * Construct an IntProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public IntProperty(URN urn, int value) { + super(urn, true); + this.value = value; + } + + /** + * IntProperty copy constructor. + * + * @param other The IntProperty to copy. + */ + public IntProperty(IntProperty other) { + super(other); + this.value = other.value; + } + + @Override + public Integer getValue() { + if (!isDefined()) { + return null; + } + return value; + } + + /** + * Set the value of this property. Future calls to {@link #isDefined()} will + * return true. + * + * @param value The new value. + */ + public void setValue(int value) { + int old = this.value; + boolean wasDefined = isDefined(); + this.value = value; + setDefined(); + if (!wasDefined || old != value) { + fireChange(old, value); + } + } + + @Override + public void takeValue(Property p) { + if (p instanceof IntProperty) { + IntProperty i = (IntProperty) p; + if (i.isDefined()) { + setValue(i.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException( + this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(value, out); + } + + @Override + public void read(InputStream in) throws IOException { + setValue(readInt32(in)); + } + + @Override + public IntProperty copy() { + return new IntProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder.setIntValue(value); + } + return builder.build(); + } + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + setValue(proto.getIntValue()); + } +} diff --git a/modules/rescuecore2/src/rescuecore2/worldmodel/properties/Point2DProperty.java b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/Point2DProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..47bce63cc5e3779c8218689382ca402da6609339 --- /dev/null +++ b/modules/rescuecore2/src/rescuecore2/worldmodel/properties/Point2DProperty.java @@ -0,0 +1,143 @@ +package rescuecore2.worldmodel.properties; + +import static rescuecore2.misc.EncodingTools.readDouble; +import static rescuecore2.misc.EncodingTools.writeDouble; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.AbstractProperty; +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.Point2DProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.misc.geometry.Point2D; + +/** + * A Point2D property. + */ +public class Point2DProperty extends AbstractProperty { + private Point2D value; + + /** + * Construct a Point2DProperty with no defined value. + * + * @param urn The urn of this property. + */ + public Point2DProperty(int urn) { + super(urn); + } + + /** + * Construct a Point2DProperty with no defined value. + * + * @param urn The urn of this property. + */ + public Point2DProperty(URN urn) { + super(urn); + } + + /** + * Construct a Point2DProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public Point2DProperty(int urn, Point2D value) { + super(urn, true); + this.value = value; + } + + /** + * Construct a Point2DProperty with a defined value. + * + * @param urn The urn of this property. + * @param value The initial value of the property. + */ + public Point2DProperty(URN urn, Point2D value) { + super(urn, true); + this.value = value; + } + + /** + * Point2DProperty copy constructor. + * + * @param other The Point2DProperty to copy. + */ + public Point2DProperty(Point2DProperty other) { + super(other); + this.value = other.value; + } + + @Override + public Point2D getValue() { + if (!isDefined()) { + return null; + } + return value; + } + + /** + * Set the value of this property. Future calls to {@link #isDefined()} will + * return true. + * + * @param value The new value. + */ + public void setValue(Point2D value) { + this.value = value; + setDefined(); + } + + @Override + public void takeValue(Property p) { + if (p instanceof Point2DProperty) { + Point2DProperty i = (Point2DProperty) p; + if (i.isDefined()) { + setValue(i.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException( + this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeDouble(value.getX(), out); + writeDouble(value.getY(), out); + } + + @Override + public void read(InputStream in) throws IOException { + double x = readDouble(in); + double y = readDouble(in); + setValue(new Point2D(x, y)); + } + + @Override + public Point2DProperty copy() { + return new Point2DProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + builder.setPoint2D(Point2DProto.newBuilder().setX(value.getX()) + .setY(value.getY())); + } + return builder.build(); + } + + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + + Point2DProto point = proto.getPoint2D(); + setValue(new Point2D(point.getX(), point.getY())); + } +} diff --git a/modules/resq-fire/src/firesimulator/FireSimulatorWrapper.java b/modules/resq-fire/src/firesimulator/FireSimulatorWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..65176f7b8452c518922e9b43fea9c49062964c5d --- /dev/null +++ b/modules/resq-fire/src/firesimulator/FireSimulatorWrapper.java @@ -0,0 +1,331 @@ +package firesimulator; + +import rescuecore2.config.NoSuchConfigOptionException; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KSUpdate; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKExtinguish; +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; + +import firesimulator.world.Hydrant; +import firesimulator.world.World; +import firesimulator.world.WorldInfo; +import firesimulator.world.Refuge; +import firesimulator.world.FireStation; +import firesimulator.world.PoliceOffice; +import firesimulator.world.AmbulanceCenter; +import firesimulator.world.Building; +import firesimulator.world.Civilian; +import firesimulator.world.FireBrigade; +import firesimulator.world.PoliceForce; +import firesimulator.world.AmbulanceTeam; +import firesimulator.world.RescueObject; +import firesimulator.world.MovingObject; +import firesimulator.simulator.Simulator; +import firesimulator.simulator.ExtinguishRequest; +import firesimulator.util.Configuration; + +import java.util.Collection; +import firesimulator.gui.*; +import javax.swing.JComponent; +import rescuecore2.GUIComponent; + +/** + A rescuecore2 Simulator that wraps the ResQ Freiburg fire simulator. + */ +public class FireSimulatorWrapper extends StandardSimulator implements GUIComponent { + private static final String MAX_WATER_KEY = "fire.tank.maximum"; + + private Simulator sim; + private World world; + private FireSimulatorGUI fireSimulatorGUI = null; + + @Override + public JComponent getGUIComponent() { + if(fireSimulatorGUI == null) { + fireSimulatorGUI = new FireSimulatorGUI(sim, world); + } + return fireSimulatorGUI; + } + + @Override + public String getGUIComponentName() { + return "Fire simulator"; + } + + @Override + protected void postConnect() { + super.postConnect(); + Configuration c = new Configuration(); + c.initialize(); + for (String next : c.getPropertyNames()) { + try { + String value = config.getValue(next); + Configuration.setProperty(next, value, true); + Logger.debug("Setting '" + next + "' to '" + value + "'"); + } + catch (NoSuchConfigOptionException e) { + // Ignore + Logger.debug("Ignoring property " + next); + } + } + world = new World(); + sim = new Simulator(world); + // Map each entity to a fire simulator object + for (Entity next : model) { + RescueObject r = mapEntity(next); + if (r != null) { + world.putObject(r); + } + } + sim.initialize(); + + + + } + + @Override + protected void handleUpdate(KSUpdate u) { + super.handleUpdate(u); + // Merge objects + for (EntityID id : u.getChangeSet().getChangedEntities()) { + Entity e = model.getEntity(id); + RescueObject r = world.getObject(id.getValue()); + if (r == null) { + r = mapEntity(e); + if (r != null) { + world.putObject(r); + } + } + else { + if (r instanceof Building && e instanceof rescuecore2.standard.entities.Building) { + Building b = (Building)r; + mapBuildingProperties((rescuecore2.standard.entities.Building)e, b); + // Check for new ignitions + if (b.getIgnition() == 1 && b.isInflameable()) { + int fieryness = b.getFieryness(); + // CHECKSTYLE:OFF:MagicNumber + if (fieryness == 0 || fieryness == 4) { + // CHECKSTYLE:ON:MagicNumber + Logger.debug("Igniting " + b); + b.ignite(); + } + } + } + else if (r instanceof MovingObject && e instanceof rescuecore2.standard.entities.Human) { + mapHumanProperties((rescuecore2.standard.entities.Human)e, (MovingObject)r); + }else if(r instanceof Hydrant){ + }else { + Logger.error("Don't know how to map " + r + " from " + e); + } + } + } + } + + @Override + protected void processCommands(KSCommands c, ChangeSet changes) { + long start = System.currentTimeMillis(); + for (Command next : c.getCommands()) { + if (next instanceof AKExtinguish) { + AKExtinguish ex = (AKExtinguish)next; + EntityID agentID = ex.getAgentID(); + EntityID targetID = ex.getTarget(); + int water = ex.getWater(); + FireBrigade source = (FireBrigade)world.getObject(agentID.getValue()); + Building target = (Building)world.getObject(targetID.getValue()); + ExtinguishRequest req = new ExtinguishRequest(source, target, water); + world.addExtinguishRequest(req); + } + } + sim.step(c.getTime()); + // Get changes + for (Object next : world.getBuildings()) { + Building b = (Building)next; + rescuecore2.standard.entities.Building oldB = (rescuecore2.standard.entities.Building)model.getEntity(new EntityID(b.getID())); + if ((!oldB.isFierynessDefined()) || (oldB.getFieryness() != b.getFieryness())) { + oldB.setFieryness(b.getFieryness()); + changes.addChange(oldB, oldB.getFierynessProperty()); + } + if ((!oldB.isTemperatureDefined()) || (oldB.getTemperature() != (int)b.getTemperature())) { + oldB.setTemperature((int)b.getTemperature()); + changes.addChange(oldB, oldB.getTemperatureProperty()); + } + } + for (Object next : world.getFirebrigades()) { + FireBrigade fb = (FireBrigade)next; + // Logger.debug("Updating water for " + fb); + // Logger.debug(fb.hasChanged() ? "Changed" : "Unchanged"); + // if (fb.hasChanged()) { + rescuecore2.standard.entities.FireBrigade oldFB = (rescuecore2.standard.entities.FireBrigade)model.getEntity(new EntityID(fb.getID())); + // Logger.debug("Old water: " + oldFB.getWaterProperty()); + // Logger.debug("New water: " + fb.getWaterQuantity()); + if ((!oldFB.isWaterDefined()) || (oldFB.getWater() != fb.getWaterQuantity())) { + oldFB.setWater(fb.getWaterQuantity()); + changes.addChange(oldFB, oldFB.getWaterProperty()); + } + // } + } + if (c.getTime() == 1) { + // Set initial water quantity for all fire brigades + for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)) { + rescuecore2.standard.entities.FireBrigade fb = (rescuecore2.standard.entities.FireBrigade)next; + fb.setWater(config.getIntValue(MAX_WATER_KEY)); + changes.addChange(fb, fb.getWaterProperty()); + } + } + long end = System.currentTimeMillis(); + Logger.info("Time " + c.getTime() + " took " + (end - start) + "ms"); + + if(fireSimulatorGUI != null) { + fireSimulatorGUI.refresh(); + } + + } + + private RescueObject mapEntity(Entity e) { + int id = e.getID().getValue(); + if (e instanceof rescuecore2.standard.entities.World) { + return new WorldInfo(id); + } + if (e instanceof rescuecore2.standard.entities.Refuge) { + Refuge r = new Refuge(id); + mapBuildingProperties((rescuecore2.standard.entities.Building)e, r); + return r; + } + if (e instanceof rescuecore2.standard.entities.Hydrant) { + Hydrant r = new Hydrant(id); + return r; + } + if (e instanceof rescuecore2.standard.entities.FireStation) { + FireStation fs = new FireStation(id); + mapBuildingProperties((rescuecore2.standard.entities.Building)e, fs); + return fs; + } + if (e instanceof rescuecore2.standard.entities.PoliceOffice) { + PoliceOffice po = new PoliceOffice(id); + mapBuildingProperties((rescuecore2.standard.entities.Building)e, po); + return po; + } + if (e instanceof rescuecore2.standard.entities.AmbulanceCentre) { + AmbulanceCenter ac = new AmbulanceCenter(id); + mapBuildingProperties((rescuecore2.standard.entities.Building)e, ac); + return ac; + } + if (e instanceof rescuecore2.standard.entities.Building) { + Building b = new Building(id); + mapBuildingProperties((rescuecore2.standard.entities.Building)e, b); + return b; + } + if (e instanceof rescuecore2.standard.entities.Civilian) { + Civilian c = new Civilian(id); + mapHumanProperties((rescuecore2.standard.entities.Civilian)e, c); + return c; + } + if (e instanceof rescuecore2.standard.entities.FireBrigade) { + FireBrigade fb = new FireBrigade(id); + mapHumanProperties((rescuecore2.standard.entities.FireBrigade)e, fb); + return fb; + } + if (e instanceof rescuecore2.standard.entities.PoliceForce) { + PoliceForce pf = new PoliceForce(id); + mapHumanProperties((rescuecore2.standard.entities.PoliceForce)e, pf); + return pf; + } + if (e instanceof rescuecore2.standard.entities.AmbulanceTeam) { + AmbulanceTeam at = new AmbulanceTeam(id); + mapHumanProperties((rescuecore2.standard.entities.AmbulanceTeam)e, at); + return at; + } + if (e instanceof rescuecore2.standard.entities.Road) { + return null; + } + if (e instanceof rescuecore2.standard.entities.Blockade) { + return null; + } + Logger.error("Don't know how to map this: " + e); + return null; + } + + private void mapBuildingProperties(rescuecore2.standard.entities.Building oldB, Building newB) { + if (oldB.isFloorsDefined()) { + newB.setFloors(oldB.getFloors()); + } + if (oldB.isBuildingAttributesDefined()) { + newB.setAttributes(oldB.getBuildingAttributes()); + } + if (oldB.isIgnitionDefined()) { + newB.setIgnition(oldB.getIgnition() ? 1 : 0); + } + if (oldB.isFierynessDefined()) { + newB.setFieryness(oldB.getFieryness()); + } + if (oldB.isBrokennessDefined()) { + newB.setBrokenness(oldB.getBrokenness()); + } + if (oldB.isBuildingCodeDefined()) { + newB.setCode(oldB.getBuildingCode()); + } + if (oldB.isGroundAreaDefined()) { + newB.setBuildingAreaGround(oldB.getGroundArea()); + } + if (oldB.isTotalAreaDefined()) { + newB.setBuildingAreaTotal(oldB.getTotalArea()); + } + if (oldB.isEdgesDefined()) { + newB.setApexes(oldB.getApexList()); + } + if (oldB.isXDefined()) { + newB.setX(oldB.getX()); + } + if (oldB.isYDefined()) { + newB.setY(oldB.getY()); + } + } + + private void mapHumanProperties(rescuecore2.standard.entities.Human oldH, MovingObject newH) { + if (oldH.isStaminaDefined()) { + newH.setStamina(oldH.getStamina()); + } + if (oldH.isHPDefined()) { + newH.setHp(oldH.getHP()); + } + if (oldH.isDamageDefined()) { + newH.setDamage(oldH.getDamage()); + } + if (oldH.isBuriednessDefined()) { + newH.setBuriedness(oldH.getBuriedness()); + } + if (oldH.isPositionDefined()) { + newH.setPositionId(oldH.getPosition().getValue()); + } + if (oldH.isXDefined()) { + newH.setX(oldH.getX()); + } + if (oldH.isYDefined()) { + newH.setY(oldH.getY()); + } + if (oldH instanceof rescuecore2.standard.entities.FireBrigade && newH instanceof FireBrigade) { + rescuecore2.standard.entities.FireBrigade oldFB = (rescuecore2.standard.entities.FireBrigade)oldH; + FireBrigade newFB = (FireBrigade)newH; + if (oldFB.isWaterDefined()) { + newFB.setInitialWaterQuantity(oldFB.getWater()); + } + } + } + + private int[] collectionToIDArray(Collection<EntityID> list) { + int[] ids = new int[list.size()]; + int i = 0; + for (EntityID next : list) { + ids[i++] = next.getValue(); + } + return ids; + } +} diff --git a/modules/resq-fire/src/firesimulator/gui/FireSimulatorGUI.java b/modules/resq-fire/src/firesimulator/gui/FireSimulatorGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..53b6605263d2b131e024a1a15d12adbeb369c0a0 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/FireSimulatorGUI.java @@ -0,0 +1,190 @@ +package firesimulator.gui; + +import firesimulator.simulator.Simulator; +import firesimulator.world.World; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashMap; +import javax.swing.BoxLayout; +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.text.StyleConstants; +import firesimulator.world.Building; +import java.awt.Polygon; +import java.util.Iterator; +import rescuecore2.misc.gui.PanZoomListener; +import firesimulator.gui.layers.*; +import javax.swing.SwingUtilities; +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class FireSimulatorGUI extends JPanel { + + private ScreenTransformExt transform = null; + private ArrayList<JCheckBox> layerCheckBoxes = null; + private JTextArea stringDataTextArea = null; + private Simulator simulator = null; + private World world = null; + private Building selectedBuilding = null; + private HashMap<String, JPanel> tabPanels = new HashMap<>(); + private JTabbedPane tabbedPane = new JTabbedPane(); + private DrawingPanel drawingPanel = null; + private int mouseX = 0; + private int mouseY = 0; + + private final ItemListener layerCheckBoxItemListener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + refresh(); + } + }; + + public final static String TAB_ALL = "All"; + + private void addLayers() { + addLayer(TAB_ALL, Buildings.class, true); + addLayer(TAB_ALL, BuildingInfo.class, true); + addLayer(TAB_ALL, Fieryness.class, true); + addLayer(TAB_ALL, AllConnectionsWeighted.class, false); + addLayer(TAB_ALL, AllConnections.class, false); + addLayer(TAB_ALL, AirCells.class, true); + addLayer(TAB_ALL, BuildingAirCells.class, true); + addLayer(TAB_ALL, ConnectedBuildings.class, false); + addLayer(TAB_ALL, ConnectedBuildingsWeighted.class, true); + } + + public void addLayer(String tabName, Class c, boolean de) { + String packageName = c.getPackage().getName(); + String className = c.getName().replace(packageName + ".", ""); + GUILayerFactory.getInstance().addLayer(className, c); + JPanel panel = tabPanels.get(tabName); + if (panel == null) { + panel = new JPanel(); + BoxLayout bxl = new BoxLayout(panel, BoxLayout.Y_AXIS); + panel.setLayout(bxl); + tabbedPane.addTab(tabName, panel); + tabPanels.put(tabName, panel); + } + JCheckBox chb = new JCheckBox(className, de); + chb.addItemListener(layerCheckBoxItemListener); + panel.add(chb); + layerCheckBoxes.add(chb); + } + + public FireSimulatorGUI(Simulator simulator, World world) { + this.simulator = simulator; + this.world = world; + this.drawingPanel = new DrawingPanel(); + this.layerCheckBoxes = new ArrayList<>(); + this.stringDataTextArea = new JTextArea(); + + JScrollPane layersScrollPane = new JScrollPane(tabbedPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + layersScrollPane.setPreferredSize(new Dimension(300, 300)); + JScrollPane stringDataScrollPane = new JScrollPane(stringDataTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + stringDataScrollPane.setPreferredSize(new Dimension(300, 300)); + + this.setLayout(new BorderLayout()); + this.add(layersScrollPane, BorderLayout.WEST); + this.add(drawingPanel, BorderLayout.CENTER); + this.add(stringDataScrollPane, BorderLayout.EAST); + + addLayers(); + + } + + public void refresh() { + repaint(); + try { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + updateStringData(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + class DrawingPanel extends JPanel { + + private final int DEFAULT_WIDTH = 480; + private final int DEFAULT_HEIGHT = 480; + + public DrawingPanel() { + setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); + transform = new ScreenTransformExt(world.getMinX(), world.getMinY(), world.getMaxX(), world.getMaxY()); + this.setLayout(new FlowLayout(StyleConstants.ALIGN_LEFT)); + new PanZoomListener(DrawingPanel.this).setScreenTransform(transform); + this.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + mouseX = (int) transform.screenToX((int) (e.getX())); + mouseY = (int) transform.screenToY((int) (e.getY())); + selectedBuilding = null; + for (Iterator i = world.getBuildings().iterator(); i.hasNext();) { + Building b = (Building) i.next(); + Polygon polygon = new Polygon(); + int apexes[] = b.getApexes(); + for (int n = 0; n < apexes.length; n++) { + int x_ = apexes[n]; + int y_ = apexes[++n]; + polygon.addPoint(x_, y_); + } + if (polygon.contains(mouseX, mouseY)) { + selectedBuilding = b; + } + } + refresh(); + } + }); + } + + @Override + public void paintComponent(Graphics g) { + transform.rescale(getWidth(), getHeight()); + Graphics2D g2 = (Graphics2D) g; + g.setColor(new Color(170, 170, 170)); + g.fillRect(0, 0, getWidth(), getHeight()); + PaintEvent paintEvent = new PaintEvent(g2, transform, simulator, world, selectedBuilding, mouseX, mouseY); + for (JCheckBox chb : layerCheckBoxes) { + if (chb.isSelected()) { + GUILayerFactory.getInstance().getLayer(chb.getText()).paint(paintEvent); + } + } + } + } + + private void updateStringData() { + String str = ""; + for (JCheckBox chb : layerCheckBoxes) { + if (chb.isSelected()) { + PaintEvent paintEvent = new PaintEvent(null, null, simulator, world, selectedBuilding, mouseX, mouseY); + String s = GUILayerFactory.getInstance().getLayer(chb.getText()).getString(paintEvent); + if (s != null) { + str += chb.getText() + ": "; + str += "\n"; + str += s; + str += "\n"; + str += "\n"; + } + } + } + stringDataTextArea.setText(str); + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/GUILayer.java b/modules/resq-fire/src/firesimulator/gui/GUILayer.java new file mode 100644 index 0000000000000000000000000000000000000000..a83acf69117046f5dae5f027312d60a5dd094ad5 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/GUILayer.java @@ -0,0 +1,16 @@ +package firesimulator.gui; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public abstract class GUILayer { + + abstract public void paint(PaintEvent paintEvent); + + public String getString(PaintEvent paintEvent) { + return null; + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/GUILayerFactory.java b/modules/resq-fire/src/firesimulator/gui/GUILayerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..dc345b0171c5af2c72271eb83cc24298326102de --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/GUILayerFactory.java @@ -0,0 +1,34 @@ +package firesimulator.gui; + +import java.util.HashMap; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class GUILayerFactory { + + private static GUILayerFactory _instance = null; + + private HashMap<String, GUILayer> layers = new HashMap<String, GUILayer>(); + + public static GUILayerFactory getInstance() { + if (_instance == null) { + _instance = new GUILayerFactory(); + } + return _instance; + } + + public void addLayer(String name, Class c) { + try { + layers.put(name, (GUILayer) c.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public GUILayer getLayer(String name) { + return layers.get(name); + } +} \ No newline at end of file diff --git a/modules/resq-fire/src/firesimulator/gui/PaintEvent.java b/modules/resq-fire/src/firesimulator/gui/PaintEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8f894e3f9259704283ee7443c8580906fcc96192 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/PaintEvent.java @@ -0,0 +1,61 @@ +package firesimulator.gui; + +import firesimulator.simulator.Simulator; +import firesimulator.world.Building; +import firesimulator.world.World; +import java.awt.Graphics2D; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class PaintEvent { + + private Graphics2D graphics2D = null; + private ScreenTransformExt transform = null; + private Simulator simulator = null; + private World world = null; + private Building selectedBuilding = null; + private int mouseX = 0; + private int mouseY = 0; + + public PaintEvent(Graphics2D graphics2D, ScreenTransformExt transform, Simulator simulator, World world, Building selectedBuilding, int mouseX, int mouseY) { + this.graphics2D = graphics2D; + this.transform = transform; + this.simulator = simulator; + this.world = world; + this.selectedBuilding = selectedBuilding; + this.mouseX = mouseX; + this.mouseY = mouseY; + } + + public Graphics2D getGraphics2D() { + return graphics2D; + } + + public ScreenTransformExt getTransform() { + return transform; + } + + public Simulator getSimulator() { + return simulator; + } + + public World getWorld() { + return world; + } + + public Building getSelectedBuilding() { + return selectedBuilding; + } + + public int getMouseX() { + return mouseX; + } + + public int getMouseY() { + return mouseY; + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/ScreenTransformExt.java b/modules/resq-fire/src/firesimulator/gui/ScreenTransformExt.java new file mode 100644 index 0000000000000000000000000000000000000000..c13781f982d93c6241ae2fd13ba7a5ac327a21c2 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/ScreenTransformExt.java @@ -0,0 +1,66 @@ +package firesimulator.gui; + +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Shape; +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; +import rescuecore2.misc.gui.ScreenTransform; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class ScreenTransformExt extends ScreenTransform { + + public ScreenTransformExt(double minX, double minY, double maxX, double maxY) { + super(minX, minY, maxX, maxY); + } + + public Polygon getTransformedPolygon(Shape shape) { + Polygon polygon = (Polygon) shape; + Polygon result = new Polygon(); + for(int i = 0; i < polygon.npoints; i++) { + result.addPoint(this.xToScreen(polygon.xpoints[i]), this.yToScreen(polygon.ypoints[i])); + } + return result; + } + + public Rectangle2D getTransformedRectangle(Rectangle2D rect) { + return new Rectangle( + xToScreen(rect.getMinX()), + yToScreen(rect.getMinY() + rect.getHeight()), + xToScreen(rect.getWidth()) - xToScreen(0), + yToScreen(0) - yToScreen(rect.getHeight()) + ); + } + + public Rectangle2D getTransformedRectangle(double x0, double y0, double w, double h) { + return new Rectangle( + xToScreen(x0), + yToScreen(y0 + h), + xToScreen(w) - xToScreen(0), + yToScreen(0) - yToScreen(h) + ); + } + + public void drawTransformedLine(Graphics2D g2, double x0, double y0, double x1, double y1) { + g2.drawLine( + this.xToScreen(x0), + this.yToScreen(y0), + this.xToScreen(x1), + this.yToScreen(y1) + ); + } + + public void fillTransformedOvalFixedRadius(Graphics2D g2, double x0, double y0, double r) { + g2.fillOval( + (int) (this.xToScreen(x0) - r), + (int) (this.yToScreen(y0) - r), + (int) (2 * r), + (int) (2 * r) + ); + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/AirCells.java b/modules/resq-fire/src/firesimulator/gui/layers/AirCells.java new file mode 100644 index 0000000000000000000000000000000000000000..dee874e8032e854766f7675f88aa1c11e81c9d15 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/AirCells.java @@ -0,0 +1,62 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.Rectangle2D; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class AirCells extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + double cells[][] = paintEvent.getWorld().getAirTemp(); + int a = paintEvent.getWorld().SAMPLE_SIZE; + int mx = paintEvent.getWorld().getMinX(); + int my = paintEvent.getWorld().getMinY(); + Color lightYellow = new Color(255, 255, 0, 15); + paintEvent.getGraphics2D().setStroke(new BasicStroke(1)); + paintEvent.getGraphics2D().setColor(lightYellow); + for(int x = 0; x <= cells.length; x++) { + paintEvent.getTransform().drawTransformedLine(paintEvent.getGraphics2D(), x * a + mx, my, x * a + mx, (cells[0].length) * a + my); + } + for(int y = 0; y <= cells[0].length; y++) { + paintEvent.getTransform().drawTransformedLine(paintEvent.getGraphics2D(), mx, y * a + my, (cells.length) * a + mx, y * a + my); + } + for(int x = 0; x < cells.length; x++) { + for(int y = 0; y < cells[0].length; y++) { + if(cells[x][y] > 0) { + paintEvent.getGraphics2D().setColor(new Color(255, 0, 0, Math.min(255, (int) (cells[x][y] / 1500 * 255)))); + Rectangle2D rect = paintEvent.getTransform().getTransformedRectangle(x * a + mx, y * a + my, a, a); + paintEvent.getGraphics2D().fill(rect); + } + } + } + int x = (paintEvent.getMouseX() - mx) / a; + int y = (paintEvent.getMouseY() - my) / a; + if(x >= 0 && x < cells.length && y >= 0 && y < cells[0].length) { + Rectangle2D rect = paintEvent.getTransform().getTransformedRectangle(x * a + mx, y * a + my, a, a); + paintEvent.getGraphics2D().setColor(Color.YELLOW); + paintEvent.getGraphics2D().draw(rect); + } + } + + @Override + public String getString(PaintEvent paintEvent) { + int a = paintEvent.getWorld().SAMPLE_SIZE; + int mx = paintEvent.getWorld().getMinX(); + int my = paintEvent.getWorld().getMinY(); + double cells[][] = paintEvent.getWorld().getAirTemp(); + int x = (paintEvent.getMouseX() - mx) / a; + int y = (paintEvent.getMouseY() - my) / a; + if(x >= 0 && x < cells.length && y >= 0 && y < cells[0].length) { + return "Selected Air Cell Temperature: " + cells[x][y]; + } + return null; + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/AllConnections.java b/modules/resq-fire/src/firesimulator/gui/layers/AllConnections.java new file mode 100644 index 0000000000000000000000000000000000000000..7ed66b540d4466bc8a45d6332eee33805027111a --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/AllConnections.java @@ -0,0 +1,30 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.BasicStroke; +import java.awt.Color; +import java.util.Iterator; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class AllConnections extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + paintEvent.getGraphics2D().setColor(Color.yellow); + paintEvent.getGraphics2D().setStroke(new BasicStroke(1)); + for (Iterator i = paintEvent.getWorld().getBuildings().iterator(); i.hasNext();) { + Building b = (Building) i.next(); + Building[] bs = b.connectedBuilding; + float[] vs = b.connectedValues; + for (int c = 0; c < vs.length; c++) { + paintEvent.getTransform().drawTransformedLine(paintEvent.getGraphics2D(), b.getX(), b.getY(), bs[c].getX(), bs[c].getY()); + } + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/AllConnectionsWeighted.java b/modules/resq-fire/src/firesimulator/gui/layers/AllConnectionsWeighted.java new file mode 100644 index 0000000000000000000000000000000000000000..e85c8247bc40a4607aa6c2e072f60ca0837ee8bf --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/AllConnectionsWeighted.java @@ -0,0 +1,32 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.BasicStroke; +import java.awt.Color; +import java.util.Iterator; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class AllConnectionsWeighted extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + paintEvent.getGraphics2D().setStroke(new BasicStroke(1)); + for (Iterator i = paintEvent.getWorld().getBuildings().iterator(); i.hasNext();) { + Building b = (Building) i.next(); + Building[] bs = b.connectedBuilding; + float[] vs = b.connectedValues; + for (int c = 0; c < vs.length; c++) { + double w = vs[c]; + w = Math.pow(w, 0.4); + paintEvent.getGraphics2D().setColor(new Color(249, 129, 42, (int) (w * 255))); + paintEvent.getTransform().drawTransformedLine(paintEvent.getGraphics2D(), b.getX(), b.getY(), bs[c].getX(), bs[c].getY()); + } + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/BuildingAirCells.java b/modules/resq-fire/src/firesimulator/gui/layers/BuildingAirCells.java new file mode 100644 index 0000000000000000000000000000000000000000..55973d412bb2de4261727ed0ed1750c8da8adfb4 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/BuildingAirCells.java @@ -0,0 +1,35 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.geom.Rectangle2D; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class BuildingAirCells extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + Building selectedBuilding = paintEvent.getSelectedBuilding(); + if(selectedBuilding == null) { + return; + } + paintEvent.getGraphics2D().setStroke(new BasicStroke(1)); + int a = paintEvent.getWorld().SAMPLE_SIZE; + int mx = paintEvent.getWorld().getMinX(); + int my = paintEvent.getWorld().getMinY(); + for(int[] cell : selectedBuilding.cells) { + paintEvent.getGraphics2D().setColor(new Color(0, 255, 255, (int) ((float) cell[2] / 100 * 200))); + Rectangle2D rect = paintEvent.getTransform().getTransformedRectangle(cell[0] * a + mx, cell[1] * a + my, a, a); + paintEvent.getGraphics2D().fill(rect); + paintEvent.getGraphics2D().setColor(Color.CYAN); + paintEvent.getGraphics2D().draw(rect); + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/BuildingInfo.java b/modules/resq-fire/src/firesimulator/gui/layers/BuildingInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..1b15bdb953ff8c6330c8bb45ce82c6d339468e07 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/BuildingInfo.java @@ -0,0 +1,61 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class BuildingInfo extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + } + + @Override + public String getString(PaintEvent paintEvent) { + Building b = paintEvent.getSelectedBuilding(); + if(b == null) { + return null; + } + String result = ""; + + result += "ID:\t\t" + b.getID(); + result += "\n"; + + result += "Temperature:\t\t" + b.getTemperature(); + result += "\n"; + + result += "Energy:\t\t" + b.getEnergy(); + result += "\n"; + + result += "Fuel:\t\t" + b.getFuel(); + result += "\n"; + + result += "Water Quantity:\t" + b.getWaterQuantity(); + result += "\n"; + + result += "Was Ever Watered:\t" + b.wasEverWatered(); + result += "\n"; + + result += "Fieryness:\t\t" + b.getFieryness(); + result += "\n"; + + result += "Floors:\t\t" + b.getFloors(); + result += "\n"; + + result += "Code:\t\t" + b.getCode(); + result += "\n"; + + + result += "X:\t\t" + b.getX(); + result += "\n"; + + result += "Y:\t\t" + b.getY(); + result += "\n"; + + return result; + } +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/Buildings.java b/modules/resq-fire/src/firesimulator/gui/layers/Buildings.java new file mode 100644 index 0000000000000000000000000000000000000000..7599df454ec501589160dc4af40f6da17ea65cb9 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/Buildings.java @@ -0,0 +1,35 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.Color; +import java.awt.Polygon; +import java.util.Iterator; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class Buildings extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + for (Iterator i = paintEvent.getWorld().getBuildings().iterator(); i.hasNext();) { + Building b = (Building) i.next(); + Polygon polygon = new Polygon(); + int apexes[] = b.getApexes(); + for (int n = 0; n < apexes.length; n++) { + int x = apexes[n]; + int y = apexes[++n]; + polygon.addPoint(x, y); + } + polygon = paintEvent.getTransform().getTransformedPolygon(polygon); + paintEvent.getGraphics2D().setColor(Color.gray); + paintEvent.getGraphics2D().fillPolygon(polygon); + paintEvent.getGraphics2D().setColor(Color.darkGray); + paintEvent.getGraphics2D().drawPolygon(polygon); + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildings.java b/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildings.java new file mode 100644 index 0000000000000000000000000000000000000000..e2acbf3ca07edb1913a9331f651e60371334340c --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildings.java @@ -0,0 +1,36 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class ConnectedBuildings extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + Building selectedBuilding = paintEvent.getSelectedBuilding(); + if(selectedBuilding == null) { + return; + } + paintEvent.getGraphics2D().setColor(Color.yellow); + paintEvent.getGraphics2D().setStroke(new BasicStroke(3)); + Building[] bs = selectedBuilding.connectedBuilding; + float[] vs = selectedBuilding.connectedValues; + for (int c = 0; c < vs.length; c++) { + paintEvent.getTransform().drawTransformedLine( + paintEvent.getGraphics2D(), + selectedBuilding.getX(), + selectedBuilding.getY(), + bs[c].getX(), + bs[c].getY() + ); + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildingsWeighted.java b/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildingsWeighted.java new file mode 100644 index 0000000000000000000000000000000000000000..fd55c3fbac706ab2fb1bfa093d1918f4da06e854 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/ConnectedBuildingsWeighted.java @@ -0,0 +1,38 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.BasicStroke; +import java.awt.Color; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class ConnectedBuildingsWeighted extends GUILayer { + + @Override + public void paint(PaintEvent paintEvent) { + Building selectedBuilding = paintEvent.getSelectedBuilding(); + if(selectedBuilding == null) { + return; + } + paintEvent.getGraphics2D().setStroke(new BasicStroke(3)); + Building[] bs = selectedBuilding.connectedBuilding; + float[] vs = selectedBuilding.connectedValues; + for (int c = 0; c < vs.length; c++) { + double w = vs[c]; + w = Math.pow(w, 0.4); + paintEvent.getGraphics2D().setColor(new Color(249, 129, 42, (int) (w * 255))); + paintEvent.getTransform().drawTransformedLine( + paintEvent.getGraphics2D(), + selectedBuilding.getX(), + selectedBuilding.getY(), + bs[c].getX(), + bs[c].getY() + ); + } + } + +} diff --git a/modules/resq-fire/src/firesimulator/gui/layers/Fieryness.java b/modules/resq-fire/src/firesimulator/gui/layers/Fieryness.java new file mode 100644 index 0000000000000000000000000000000000000000..abbc7689ec873fb16ffcd980b18a7c0e843c5e73 --- /dev/null +++ b/modules/resq-fire/src/firesimulator/gui/layers/Fieryness.java @@ -0,0 +1,47 @@ +package firesimulator.gui.layers; + +import firesimulator.gui.*; +import firesimulator.world.Building; +import java.awt.Color; +import java.awt.Polygon; +import java.util.Iterator; + +/** + * + * Created by Alireza Kandeh on March 2018 + */ + +public class Fieryness extends GUILayer { + + private final static Color COLORS[] = new Color[] { + new Color(176, 176, 56, 128), // HEATING + new Color(204, 122, 50, 128), // BURNING + new Color(160, 52, 52, 128), // INFERNO + new Color(50, 120, 130, 128), // WATER_DAMAGE + new Color(100, 140, 210, 128), // MINOR_DAMAGE + new Color(100, 70, 190, 128), // MODERATE_DAMAGE + new Color(80, 60, 140, 128), // SEVERE_DAMAGE + new Color(0, 0, 0, 255) // BURNT_OUT + }; + + @Override + public void paint(PaintEvent paintEvent) { + for (Iterator i = paintEvent.getWorld().getBuildings().iterator(); i.hasNext();) { + Building b = (Building) i.next(); + int f = b.getFieryness(); + if(f > 0) { + Polygon polygon = new Polygon(); + int apexes[] = b.getApexes(); + for (int n = 0; n < apexes.length; n++) { + int x = apexes[n]; + int y = apexes[++n]; + polygon.addPoint(x, y); + } + polygon = paintEvent.getTransform().getTransformedPolygon(polygon); + paintEvent.getGraphics2D().setColor(COLORS[f -1]); + paintEvent.getGraphics2D().fillPolygon(polygon); + } + } + } + +} diff --git a/modules/sample/src/sample/AbstractSampleAgent.java b/modules/sample/src/sample/AbstractSampleAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..96b1acfb0e6ca44e550a5f6ae9762cdff71a1ef0 --- /dev/null +++ b/modules/sample/src/sample/AbstractSampleAgent.java @@ -0,0 +1,133 @@ +package sample; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.log4j.Logger; +import rescuecore2.Constants; +import rescuecore2.standard.components.StandardAgent; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.kernel.comms.ChannelCommunicationModel; +import rescuecore2.standard.kernel.comms.StandardCommunicationModel; +import rescuecore2.worldmodel.EntityID; + +/** + * Abstract base class for sample agents. + * + * @param <E> + * The subclass of StandardEntity this agent wants to control. + */ +public abstract class AbstractSampleAgent<E extends StandardEntity> + extends StandardAgent<E> { + + private static final int RANDOM_WALK_LENGTH = 50; + + private static final String SAY_COMMUNICATION_MODEL = StandardCommunicationModel.class + .getName(); + private static final String SPEAK_COMMUNICATION_MODEL = ChannelCommunicationModel.class + .getName(); + private static final Logger LOG = Logger + .getLogger( AbstractSampleAgent.class ); + + /** + * The search algorithm. + */ + protected SampleSearch search; + + /** + * Whether to use AKSpeak messages or not. + */ + protected boolean useSpeak; + + /** + * Cache of building IDs. + */ + protected List<EntityID> buildingIDs; + + /** + * Cache of road IDs. + */ + protected List<EntityID> roadIDs; + + /** + * Cache of refuge IDs. + */ + protected List<EntityID> refugeIDs; + + private Map<EntityID, Set<EntityID>> neighbours; + + + /** + * Construct an AbstractSampleAgent. + */ + protected AbstractSampleAgent() { + } + + + @Override + protected void postConnect() { + super.postConnect(); + buildingIDs = new ArrayList<EntityID>(); + roadIDs = new ArrayList<EntityID>(); + refugeIDs = new ArrayList<EntityID>(); + for ( StandardEntity next : model ) { + if ( next instanceof Building ) { + buildingIDs.add( next.getID() ); + } + if ( next instanceof Road ) { + roadIDs.add( next.getID() ); + } + if ( next instanceof Refuge ) { + refugeIDs.add( next.getID() ); + } + } + search = new SampleSearch( model ); + neighbours = search.getGraph(); + useSpeak = config.getValue( Constants.COMMUNICATION_MODEL_KEY ) + .equals( SPEAK_COMMUNICATION_MODEL ); + LOG.debug( "Communcation model: " + + config.getValue( Constants.COMMUNICATION_MODEL_KEY ) ); + LOG.debug( useSpeak ? "Using speak model" : "Using say model" ); + } + + + /** + * Construct a random walk starting from this agent's current location to a + * random building. + * + * @return A random walk. + */ + protected List<EntityID> randomWalk() { + List<EntityID> result = new ArrayList<EntityID>( RANDOM_WALK_LENGTH ); + Set<EntityID> seen = new HashSet<EntityID>(); + EntityID current = ( (Human) me() ).getPosition(); + for ( int i = 0; i < RANDOM_WALK_LENGTH; ++i ) { + result.add( current ); + seen.add( current ); + List<EntityID> possible = new ArrayList<EntityID>( + neighbours.get( current ) ); + Collections.shuffle( possible, random ); + boolean found = false; + for ( EntityID next : possible ) { + if ( seen.contains( next ) ) { + continue; + } + current = next; + found = true; + break; + } + if ( !found ) { + // We reached a dead-end. + break; + } + } + return result; + } +} diff --git a/modules/sample/src/sample/DistanceSorter.java b/modules/sample/src/sample/DistanceSorter.java new file mode 100644 index 0000000000000000000000000000000000000000..72323c10622830fa87c243ebbb8064ecbe1d0ef8 --- /dev/null +++ b/modules/sample/src/sample/DistanceSorter.java @@ -0,0 +1,31 @@ +package sample; + +import java.util.Comparator; + +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardWorldModel; + +/** + A comparator that sorts entities by distance to a reference point. +*/ +public class DistanceSorter implements Comparator<StandardEntity> { + private StandardEntity reference; + private StandardWorldModel world; + + /** + Create a DistanceSorter. + @param reference The reference point to measure distances from. + @param world The world model. + */ + public DistanceSorter(StandardEntity reference, StandardWorldModel world) { + this.reference = reference; + this.world = world; + } + + @Override + public int compare(StandardEntity a, StandardEntity b) { + int d1 = world.getDistance(reference, a); + int d2 = world.getDistance(reference, b); + return d1 - d2; + } +} diff --git a/modules/sample/src/sample/LiveLogExtractor.java b/modules/sample/src/sample/LiveLogExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..f585d40bebaeacc63265c6de6985c19c8e611f90 --- /dev/null +++ b/modules/sample/src/sample/LiveLogExtractor.java @@ -0,0 +1,259 @@ +package sample; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.Transparency; +import java.awt.image.BufferedImage; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.NumberFormat; +import java.util.List; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import rescuecore2.Timestep; +import rescuecore2.Constants; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.score.ScoreFunction; +import rescuecore2.standard.components.StandardViewer; +import rescuecore2.standard.view.AnimatedWorldModelViewer; +import rescuecore2.view.RenderedObject; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; + +/** + * A simple viewer. + */ +public class LiveLogExtractor extends StandardViewer { + + private static final int DEFAULT_FONT_SIZE = 20; + private static final int PRECISION = 3; + + private static final String FONT_SIZE_KEY = "viewer.font-size"; + private static final String MAXIMISE_KEY = "viewer.maximise"; + private static final String TEAM_NAME_KEY = "viewer.team-name"; + private static final String OUTDIR_KEY = "viewer.output-dir"; + + private ScoreFunction scoreFunction; + private ViewComponent viewer; + private ViewComponent imageViewer; + private JLabel timeLabel; + private JLabel scoreLabel; + private JLabel teamLabel; + private JLabel mapLabel; + private NumberFormat format; + + private String outdir; + + + @Override + protected void postConnect() { + super.postConnect(); + int fontSize = config.getIntValue( FONT_SIZE_KEY, DEFAULT_FONT_SIZE ); + String teamName = config.getValue( TEAM_NAME_KEY, "" ); + outdir = config.getValue( OUTDIR_KEY, "." ); + scoreFunction = makeScoreFunction(); + format = NumberFormat.getInstance(); + format.setMaximumFractionDigits( PRECISION ); + JFrame frame = new JFrame( "Viewer " + getViewerID() + " (" + + model.getAllEntities().size() + " entities)" ); + viewer = new AnimatedWorldModelViewer(); + viewer.initialise( config ); + viewer.view( model ); + imageViewer = new AnimatedWorldModelViewer(); + imageViewer.initialise( config ); + imageViewer.view( model ); + viewer.setPreferredSize( new Dimension( 500, 500 ) ); + imageViewer.setBounds( 0, 0, 1024, 786 ); + timeLabel = new JLabel( "Time: Not started", JLabel.CENTER ); + teamLabel = new JLabel( teamName, JLabel.CENTER ); + scoreLabel = new JLabel( "Score: Unknown", JLabel.CENTER ); + String mapdir = config.getValue( "gis.map.dir" ).trim(); + + String[] map_spl = mapdir.split( "/" ); + int index = map_spl.length - 1; + String mapname = map_spl[index].trim(); + if ( mapname.equals( "" ) ) mapname = map_spl[--index].trim(); + if ( mapname.equals( "map" ) ) mapname = map_spl[--index].trim(); + + String totalTime = config.getValue( "kernel.timesteps" ); + int channelCount = config.getIntValue( "comms.channels.count" ) - 1;// -1 + // for + // say + + mapLabel = new JLabel( + mapname + " (" + totalTime + ") | " + + ( channelCount == 0 ? "No Comm" : channelCount + " channels" ), + JLabel.CENTER ); + timeLabel.setBackground( Color.WHITE ); + timeLabel.setOpaque( true ); + timeLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + teamLabel.setBackground( Color.WHITE ); + teamLabel.setOpaque( true ); + teamLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + scoreLabel.setBackground( Color.WHITE ); + scoreLabel.setOpaque( true ); + scoreLabel + .setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + + mapLabel.setBackground( Color.WHITE ); + mapLabel.setOpaque( true ); + mapLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + + frame.add( viewer, BorderLayout.CENTER ); + JPanel labels = new JPanel( new GridLayout( 1, 4 ) ); + labels.add( teamLabel ); + labels.add( timeLabel ); + labels.add( scoreLabel ); + labels.add( mapLabel ); + frame.add( labels, BorderLayout.NORTH ); + frame.pack(); + if ( config.getBooleanValue( MAXIMISE_KEY, false ) ) { + frame.setExtendedState( JFrame.MAXIMIZED_BOTH ); + } + frame.setVisible( true ); + + double score = scoreFunction.score( model, new Timestep( 0 ) ); + writeFile( outdir + "/init-score.txt", String.valueOf( score ) ); + writeFile( outdir + "/scores.txt", String.valueOf( score ) ); + scoreLabel.setText( "Score: " + format.format( score ) ); + + viewer.addViewListener( new ViewListener() { + + @Override + public void objectsClicked( ViewComponent view, + List<RenderedObject> objects ) { + for ( RenderedObject next : objects ) { + System.out.println( next.getObject() ); + } + } + + + @Override + public void objectsRollover( ViewComponent view, + List<RenderedObject> objects ) { + } + } ); + } + + + @Override + protected void handleTimestep( final KVTimestep t ) { + super.handleTimestep( t ); + SwingUtilities.invokeLater( new Runnable() { + + public void run() { + timeLabel.setText( "Time: " + t.getTime() ); + double score = scoreFunction.score( model, + new Timestep( t.getTime() ) ); + scoreLabel.setText( "Score: " + format.format( score ) ); + viewer.view( model, t.getCommands() ); + viewer.repaint(); + + if ( t.getTime() == 1 ) { + writeImage( outdir + "/snapshot-init.png" ); + } else if ( t.getTime() % 50 == 0 ) { + writeImage( outdir + "/snapshot-" + t.getTime() + ".png" ); + } + appendFile( outdir + "/scores.txt", " " + String.valueOf( score ) ); + if ( t.getTime() == Integer + .parseInt( config.getValue( "kernel.timesteps" ) ) ) { + writeImage( outdir + "/snapshot-final.png" ); + writeFile( outdir + "/final-score.txt", String.valueOf( score ) ); + try { + Thread.sleep( 100000 ); + } catch ( Exception e ) { + e.printStackTrace(); + } + System.exit( 0 ); + } + + } + } ); + } + + + @Override + public String toString() { + return "Sample viewer"; + } + + + private static void writeFile( String filename, String content ) { + try { + PrintWriter out = new PrintWriter( + new BufferedWriter( new FileWriter( filename ) ) ); + out.print( content ); + out.close(); + } catch ( IOException e ) { + System.out.println( "Error writing file: " + e.getMessage() ); + } + + } + + + private static void appendFile( String filename, String content ) { + try { + PrintWriter out = new PrintWriter( + new BufferedWriter( new FileWriter( filename, true ) ) ); + out.print( content ); + out.close(); + } catch ( IOException e ) { + System.out.println( "Error writing file: " + e.getMessage() ); + } + + } + + + public void writeImage( String filename ) { + BufferedImage bi = paintImage(); + File outfile = new File( filename ); + try { + ImageIO.write( bi, "png", outfile ); + } catch ( IOException e ) { + System.out.println( "Error writing image: " + e.getMessage() ); + } + } + + + public BufferedImage paintImage() { + imageViewer.view( model, null, null ); + // Create the image + GraphicsConfiguration configuration = GraphicsEnvironment + .getLocalGraphicsEnvironment().getDefaultScreenDevice() + .getDefaultConfiguration(); + BufferedImage image = configuration.createCompatibleImage( + imageViewer.getWidth(), imageViewer.getHeight(), + Transparency.TRANSLUCENT ); + + // Render the component onto the image + Graphics graphics = image.createGraphics(); + imageViewer.paint( graphics ); + graphics.dispose(); + return image; + } + + + private ScoreFunction makeScoreFunction() { + String className = config.getValue( Constants.SCORE_FUNCTION_KEY ); + ScoreFunction result = instantiate( className, ScoreFunction.class ); + result.initialise( model, config ); + return result; + } +} \ No newline at end of file diff --git a/modules/sample/src/sample/SampleCivilian.java b/modules/sample/src/sample/SampleCivilian.java new file mode 100644 index 0000000000000000000000000000000000000000..3368c633fc17350fd904b99cf985a9f6ba9dc990 --- /dev/null +++ b/modules/sample/src/sample/SampleCivilian.java @@ -0,0 +1,194 @@ +package sample; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; + +import rescuecore2.messages.Command; +import rescuecore2.registry.FilterEntityFactory; +import rescuecore2.registry.FilterPropertyFactory; +import rescuecore2.registry.Registry; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntityFactory; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardPropertyFactory; +import rescuecore2.standard.entities.StandardPropertyURN; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.EntityID; + +/** + * A sample civilian agent. + */ +public class SampleCivilian extends AbstractSampleAgent<Civilian> { + + private static final Logger LOG = Logger.getLogger(SampleCivilian.class); + private static final double DEFAULT_HELP_PROBABILITY = 0.1; + private static final double DEFAULT_OUCH_PROBABILITY = 0.1; + private static final int DEFAULT_CONSCIOUS_THRESHOLD = 2500; + + private static final String HELP_PROBABILITY_KEY = "civilian.help.probability"; + private static final String OUCH_PROBABILITY_KEY = "civilian.ouch.probability"; + private static final String CONSCIOUS_THRESHOLD_KEY = "civilian.conscious.threshold"; + + private static final String OUCH = "Ouch"; + private static final String HELP = "Help"; + + private double helpProbability; + private double ouchProbability; + private int consciousThreshold; + + @Override + public String toString() { + return "Sample civilian"; + } + + @Override + protected void postConnect() { + super.postConnect(); + // model.indexClass(StandardEntityURN.REFUGE); + helpProbability = config.getFloatValue(HELP_PROBABILITY_KEY, DEFAULT_HELP_PROBABILITY); + ouchProbability = config.getFloatValue(OUCH_PROBABILITY_KEY, DEFAULT_OUCH_PROBABILITY); + consciousThreshold = config.getIntValue(CONSCIOUS_THRESHOLD_KEY, DEFAULT_CONSCIOUS_THRESHOLD); + LOG.info("Civilian " + getID() + " connected"); + Civilian me = me(); + // Remove all entities except me + model.removeAllEntities(); + model.addEntity(me); + } + + @Override + public Registry getPreferredRegistry(Registry parent) { + // Return a registry that filters out buildings and civilians + Registry result = new Registry("SampleCivilian filter registry", super.getPreferredRegistry(parent)); + Set<Integer> entityURNs = new HashSet<>(); + entityURNs.add(StandardEntityURN.BUILDING.getURNId()); + entityURNs.add(StandardEntityURN.REFUGE.getURNId()); + entityURNs.add(StandardEntityURN.HYDRANT.getURNId()); + entityURNs.add(StandardEntityURN.GAS_STATION.getURNId()); + entityURNs.add(StandardEntityURN.ROAD.getURNId()); + entityURNs.add(StandardEntityURN.CIVILIAN.getURNId()); + Set<Integer> propertyURNs = new HashSet<>(); + propertyURNs.add(StandardPropertyURN.X.getURNId()); + propertyURNs.add(StandardPropertyURN.Y.getURNId()); + propertyURNs.add(StandardPropertyURN.EDGES.getURNId()); + propertyURNs.add(StandardPropertyURN.DAMAGE.getURNId()); + propertyURNs.add(StandardPropertyURN.BURIEDNESS.getURNId()); + propertyURNs.add(StandardPropertyURN.HP.getURNId()); + propertyURNs.add(StandardPropertyURN.POSITION.getURNId()); + result.registerFactory(new FilterEntityFactory(StandardEntityFactory.INSTANCE, entityURNs, true)); + result.registerFactory(new FilterPropertyFactory(StandardPropertyFactory.INSTANCE, propertyURNs, true)); + return result; + } + + @Override + protected void think(int time, ChangeSet changed, Collection<Command> heard) { + // If we're not hurt or buried run for a refuge! + + Civilian me = me(); + // Remove all entities except me + model.removeAllEntities(); + model.addEntity(me); + int damage = me.isDamageDefined() ? me.getDamage() : 0; + int hp = me.isHPDefined() ? me.getHP() : 0; + int buriedness = me.isBuriednessDefined() ? me.getBuriedness() : 0; + if (hp <= 0 || hp < consciousThreshold) { + // Unconscious (or dead): do nothing + LOG.info("Unconscious or dead"); + sendRest(time); + return; + } + if (damage > 0 && random.nextDouble() < ouchProbability) { + LOG.info("Shouting in pain"); + say(OUCH, time); + } + if (buriedness > 0 && random.nextDouble() < helpProbability) { + LOG.info("Calling for help"); + say(HELP, time); + } + + if (damage == 0 && buriedness == 0) { + // Run for the refuge + List<EntityID> path = search.breadthFirstSearchForCivilian(me().getPosition(), refugeIDs); + if (path != null) { + LOG.info("Heading for a refuge"); + sendMove(time, path); + return; + } else { + LOG.info("Moving to road"); + if (model.getEntity(me().getPosition()) instanceof Road) + sendRest(time); + else + sendMove(time, nearestRoad()); + return; + } + } + LOG.info("Not moving: damage = " + damage + ", buriedness = " + buriedness); + sendRest(time); + } + + protected List<EntityID> nearestRoad() { + int maxPathLength = 20; + List<EntityID> result = new ArrayList<EntityID>(maxPathLength); + Set<EntityID> seen = new HashSet<EntityID>(); + EntityID current = ((Human) me()).getPosition(); + + for (int i = 0; i < maxPathLength; ++i) { + result.add(current); + seen.add(current); + Area area = (Area) model.getEntity(current); + if (area instanceof Road) + break; + if (area == null) { + System.err.println("My position=" + current + " is null??? " + me()); + break; + } + List<EntityID> possible = new ArrayList<EntityID>(area.getNeighbours()); + Collections.shuffle(possible, random); + boolean found = false; + for (EntityID next : possible) { + if (seen.contains(next)) { + continue; + } + current = next; + found = true; + break; + } + if (!found) { + // We reached a dead-end. + break; + } + } + return result; + } + + @Override + protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() { + return EnumSet.of(StandardEntityURN.CIVILIAN); + } + + @Override + protected boolean shouldIndex() { + return false; + } + + private void say(String message, int time) { + try { + if (useSpeak) { + sendSpeak(time, 0, message.getBytes("UTF-8")); + } else { + sendSay(time, message.getBytes("UTF-8")); + } + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("This should not have happened!", e); + } + } +} \ No newline at end of file diff --git a/modules/sample/src/sample/SampleLogViewer.java b/modules/sample/src/sample/SampleLogViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..cd4d6dac5c7c014cfb9661137f0e671074b8618b --- /dev/null +++ b/modules/sample/src/sample/SampleLogViewer.java @@ -0,0 +1,325 @@ +package sample; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JToggleButton; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Logger; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.log.CommandsRecord; +import rescuecore2.log.LogException; +import rescuecore2.log.LogReader; +import rescuecore2.log.RCRSLogFactory; +import rescuecore2.log.UpdatesRecord; +import rescuecore2.messages.Command; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.gui.ListModelList; +import rescuecore2.misc.java.LoadableTypeProcessor; +import rescuecore2.registry.Registry; +import rescuecore2.standard.view.StandardEntityInspector; +import rescuecore2.view.RenderedObject; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +/** + * A class for viewing log files. + */ +public class SampleLogViewer extends JPanel { + + private static final Logger LOG = Logger.getLogger(SampleLogViewer.class); + private static final String VIEWERS_KEY = "log.viewers"; + + private static final int TICK_STEP_SIZE = 10; + + private static final int VIEWER_SIZE = 500; + private static final int FRAME_DELAY = 1000; + + private LogReader log; + private JLabel timestep; + private StandardEntityInspector inspector; + private JSlider slider; + private JList commandsList; + private JList updatesList; + private ListModelList<Command> commands; + private ListModelList<Entity> updates; + private List<ViewComponent> viewers; + private JButton down; + private JButton up; + private int maxTime; + private EntityID selectedEntityId; + + /** + * Construct a LogViewer. + * + * @param reader The LogReader to read. + * @param config The system configuration. + * @throws LogException If there is a problem reading the log. + */ + public SampleLogViewer(LogReader reader, Config config) + throws LogException { + super(new BorderLayout()); + this.log = reader; + inspector = new StandardEntityInspector(); + registerViewers(config); + maxTime = log.getMaxTimestep(); + slider = new JSlider(0, maxTime); + down = new JButton("<-"); + up = new JButton("->"); + slider.setSnapToTicks(true); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinorTickSpacing(1); + Dictionary<Integer, JComponent> labels = new Hashtable<Integer, JComponent>(); + labels.put(maxTime, new JLabel(String.valueOf(maxTime))); + for (int i = 0; i < maxTime; i += TICK_STEP_SIZE) { + labels.put(i, new JLabel(String.valueOf(i))); + } + slider.setLabelTable(labels); + slider.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + showTimestep(slider.getValue()); + } + }); + down.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + int value = slider.getValue() - 1; + if (value >= 0) { + slider.setValue(value); + } + } + }); + up.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + int value = slider.getValue() + 1; + if (value <= maxTime) { + slider.setValue(value); + } + } + }); + final JToggleButton play = new JToggleButton(">>"); + play.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + Thread t = new Thread(new Runnable() { + + @Override + public void run() { + while (play.isSelected()) { + int value = slider.getValue() + 1; + if (value <= maxTime) { + slider.setValue(value); + } else { + play.setSelected(false); + } + try { + Thread.sleep(FRAME_DELAY); + } catch (InterruptedException e1) { + LOG.warn("Player interrupted", e1); + } + } + } + }); + t.start(); + } + }); + JPanel lists = new JPanel(new GridLayout(0, 1)); + commands = new ListModelList<Command>(); + commandsList = new JList(commands); + updates = new ListModelList<Entity>(); + updatesList = new JList(updates); + JScrollPane s = new JScrollPane(commandsList); + s.setBorder(BorderFactory.createTitledBorder("Commands")); + s.setPreferredSize(commandsList.getPreferredScrollableViewportSize()); + lists.add(s); + s = new JScrollPane(updatesList); + s.setBorder(BorderFactory.createTitledBorder("Updates")); + s.setPreferredSize(updatesList.getPreferredScrollableViewportSize()); + lists.add(s); + timestep = new JLabel("Timestep: 0"); + JTabbedPane tabs = new JTabbedPane(); + for (ViewComponent next : viewers) { + tabs.addTab(next.getViewerName(), next); + next.addViewListener(new ViewListener() { + + @Override + public void objectsClicked(ViewComponent view, + List<RenderedObject> objects) { + for (RenderedObject next : objects) { + if (next.getObject() instanceof Entity) { + selectedEntityId = ((Entity) next.getObject()) + .getID(); + inspector.inspect((Entity) next.getObject()); + return; + } + } + } + + @Override + public void objectsRollover(ViewComponent view, + List<RenderedObject> objects) { + } + }); + } + JSplitPane split1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + inspector, tabs); + JSplitPane split2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, split1, + lists); + add(split2, BorderLayout.CENTER); + Box bottom = Box.createHorizontalBox(); + bottom.add(down); + bottom.add(slider); + bottom.add(up); + bottom.add(play); + add(bottom, BorderLayout.SOUTH); + add(timestep, BorderLayout.NORTH); + slider.setValue(0); + } + + /** + * Show a particular timestep in the viewer. + * + * @param time The timestep to show. If this value is out of range then this + * method will silently return. + */ + public void showTimestep(int time) { + try { + if (time < 0 || time > maxTime) { + return; + } + timestep.setText("Timestep: " + time); + commands.clear(); + updates.clear(); + CommandsRecord commandsRecord = log.getCommands(time); + if (commandsRecord != null) { + commands.addAll(commandsRecord.getCommands()); + } + UpdatesRecord updatesRecord = log.getUpdates(time); + WorldModel<? extends Entity> model = log.getWorldModel(time); + if (updatesRecord != null) { + for (EntityID eid : updatesRecord.getChangeSet() + .getChangedEntities()) { + updates.add(model.getEntity(eid)); + } + } + inspector.inspect(model.getEntity(selectedEntityId)); + for (ViewComponent next : viewers) { + next.view(model, + commandsRecord == null ? null + : commandsRecord.getCommands(), + updatesRecord == null ? null + : updatesRecord.getChangeSet()); + next.repaint(); + } + down.setEnabled(time != 0); + up.setEnabled(time != maxTime); + } catch (LogException e) { + JOptionPane.showMessageDialog(this, e, "Error", + JOptionPane.ERROR_MESSAGE); + } + } + + private void registerViewers(Config config) { + viewers = new ArrayList<ViewComponent>(); + for (String next : config.getArrayValue(VIEWERS_KEY, "")) { + ViewComponent viewer = instantiate(next, ViewComponent.class); + if (viewer != null) { + viewer.initialise(config); + viewers.add(viewer); + } + } + } + + /** + * Launch a new LogViewer. + * + * @param args Command line arguments. Accepts only one argument: the name + * of a log file. + */ + public static void main(String[] args) { + System.out.println("ali"); + BasicConfigurator.configure(); + + Config config = new Config(); + try { + args = CommandLineOptions.processArgs(args, config); + if (args.length != 1) { + printUsage(); + return; + } + String name = args[0]; + processJarFiles(config); + LogReader reader = RCRSLogFactory.getLogReader(name, + Registry.SYSTEM_REGISTRY); + SampleLogViewer viewer = new SampleLogViewer(reader, config); + viewer.setPreferredSize(new Dimension(VIEWER_SIZE, VIEWER_SIZE)); + JFrame frame = new JFrame("Log viewer: " + name); + frame.getContentPane().add(viewer, BorderLayout.CENTER); + frame.pack(); + frame.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + frame.setVisible(true); + } catch (IOException e) { + LOG.error("Error reading log", e); + } catch (ConfigException e) { + LOG.error("Configuration error", e); + } catch (LogException e) { + LOG.error("Error reading log", e); + } + } + + private static void printUsage() { + System.out.println("Usage: LogViewer <filename>"); + } + + private static void processJarFiles(Config config) throws IOException { + LoadableTypeProcessor processor = new LoadableTypeProcessor(config); + processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY); + processor.process(); + } +} diff --git a/modules/sample/src/sample/SampleSearch.java b/modules/sample/src/sample/SampleSearch.java new file mode 100644 index 0000000000000000000000000000000000000000..0d534a44efd07c8f8dd48598e632e2a86a45317e --- /dev/null +++ b/modules/sample/src/sample/SampleSearch.java @@ -0,0 +1,224 @@ +package sample; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.Area; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * A sample search class that uses a connection graph to look up neighbours. + */ +public final class SampleSearch { + + private Map<EntityID, Set<EntityID>> graph; + private Set<EntityID> buildingSet; + + + /** + * Construct a new SampleSearch. + * + * @param world + * The world model to construct the neighbourhood graph from. + */ + public SampleSearch( StandardWorldModel world ) { + Map<EntityID, Set<EntityID>> neighbours = new LazyMap<EntityID, Set<EntityID>>() { + + @Override + public Set<EntityID> createValue() { + return new HashSet<EntityID>(); + } + }; + buildingSet = new HashSet<EntityID>(); + for ( Entity next : world ) { + if ( next instanceof Area ) { + Collection<EntityID> areaNeighbours = ( (Area) next ).getNeighbours(); + neighbours.get( next.getID() ).addAll( areaNeighbours ); + if ( next instanceof Building ) buildingSet.add( next.getID() ); + } + } + setGraph( neighbours ); + } + + + /** + * Construct a new ConnectionGraphSearch. + * + * @param graph + * The connection graph in the form of a map from EntityID to the set + * of neighbouring EntityIDs. + */ + public SampleSearch( Map<EntityID, Set<EntityID>> graph ) { + setGraph( graph ); + } + + + /** + * Set the neighbourhood graph. + * + * @param newGraph + * The new neighbourhood graph. + */ + public void setGraph( Map<EntityID, Set<EntityID>> newGraph ) { + this.graph = newGraph; + } + + + /** + * Get the neighbourhood graph. + * + * @return The neighbourhood graph. + */ + public Map<EntityID, Set<EntityID>> getGraph() { + return graph; + } + + + /** + * Do a breadth first search from one location to the closest (in terms of + * number of nodes) of a set of goals. + * + * @param start + * The location we start at. + * @param goals + * The set of possible goals. + * @return The path from start to one of the goals, or null if no path can be + * found. + */ + public List<EntityID> breadthFirstSearch( EntityID start, + EntityID... goals ) { + return breadthFirstSearch( start, Arrays.asList( goals ) ); + } + + + /** + * Do a breadth first search from one location to the closest (in terms of + * number of nodes) of a set of goals. + * + * @param start + * The location we start at. + * @param goals + * The set of possible goals. + * @return The path from start to one of the goals, or null if no path can be + * found. + */ + public List<EntityID> breadthFirstSearch( EntityID start, + Collection<EntityID> goals ) { + List<EntityID> open = new LinkedList<EntityID>(); + Map<EntityID, EntityID> ancestors = new HashMap<EntityID, EntityID>(); + open.add( start ); + EntityID next = null; + boolean found = false; + ancestors.put( start, start ); + do { + next = open.remove( 0 ); + if ( isGoal( next, goals ) ) { + found = true; + break; + } + Collection<EntityID> neighbours = graph.get( next ); + if ( neighbours.isEmpty() ) { + continue; + } + for ( EntityID neighbour : neighbours ) { + if ( isGoal( neighbour, goals ) ) { + ancestors.put( neighbour, next ); + next = neighbour; + found = true; + break; + } else { + if ( !ancestors.containsKey( neighbour ) ) { + open.add( neighbour ); + ancestors.put( neighbour, next ); + } + } + } + } while ( !found && !open.isEmpty() ); + if ( !found ) { + // No path + return null; + } + // Walk back from goal to start + EntityID current = next; + List<EntityID> path = new LinkedList<EntityID>(); + do { + path.add( 0, current ); + current = ancestors.get( current ); + if ( current == null ) { + throw new RuntimeException( + "Found a node with no ancestor! Something is broken." ); + } + } while ( current != start ); + return path; + } + + + public List<EntityID> breadthFirstSearchForCivilian( EntityID start, + Collection<EntityID> goals ) { + + List<EntityID> open = new LinkedList<EntityID>(); + Map<EntityID, EntityID> ancestors = new HashMap<EntityID, EntityID>(); + open.add( start ); + EntityID next = null; + boolean found = false; + ancestors.put( start, start ); + do { + next = open.remove( 0 ); + if ( isGoal( next, goals ) ) { + found = true; + break; + } + Collection<EntityID> neighbours = graph.get( next ); + if ( neighbours.isEmpty() ) { + continue; + } + for ( EntityID neighbour : neighbours ) { + if ( isGoal( neighbour, goals ) ) { + ancestors.put( neighbour, next ); + next = neighbour; + found = true; + break; + } else { + if ( !ancestors.containsKey( neighbour ) ) { + if ( buildingSet.contains( next ) + || !buildingSet.contains( neighbour ) ) { + open.add( neighbour ); + ancestors.put( neighbour, next ); + } + } + } + } + } while ( !found && !open.isEmpty() ); + if ( !found ) { + // No path + return null; + } + // Walk back from goal to start + EntityID current = next; + List<EntityID> path = new LinkedList<EntityID>(); + do { + path.add( 0, current ); + current = ancestors.get( current ); + if ( current == null ) { + throw new RuntimeException( + "Found a node with no ancestor! Something is broken." ); + } + } while ( current != start ); + return path; + } + + + private boolean isGoal( EntityID e, Collection<EntityID> test ) { + return test.contains( e ); + } +} diff --git a/modules/sample/src/sample/SampleViewer.java b/modules/sample/src/sample/SampleViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..66493b894871b3812cd77cbcaf4e9eeb6ec6c2e9 --- /dev/null +++ b/modules/sample/src/sample/SampleViewer.java @@ -0,0 +1,162 @@ +package sample; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridLayout; +import java.text.NumberFormat; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import rescuecore2.Constants; +import rescuecore2.Timestep; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.score.ScoreFunction; +import rescuecore2.standard.components.StandardViewer; +import rescuecore2.standard.view.AnimatedWorldModelViewer; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; +import rescuecore2.view.RenderedObject; + +/** + * A simple viewer. + */ +public class SampleViewer extends StandardViewer { + + private static final int DEFAULT_FONT_SIZE = 20; + private static final int PRECISION = 3; + + private static final String FONT_SIZE_KEY = "viewer.font-size"; + private static final String MAXIMISE_KEY = "viewer.maximise"; + private static final String TEAM_NAME_KEY = "viewer.team-name"; + + private ScoreFunction scoreFunction; + private ViewComponent viewer; + private JLabel timeLabel; + private JLabel scoreLabel; + private JLabel teamLabel; + private JLabel mapLabel; + private NumberFormat format; + + + @Override + protected void postConnect() { + super.postConnect(); + int fontSize = config.getIntValue( FONT_SIZE_KEY, DEFAULT_FONT_SIZE ); + String teamName = config.getValue( TEAM_NAME_KEY, "" ); + scoreFunction = makeScoreFunction(); + format = NumberFormat.getInstance(); + format.setMaximumFractionDigits( PRECISION ); + JFrame frame = new JFrame( "Viewer " + getViewerID() + " (" + + model.getAllEntities().size() + " entities)" ); + viewer = new AnimatedWorldModelViewer(); + viewer.initialise( config ); + viewer.view( model ); + // CHECKSTYLE:OFF:MagicNumber + viewer.setPreferredSize( new Dimension( 500, 500 ) ); + // CHECKSTYLE:ON:MagicNumber + timeLabel = new JLabel( "Time: Not started", JLabel.CENTER ); + teamLabel = new JLabel( teamName, JLabel.CENTER ); + scoreLabel = new JLabel( "Score: Unknown", JLabel.CENTER ); + String mapdir = config.getValue( "gis.map.dir" ).trim(); + + String[] map_spl = mapdir.split( "/" ); + int index = map_spl.length - 1; + String mapname = map_spl[index].trim(); + if ( mapname.equals( "" ) ) mapname = map_spl[--index].trim(); + if ( mapname.equals( "map" ) ) mapname = map_spl[--index].trim(); + + String totalTime = config.getValue( "kernel.timesteps" ); + int channelCount = config.getIntValue( "comms.channels.count" ) - 1;// -1 + // for + // say + + mapLabel = new JLabel( + mapname + " (" + totalTime + ") | " + + ( channelCount == 0 ? "No Comm" : channelCount + " channels" ), + JLabel.CENTER ); + timeLabel.setBackground( Color.WHITE ); + timeLabel.setOpaque( true ); + timeLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + teamLabel.setBackground( Color.WHITE ); + teamLabel.setOpaque( true ); + teamLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + scoreLabel.setBackground( Color.WHITE ); + scoreLabel.setOpaque( true ); + scoreLabel + .setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + + mapLabel.setBackground( Color.WHITE ); + mapLabel.setOpaque( true ); + mapLabel.setFont( timeLabel.getFont().deriveFont( Font.PLAIN, fontSize ) ); + + frame.add( viewer, BorderLayout.CENTER ); + // CHECKSTYLE:OFF:MagicNumber + JPanel labels = new JPanel( new GridLayout( 1, 4 ) ); + // CHECKSTYLE:ON:MagicNumber + labels.add( teamLabel ); + labels.add( timeLabel ); + labels.add( scoreLabel ); + labels.add( mapLabel ); + frame.add( labels, BorderLayout.NORTH ); + frame.pack(); + if ( config.getBooleanValue( MAXIMISE_KEY, false ) ) { + frame.setExtendedState( JFrame.MAXIMIZED_BOTH ); + } + frame.setVisible( true ); + + viewer.addViewListener( new ViewListener() { + + @Override + public void objectsClicked( ViewComponent view, + List<RenderedObject> objects ) { + for ( RenderedObject next : objects ) { + System.out.println( next.getObject() ); + } + } + + + @Override + public void objectsRollover( ViewComponent view, + List<RenderedObject> objects ) { + } + } ); + } + + + @Override + protected void handleTimestep( final KVTimestep t ) { + super.handleTimestep( t ); + SwingUtilities.invokeLater( new Runnable() { + + public void run() { + timeLabel.setText( "Time: " + t.getTime() ); + scoreLabel.setText( "Score: " + format.format( + scoreFunction.score( model, new Timestep( t.getTime() ) ) ) ); + viewer.view( model, t.getCommands() ); + viewer.repaint(); + } + } ); + } + + + @Override + public String toString() { + return "Sample viewer"; + } + + + private ScoreFunction makeScoreFunction() { + String className = config.getValue( Constants.SCORE_FUNCTION_KEY ); + ScoreFunction result = instantiate( className, ScoreFunction.class ); + result.initialise( model, config ); + return result; + } +} diff --git a/modules/sample/src/sample/SampleViewerEventLogger.java b/modules/sample/src/sample/SampleViewerEventLogger.java new file mode 100644 index 0000000000000000000000000000000000000000..c6f9156b0238022004f71158b2ac475467f05326 --- /dev/null +++ b/modules/sample/src/sample/SampleViewerEventLogger.java @@ -0,0 +1,259 @@ +package sample; + +import static rescuecore2.misc.java.JavaTools.instantiate; + +import java.io.FileWriter; +import java.io.IOException; +import java.text.NumberFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.json.JSONArray; +import org.json.JSONObject; + +import rescuecore2.Constants; +import rescuecore2.Timestep; +import rescuecore2.log.Logger; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KVTimestep; +import rescuecore2.score.ScoreFunction; +import rescuecore2.standard.components.StandardViewer; +import rescuecore2.standard.messages.StandardMessageURN; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +/** + * A simple viewer event recorder. + */ +public class SampleViewerEventLogger extends StandardViewer { + + private static final int PRECISION = 3; + + private String JSON_RECORD_FILE_FORMAT = "%s__%d_viewer_event_log.jlog"; + private static final String TEAM_NAME_KEY = "viewer.team-name"; + private static final String RECORDS_DIR_KEY = "records.dir"; + private ScoreFunction scoreFunction; + private NumberFormat format; + + private String teamName; + private String recordsDir = "./records"; + private String logFilePath; + + + @Override + protected void postConnect() { + super.postConnect(); + + scoreFunction = makeScoreFunction(); + format = NumberFormat.getInstance(); + format.setMaximumFractionDigits( PRECISION ); + + Logger.info( "Sample Viewer Event Recorder start ..." ); + + // write summery + JSONObject jsonSummery = generateSummery(); + writeJsonFile( jsonSummery, logFilePath, false ); + + // write map + JSONObject jsonRecord = new JSONObject(); + JSONArray jsonAllEntities = generateMap(); + + jsonRecord.put( "Entities", jsonAllEntities ); + jsonRecord.put( "TimeStep", 0 ); + + writeJsonFile( jsonRecord, logFilePath, true ); + } + + + @Override + protected void handleTimestep( final KVTimestep kvt ) { + super.handleTimestep( kvt ); + + JSONObject jsonInfo = generateInfo( kvt ); + JSONObject jsonRecord = new JSONObject(); + + JSONArray jsonEntities = new JSONArray(); + JSONArray jsonDeletedEntities = new JSONArray(); + JSONArray jsonCommands = new JSONArray(); + + try { + jsonEntities = generateChanges( kvt ); + jsonDeletedEntities = getDeletedEntities( kvt ); + jsonCommands = getCommandActionLog( kvt ); + } catch(Exception e) { + e.printStackTrace(); + } + + jsonRecord.put( "Info", jsonInfo ); + jsonRecord.put( "TimeStep", kvt.getTime() ); + jsonRecord.put( "Commands", jsonCommands ); + jsonRecord.put( "Entities", jsonEntities ); + jsonRecord.put( "DeletedEntities", jsonDeletedEntities ); + + writeJsonFile( jsonRecord, logFilePath, true ); + } + + + private JSONArray generateMap() { + JSONArray jsonAllEntities = new JSONArray(); + for ( Entity entity : model.getAllEntities() ) { + try { + jsonAllEntities.put( entity.toJson() ); + } catch(Exception e) { + e.printStackTrace(); + } + } + return jsonAllEntities; + } + + + private JSONObject generateSummery() { + String mapName = getMapName(); + + this.teamName = config.getValue( TEAM_NAME_KEY, "unknown" ); + recordsDir = config.getValue( RECORDS_DIR_KEY, "./records" ); + String totalTime = config.getValue( "kernel.timesteps" ); + int channelCount = config.getIntValue( "comms.channels.count" ) - 1;// -1 + // for + // say + + String JSON_RECORD_FILE_NAME = String.format( JSON_RECORD_FILE_FORMAT, + mapName, System.currentTimeMillis() ); + logFilePath = recordsDir + "/" + JSON_RECORD_FILE_NAME; + + Logger + .debug( "Sample Viewer: mapName: " + mapName + " teamName: " + teamName + + " totalTime: " + totalTime + " channelCount:" + channelCount ); + + JSONObject jsonSummery = new JSONObject(); + jsonSummery.put( "TotalTime", totalTime ); + jsonSummery.put( "TeamName", this.teamName ); + jsonSummery.put( "channelCount", channelCount ); + jsonSummery.put( "MapName", mapName ); + return jsonSummery; + } + + + private JSONObject generateInfo( final KVTimestep t ) { + JSONObject jsonInfo = new JSONObject(); + double score = scoreFunction.score( model, new Timestep( t.getTime() ) ); + + jsonInfo.put( "Score", format.format( score ) ); + return jsonInfo; + } + + + private JSONArray generateChanges( final KVTimestep kvt ) { + JSONArray jsonEntities = new JSONArray(); + for ( EntityID id : kvt.getChangeSet().getChangedEntities() ) { + Entity entity = model.getEntity( id ); + Set<Property> changedProperties = kvt.getChangeSet() + .getChangedProperties( entity.getID() ); + try { + // Filter Entity + JSONObject jsonEntity = entity.toJson(); + JSONObject filteredJsonEntity = new JSONObject(); + for ( Property property : changedProperties ) { + String propertyName = property.getURN()+""; + if ( jsonEntity.has( propertyName ) + && !jsonEntity.isNull( propertyName ) ) { + String jsonEntityProperty = jsonEntity.get( propertyName ).toString(); + filteredJsonEntity.put( propertyName, jsonEntityProperty ); + } + } + + if ( !filteredJsonEntity.isEmpty() ) { + filteredJsonEntity.put( "Id", jsonEntity.get( "Id" ) ); + jsonEntities.put( jsonEntity ); + } + } catch(Exception e) { + e.printStackTrace(); + } + } + + return jsonEntities; + } + + + private JSONArray getDeletedEntities( final KVTimestep kvt ) { + JSONArray jsonDeletedEntities = new JSONArray(); + for ( EntityID id : kvt.getChangeSet().getDeletedEntities() ) { + try { + jsonDeletedEntities.put( id.getValue() ); + } catch(Exception e) { + e.printStackTrace(); + } + } + + return jsonDeletedEntities; + } + + + List<StandardMessageURN> allowed_command_child = Arrays.asList( + StandardMessageURN.AK_CLEAR, StandardMessageURN.AK_CLEAR_AREA, + StandardMessageURN.AK_EXTINGUISH, StandardMessageURN.AK_LOAD, + StandardMessageURN.AK_MOVE, StandardMessageURN.AK_RESCUE, + StandardMessageURN.AK_REST, StandardMessageURN.AK_UNLOAD ); + + + private JSONArray getCommandActionLog( final KVTimestep t ) { + JSONArray jsonAllEntities = new JSONArray(); + + for ( Command command : t.getCommands() ) { + StandardMessageURN commandStandardMessageURN = StandardMessageURN + .fromInt( command.getURN() ); + try { + if ( allowed_command_child.contains( commandStandardMessageURN ) ) { + jsonAllEntities.put( command.toJson() ); + } + } catch(Exception e) { + e.printStackTrace(); + } + } + return jsonAllEntities; + } + + + private void writeJsonFile( JSONObject output, String filename, + boolean append ) { + + // Write JSON file + try ( FileWriter file = new FileWriter( filename, append ) ) { + + file.write( JSONObject.valueToString( output ) + "\r\n" ); + file.flush(); + + } catch ( IOException e ) { + e.printStackTrace(); + } + } + + + private ScoreFunction makeScoreFunction() { + String className = config.getValue( Constants.SCORE_FUNCTION_KEY ); + ScoreFunction result = instantiate( className, ScoreFunction.class ); + result.initialise( model, config ); + return result; + } + + + private String getMapName() { + String mapDir = config.getValue( "gis.map.dir" ).trim(); + String[] map_spl = mapDir.split( "/" ); + int index = map_spl.length - 1; + String mapName = map_spl[index].trim(); + if ( mapName.equals( "" ) ) mapName = map_spl[--index].trim(); + if ( mapName.equals( "map" ) ) mapName = map_spl[--index].trim(); + + return mapName; + } + + + @Override + public String toString() { + return "SampleViewerRecorder"; + } + +} diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..fc9a6602291428d3ec55f3abf351f30499e6a8eb Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6217331a776797396632fd8138285d75734c1688 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..ed281d777dd0790dbfeae4812f06b64d808be33a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..22f87b9edc2e499e9d8b0b0283eb2d9bc1e8b338 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceCentre-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe6167614d77ab09a213badf459a03c43f7ada9 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ddf3ec0effe095d4ba9710c3cc8f30b82ef24d8e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..8db300b05e5bee20c177ddeccc487e9328aa6628 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..0730c545531803d2cf9614527cc189fa521e6299 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..e630875b9dc1c40825eb2cbc9cb890843f3dd099 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..bdea1e2d354abef620cb28ecea781909fa934350 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..b45802cac0059f1331cedcdc6446f6a9b14bbb2f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ded755e87006ad78189097f49c01191b826d3e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Critical-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..2cb72087facf0c2ad857a8d6e748fdd6e56c0b8c Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..754c5b66f50fcb6dfd867e9b6f1e56b56d7c2fdb Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..859e09401f27495baf43be7f269c54c7f49441e5 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..0d8ab41ca8bde895d3ee2e662cbfdeec7ff75bb6 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Dead-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe6167614d77ab09a213badf459a03c43f7ada9 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ddf3ec0effe095d4ba9710c3cc8f30b82ef24d8e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..8db300b05e5bee20c177ddeccc487e9328aa6628 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..0730c545531803d2cf9614527cc189fa521e6299 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Healthy-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-24x24.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..9be09d2c4a92e4b80cfa40957ab8200a8f147457 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-32x32.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..18881dea439d231a29b886ca04573da2f3e739c4 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-48x48.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..344fa93bcef2541bd678e385b562f4d92ab6f330 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-64x64.png b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..791e5f222af504c795ba4c9f2b87fc73f1271597 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/AmbulanceTeam-Injured-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..1569d60f56e4d01ed1d54c693213b9423002a436 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..e096779316a8e5d65da04bd1b5a69dfc99d29c66 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..accb7a0c2738865693082b9e42b80c032654fdea Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d2568b38460e8ff79a83382bef5d2dac1f023d57 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..240cd7427a10589650f9bc915a9b683669b071e7 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..d52322af13729584f9250e7a2a6dc7c730c6fd57 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..2924729ea330b781ac5b9c551d6b0f24cc9a1b6f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e494f7fb8867e2ac20d8965ead0caf72d4f6d1 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Critical-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..fee8098169e5e25797b5a3a147c8b535b34327a5 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..5bc8227cc4a01cddf69c801601988da7c8eda72b Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..99ce61abb4a671286be02fc1d69fca085c0d95f8 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..a3971904bb427b4d7c2e6d940cb2d235e9b89608 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Dead-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..1569d60f56e4d01ed1d54c693213b9423002a436 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..e096779316a8e5d65da04bd1b5a69dfc99d29c66 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..accb7a0c2738865693082b9e42b80c032654fdea Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d2568b38460e8ff79a83382bef5d2dac1f023d57 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Healthy-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..b560c6577fa13ef0118aa23f4d764482b0c4a7db Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce6aff8155936af82a31265f1e71c1b4e060215 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..a67c41d4489ab9a87efdf3d1752077bbd551b586 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..bab88b693b0f46688df478131b00b37b1351f51d Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Female-Injured-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..5622100c748de997fdfca8b57496c60b4ba6fa44 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..925d495dd2ac09da47585f8fd3b3c1b4ad3be1c3 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..51121d0e1547cd3eceeb4eb5f25f1f07c779465a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..4f9c39abfa8694c89d4722d045367e76836208e8 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..e26717b1b5729848e6f77cab018ad33be64257b8 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..133649166685ef23c6c02e1bedd74f04e4eebb59 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..4b996e6b3a1bf9edf43b3f157e2cd7f63bbb755e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..412de08557af139536f9987bed7690fe9ba05a26 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Critical-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..75111be9616c45c1d2e25145500b4afeadfcdeff Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1c0be2085f130a2189c13ecc2d7f90e8b6eb4f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..8d27667d6da46c09ce36651cdfaa10316c67b906 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..52813fd5cf8ab652ad7fc611d1adb8b667209819 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Dead-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..5622100c748de997fdfca8b57496c60b4ba6fa44 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..925d495dd2ac09da47585f8fd3b3c1b4ad3be1c3 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..51121d0e1547cd3eceeb4eb5f25f1f07c779465a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..4f9c39abfa8694c89d4722d045367e76836208e8 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Healthy-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..6fc812fcb4242c6b59c54ff8a46abb81e3604b84 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..70a4644790adb91d29428b7ea80e55d67284bec9 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..67ab851bfd59e9bc2aa2ccd06ea83d33da4298df Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..db6215a4280034b0acf28a15f81b14c0957ce72e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Civilian-Male-Injured-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..602bbd6381827702b517837e602530336e1a50c2 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..62e637f4284723dafa9bc217ee4ea76410d58b94 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..571edb819623d4c84dcde477bc8a23cfa161cd4e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..109797a2e9aaad007f78a20a757d3d37798cf6fc Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..c2cbbf514e55ff8965b8b4953bff98a3cefbfa8f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..f578b92ecd0157a6722d1c69553924fdd2161c9f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..72bb7845f06b251b75d231bb6bab3657343903ed Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..0c15679db2e78bd068e6c8a3d3ed3dfa8b91463b Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Critical-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..aea65677601d41d479b8c85ea21d0b73fe11fcc4 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..1f101323f1d6b7de7c143254c0c22293f3071c5d Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd4e7a8a5362c7ee0315feba053df23b3db28b4 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..c8387aeb96a1362f0b6892097c8887a0d2c68571 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Dead-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..602bbd6381827702b517837e602530336e1a50c2 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..62e637f4284723dafa9bc217ee4ea76410d58b94 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..571edb819623d4c84dcde477bc8a23cfa161cd4e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..109797a2e9aaad007f78a20a757d3d37798cf6fc Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Healthy-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..3f8fa730ec131f276fd3aff03f9b948c92c1032a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6e89b3f3e4b77c67ab49211f55b367e4eca2c223 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..fbe09d68339ae012f608a179374702670c9097db Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..9f54d12f29a5ba54548156a29b741997f1b99611 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireBrigade-Injured-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireStation-24x24.png b/modules/standard/resources/rescuecore2/standard/view/FireStation-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..60dfd142adacefc58fd168bd4587c1818a60ed32 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireStation-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireStation-32x32.png b/modules/standard/resources/rescuecore2/standard/view/FireStation-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..4dfa0ac9de3180e283da21ba42de6c6e2870e1ac Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireStation-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireStation-48x48.png b/modules/standard/resources/rescuecore2/standard/view/FireStation-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6c29f4fac2f9ae1f7d9081000be68193c2715b Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireStation-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/FireStation-64x64.png b/modules/standard/resources/rescuecore2/standard/view/FireStation-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..65ead1255467ff4960dd9bdeedbcb0ac8d9e7e1b Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/FireStation-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/GasStation-24x24.png b/modules/standard/resources/rescuecore2/standard/view/GasStation-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..ef77858591ca20336a91996d19b0c9d8d1813b97 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/GasStation-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/GasStation-32x32.png b/modules/standard/resources/rescuecore2/standard/view/GasStation-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f155c24daf1fad15a452713c1d63c5fd2af68e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/GasStation-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/GasStation-48x48.png b/modules/standard/resources/rescuecore2/standard/view/GasStation-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..ba3d4076e3d2670b2bb27ad9074d5248d7522f30 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/GasStation-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/GasStation-64x64.png b/modules/standard/resources/rescuecore2/standard/view/GasStation-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..b1103c54b5de1749a6995ebf894ca06ca3bb1fed Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/GasStation-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Hydrant-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Hydrant-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..b59d40c68e7e854a822dc0f4d68cd025669f58fa Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Hydrant-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Hydrant-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Hydrant-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..e82455e998260e210d2559914df5024548691f63 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Hydrant-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Hydrant-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Hydrant-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..3a423163bfd61ee6bb5675e758732604213a2dee Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Hydrant-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Hydrant-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Hydrant-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..be23a47d931bc6072ffba9d47d336457476c890c Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Hydrant-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/IconsLandStockIconsLicense.txt b/modules/standard/resources/rescuecore2/standard/view/IconsLandStockIconsLicense.txt new file mode 100644 index 0000000000000000000000000000000000000000..5a672754094f63d4d6b84fae1940ab2fc20f4a74 --- /dev/null +++ b/modules/standard/resources/rescuecore2/standard/view/IconsLandStockIconsLicense.txt @@ -0,0 +1,25 @@ +Stock Icons License Agreement: + +This is a legal agreement between you, the purchaser, and the Icons-Land.com. By purchasing or downloading any stock icons (The Icons) from our website you agree to the following: + +All of The Icons remain the property of Icons-Land.com. + +The Icons can be used royalty-free by the license for any personal or commercial project including software application, documentation, computer game, gui design, web design, advertising, film, video. +You may modify The Icons in shape, color, and/or file format and use the modified icons royalty-free according to the license terms for any personal or commercial product. + +The license DOES NOT permit following uses: +a) The Icons may no be resold, sublicensed, rented, transferred or otherwise made available for use or detached from a product, software application or web page; +b) The Icons may not be placed on any electronic bulletin board or downloadable format; +c) The Icons may not be included in any web template, including those which are web based, but not limited to website designs and presentation templates. + +You may NOT use, or allow anyone else to use The Icons to create pornographic, libelous, obscene, or defamatory material. + +All icon files are provided "as is". You agree not to hold Icons-Land.com liable for any damages that may occur due to use, or inability to use, icons or image data from Icons-Land.com. + + +============================================================================= +Demo Icons License Agreement: + +Demo set includes only few icons for evaluation purposes. +These icons are provided in .PNG format are are free of charge for personal use. + diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..4883c5c7ec94f1525d818dc41206f319f47a8c76 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..c4557f35817b0ab0bddb866934ced62892fc5eae Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..9876f28623ebb5b91c6c009077aac93246d97f54 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..9d99045f764f5c9a32b414de8b744253c338d61c Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..d818afb91c032b8de1ed3f7ac1a3cc9252322cdf Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7d940ce1c603f4531976373f3c3046b32d9c8b Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..2719bb3117429e702dc0377ba5355cabf1b47c82 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..a5319c4ad8f38280ce7976c4c16f080223fdda91 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Critical-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca7c057720ff44f4ef0aeec2d78008cb7cafd6d Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..593027c90386a5a512896de303a01be13ae807ac Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc8200459f238833ecc35405474c1c63857f1bb Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..59a8880d03cce4be4415592998b94cd43cabb52d Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Dead-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..4883c5c7ec94f1525d818dc41206f319f47a8c76 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..c4557f35817b0ab0bddb866934ced62892fc5eae Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..9876f28623ebb5b91c6c009077aac93246d97f54 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..9d99045f764f5c9a32b414de8b744253c338d61c Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Healthy-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..0b776c4b646446c9dead1b533a391a41f91e6b16 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..ddda9718d3a8e87c1a2c74bd0737dcaa57346c3f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..1758a496b35d7754a6e92e739b2fd9fcfe62900a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..7096062afbee0568836d32c868f79e8553c512e5 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceForce-Injured-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-24x24.png b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..c443b29d4a7862e920d11b9334d10fde5e66554f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-32x32.png b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..9725078f366dd67173d2fbf7a65caf42d6f4e48e Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-48x48.png b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..2f42f0efd6ca7e257e866dc105e029f13461b05f Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-64x64.png b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..1125f1576108c8a3a5b76ce378681b8dc469a67c Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/PoliceOffice-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Refuge-24x24.png b/modules/standard/resources/rescuecore2/standard/view/Refuge-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..4458e1c98416db2fbc1dc8b4f8d3a03ec34f25ff Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Refuge-24x24.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Refuge-32x32.png b/modules/standard/resources/rescuecore2/standard/view/Refuge-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..94bde654a818475c2bfe62419d9fe416f98becd2 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Refuge-32x32.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Refuge-48x48.png b/modules/standard/resources/rescuecore2/standard/view/Refuge-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..442bdabcf1e3662b24af06fc2519eca4dc3e0a3a Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Refuge-48x48.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/Refuge-64x64.png b/modules/standard/resources/rescuecore2/standard/view/Refuge-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d828aa396a9491198112bf5354048e2e90352684 Binary files /dev/null and b/modules/standard/resources/rescuecore2/standard/view/Refuge-64x64.png differ diff --git a/modules/standard/resources/rescuecore2/standard/view/sources.txt b/modules/standard/resources/rescuecore2/standard/view/sources.txt new file mode 100644 index 0000000000000000000000000000000000000000..c8f91118e47c92e89e6b48028b1b1e9c56d6607e --- /dev/null +++ b/modules/standard/resources/rescuecore2/standard/view/sources.txt @@ -0,0 +1,2 @@ +AmbulanceTeam, FireBrigade, PoliceForce: Icons-land.com +AmbulanceCentre, FireStation, PoliceOffice, Refuge, Civilian: Wikimedia commons diff --git a/modules/standard/src/rescuecore2/standard/Constants.java b/modules/standard/src/rescuecore2/standard/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..1eae6144747ec20516f880d41e9054f277e36884 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/Constants.java @@ -0,0 +1,25 @@ +package rescuecore2.standard; + +/** + * Constants for the standard package. + */ +public final class Constants { + /** Prefix for entity URNs. */ + public static final int ENTITY_URN_PREFIX = 0x1100; + /** Prefix for property URNs. */ + public static final int PROPERTY_URN_PREFIX = 0x1200; + /** Prefix for message URNs. */ + public static final int MESSAGE_URN_PREFIX = 0x1300; + /** Prefix for message component URNs. */ + public static final int STANDARD_MSG_COMPONENT_URN_PREFIX = 0x1400; + + /** Prefix for entity URNs. */ + public static final String ENTITY_URN_PREFIX_STR = "urn:rescuecore2.standard:entity:"; + /** Prefix for property URNs. */ + public static final String PROPERTY_URN_PREFIX_STR = "urn:rescuecore2.standard:property:"; + /** Prefix for message URNs. */ + public static final String MESSAGE_URN_PREFIX_STR = "urn:rescuecore2.standard:message:"; + + private Constants() { + } +} diff --git a/modules/standard/src/rescuecore2/standard/StandardConstants.java b/modules/standard/src/rescuecore2/standard/StandardConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..e53d5c0d8922187c9cd990ad564cf6cc1f078c06 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/StandardConstants.java @@ -0,0 +1,26 @@ +package rescuecore2.standard; + +/** + Useful constants for the standard package. +*/ +public final class StandardConstants { + /** Config key for the number of fire brigades in the scenario. */ + public static final String FIRE_BRIGADE_COUNT_KEY = "scenario.agents.fb"; + + /** Config key for the number of ambulance teams in the scenario. */ + public static final String AMBULANCE_TEAM_COUNT_KEY = "scenario.agents.at"; + + /** Config key for the number of police forces in the scenario. */ + public static final String POLICE_FORCE_COUNT_KEY = "scenario.agents.pf"; + + /** Config key for the number of fire stations in the scenario. */ + public static final String FIRE_STATION_COUNT_KEY = "scenario.agents.fs"; + + /** Config key for the number of ambulance centres in the scenario. */ + public static final String AMBULANCE_CENTRE_COUNT_KEY = "scenario.agents.ac"; + + /** Config key for the number of police offices in the scenario. */ + public static final String POLICE_OFFICE_COUNT_KEY = "scenario.agents.po"; + + private StandardConstants() {} +} diff --git a/modules/standard/src/rescuecore2/standard/components/StandardAgent.java b/modules/standard/src/rescuecore2/standard/components/StandardAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..5a53297c156c0c7dfbe7e34a676a2176f0d575ba --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/components/StandardAgent.java @@ -0,0 +1,234 @@ +package rescuecore2.standard.components; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import rescuecore2.components.AbstractAgent; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.messages.AKClear; +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.standard.messages.AKExtinguish; +import rescuecore2.standard.messages.AKLoad; +import rescuecore2.standard.messages.AKMove; +import rescuecore2.standard.messages.AKRescue; +import rescuecore2.standard.messages.AKRest; +import rescuecore2.standard.messages.AKSay; +import rescuecore2.standard.messages.AKSpeak; +import rescuecore2.standard.messages.AKSubscribe; +import rescuecore2.standard.messages.AKTell; +import rescuecore2.standard.messages.AKUnload; +import rescuecore2.worldmodel.EntityID; + +/** + * Abstract base class for standard agents. + * + * @param <E> The subclass of StandardEntity that this agent wants to control. + */ +public abstract class StandardAgent<E extends StandardEntity> extends AbstractAgent<StandardWorldModel, E> { + @Override + public final int[] getRequestedEntityURNs() { + EnumSet<StandardEntityURN> set = getRequestedEntityURNsEnum(); + int[] result = new int[set.size()]; + int i = 0; + for (StandardEntityURN next : set) { + result[i++] = next.getURNId(); + } + return result; + } + + /** + * Get an EnumSet containing requested entity URNs. + * + * @return An EnumSet containing requested entity URNs. + */ + protected abstract EnumSet<StandardEntityURN> getRequestedEntityURNsEnum(); + + @Override + protected StandardWorldModel createWorldModel() { + return new StandardWorldModel(); + } + + @Override + protected void postConnect() { + super.postConnect(); + if (shouldIndex()) { + model.index(); + } + } + + /** + * Send a rest command to the kernel. + * + * @param time The current time. + */ + protected void sendRest(int time) { + send(new AKRest(getID(), time)); + } + + /** + * Send a move command to the kernel. + * + * @param time The current time. + * @param path The path to send. + */ + protected void sendMove(int time, List<EntityID> path) { + send(new AKMove(getID(), time, path)); + } + + /** + * Send a move command to the kernel. + * + * @param time The current time. + * @param path The path to send. + * @param destX The destination X coordinate. + * @param destY The destination Y coordinate. + */ + protected void sendMove(int time, List<EntityID> path, int destX, int destY) { + send(new AKMove(getID(), time, path, destX, destY)); + } + + /** + * Send an extinguish command to the kernel. + * + * @param time The current time. + * @param target The target building. + * @param water The amount of water to use. + */ + protected void sendExtinguish(int time, EntityID target, int water) { + send(new AKExtinguish(getID(), time, target, water)); + } + + /** + * Send a clear command to the kernel. + * + * @param time The current time. + * @param target The target road. + */ + protected void sendClear(int time, EntityID target) { + send(new AKClear(getID(), time, target)); + } + + /** + * Send a clear command to the kernel. + * + * @param time The current time. + * @param destX The destination X coordinate to clear. + * @param destY The destination Y coordinate to clear. + */ + protected void sendClear(int time, int destX, int destY) { + send(new AKClearArea(getID(), time, destX, destY)); + } + + /** + * Send a rescue command to the kernel. + * + * @param time The current time. + * @param target The target human. + */ + protected void sendRescue(int time, EntityID target) { + send(new AKRescue(getID(), time, target)); + } + + /** + * Send a load command to the kernel. + * + * @param time The current time. + * @param target The target human. + */ + protected void sendLoad(int time, EntityID target) { + send(new AKLoad(getID(), time, target)); + } + + /** + * Send an unload command to the kernel. + * + * @param time The current time. + */ + protected void sendUnload(int time) { + send(new AKUnload(getID(), time)); + } + + /** + * Send a speak command to the kernel. + * + * @param time The current time. + * @param channel The channel to speak on. + * @param data The data to send. + */ + protected void sendSpeak(int time, int channel, byte[] data) { + send(new AKSpeak(getID(), time, channel, data)); + } + + /** + * Send a subscribe command to the kernel. + * + * @param time The current time. + * @param channels The channels to subscribe to. + */ + protected void sendSubscribe(int time, int... channels) { + send(new AKSubscribe(getID(), time, channels)); + } + + /** + * Send a say command to the kernel. + * + * @param time The current time. + * @param data The data to send. + */ + protected void sendSay(int time, byte[] data) { + send(new AKSay(getID(), time, data)); + } + + /** + * Send a tell command to the kernel. + * + * @param time The current time. + * @param data The data to send. + */ + protected void sendTell(int time, byte[] data) { + send(new AKTell(getID(), time, data)); + } + + /** + * Get a list of all refuges in the world. + * + * @return All refuges. + */ + protected List<Refuge> getRefuges() { + List<Refuge> result = new ArrayList<Refuge>(); + for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.REFUGE)) { + if (next instanceof Refuge) { + result.add((Refuge) next); + } + } + return result; + } + + /** + * Get the location of the entity controlled by this agent. + * + * @return The location of the entity controlled by this agent. + */ + protected StandardEntity location() { + E me = me(); + if (me instanceof Human) { + return ((Human) me).getPosition(model); + } + return me; + } + + /** + * Should the world model be automatically indexed? + * + * @return True if the world model should be automatically indexed, false + * otherwise. Default implementation returns true. + */ + protected boolean shouldIndex() { + return true; + } +} diff --git a/modules/standard/src/rescuecore2/standard/components/StandardSimulator.java b/modules/standard/src/rescuecore2/standard/components/StandardSimulator.java new file mode 100644 index 0000000000000000000000000000000000000000..7080426fe8e93f373bf69c4d77b7e25a4abcb1d6 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/components/StandardSimulator.java @@ -0,0 +1,20 @@ +package rescuecore2.standard.components; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.components.AbstractSimulator; + +/** + Abstract base class for standard simulators. +*/ +public abstract class StandardSimulator extends AbstractSimulator<StandardWorldModel> { + @Override + protected StandardWorldModel createWorldModel() { + return new StandardWorldModel(); + } + + @Override + protected void postConnect() { + super.postConnect(); + model.index(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/components/StandardViewer.java b/modules/standard/src/rescuecore2/standard/components/StandardViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..af4dda43ced6e31c02ef853eb2a93a43328fb9a6 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/components/StandardViewer.java @@ -0,0 +1,20 @@ +package rescuecore2.standard.components; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.components.AbstractViewer; + +/** + Abstract base class for standard viewers. +*/ +public abstract class StandardViewer extends AbstractViewer<StandardWorldModel> { + @Override + protected StandardWorldModel createWorldModel() { + return new StandardWorldModel(); + } + + @Override + protected void postConnect() { + super.postConnect(); + model.index(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/AmbulanceCentre.java b/modules/standard/src/rescuecore2/standard/entities/AmbulanceCentre.java new file mode 100644 index 0000000000000000000000000000000000000000..3c9119fff28ef5fbc41625077beabb0a414fa0d3 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/AmbulanceCentre.java @@ -0,0 +1,60 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The AmbulanceCentre object. + */ +public class AmbulanceCentre extends Building { + + /** + * Construct a AmbulanceCentre object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public AmbulanceCentre( EntityID id ) { + super( id ); + } + + + /** + * AmbulanceCentre copy constructor. + * + * @param other + * The AmbulanceCentre to copy. + */ + public AmbulanceCentre( AmbulanceCentre other ) { + super( other ); + } + + + /** + * Create an ambulance centre based on another Building. + * + * @param other + * The Building to copy. + */ + public AmbulanceCentre( Building other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new AmbulanceCentre( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.AMBULANCE_CENTRE; + } + + + @Override + protected String getEntityName() { + return "Ambulance centre"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/AmbulanceTeam.java b/modules/standard/src/rescuecore2/standard/entities/AmbulanceTeam.java new file mode 100644 index 0000000000000000000000000000000000000000..9c21e9153de511a5539b641a6f2d07ab179c5d2b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/AmbulanceTeam.java @@ -0,0 +1,50 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The AmbulanceTeam object. + */ +public class AmbulanceTeam extends Human { + + /** + * Construct a AmbulanceTeam object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public AmbulanceTeam( EntityID id ) { + super( id ); + } + + + /** + * AmbulanceTeam copy constructor. + * + * @param other + * The AmbulanceTeam to copy. + */ + public AmbulanceTeam( AmbulanceTeam other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new AmbulanceTeam( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.AMBULANCE_TEAM; + } + + + @Override + protected String getEntityName() { + return "Ambulance team"; + } + +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Area.java b/modules/standard/src/rescuecore2/standard/entities/Area.java new file mode 100644 index 0000000000000000000000000000000000000000..b4d2894db277010a5e3e70e990162838bde1c01e --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Area.java @@ -0,0 +1,398 @@ +package rescuecore2.standard.entities; + +import java.util.ArrayList; +import java.util.List; +import java.awt.Polygon; +import java.awt.Shape; + +import org.json.JSONObject; + +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.EntityListener; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.properties.EntityRefListProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * The Area object. + */ +public abstract class Area extends StandardEntity { + + private IntProperty x; + private IntProperty y; + private EdgeListProperty edges; + private EntityRefListProperty blockades; + + private Shape shape; + private int[] apexList; + private List<EntityID> neighbours; + + + /** + * Construct a subclass of Area with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + protected Area( EntityID id ) { + super( id ); + x = new IntProperty( StandardPropertyURN.X ); + y = new IntProperty( StandardPropertyURN.Y ); + edges = new EdgeListProperty( StandardPropertyURN.EDGES ); + blockades = new EntityRefListProperty( StandardPropertyURN.BLOCKADES ); + registerProperties( x, y, edges, blockades ); + shape = null; + apexList = null; + neighbours = null; + addEntityListener( new EdgesListener() ); + } + + + /** + * Area copy constructor. + * + * @param other + * The Area to copy. + */ + protected Area( Area other ) { + super( other ); + x = new IntProperty( other.x ); + y = new IntProperty( other.y ); + edges = new EdgeListProperty( other.edges ); + blockades = new EntityRefListProperty( other.blockades ); + registerProperties( x, y, edges, blockades ); + shape = null; + apexList = null; + neighbours = null; + addEntityListener( new EdgesListener() ); + } + + + @Override + public Pair<Integer, Integer> + getLocation( WorldModel<? extends StandardEntity> world ) { + return new Pair<Integer, Integer>( x.getValue(), y.getValue() ); + } + + + @Override + public Property getProperty(int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case X: + return x; + case Y: + return y; + case EDGES: + return edges; + case BLOCKADES: + return blockades; + default: + return super.getProperty( urn ); + } + } + + + /** + * Get the X property. + * + * @return The X property. + */ + public IntProperty getXProperty() { + return x; + } + + + /** + * Get the X coordinate. + * + * @return The X coordinate. + */ + public int getX() { + return x.getValue(); + } + + + /** + * Set the X coordinate. + * + * @param x + * The new X coordinate. + */ + public void setX( int x ) { + this.x.setValue( x ); + } + + + /** + * Find out if the X property has been defined. + * + * @return True if the X property has been defined, false otherwise. + */ + public boolean isXDefined() { + return x.isDefined(); + } + + + /** + * Undefine the X property. + */ + public void undefineX() { + x.undefine(); + } + + + /** + * Get the Y property. + * + * @return The Y property. + */ + public IntProperty getYProperty() { + return y; + } + + + /** + * Get the Y coordinate. + * + * @return The Y coordinate. + */ + public int getY() { + return y.getValue(); + } + + + /** + * Set the Y coordinate. + * + * @param y + * The new y coordinate. + */ + public void setY( int y ) { + this.y.setValue( y ); + } + + + /** + * Find out if the Y property has been defined. + * + * @return True if the Y property has been defined, false otherwise. + */ + public boolean isYDefined() { + return y.isDefined(); + } + + + /** + * Undefine the Y property. + */ + public void undefineY() { + y.undefine(); + } + + + /** + * Get the edges property. + * + * @return The edges property. + */ + public EdgeListProperty getEdgesProperty() { + return edges; + } + + + /** + * Get the edges of this area. + * + * @return The edges. + */ + public List<Edge> getEdges() { + return edges.getValue(); + } + + + /** + * Set the edges. + * + * @param edges + * The new edges. + */ + public void setEdges( List<Edge> edges ) { + this.edges.setEdges( edges ); + } + + + /** + * Find out if the edges property has been defined. + * + * @return True if the edges property has been defined, false otherwise. + */ + public boolean isEdgesDefined() { + return edges.isDefined(); + } + + + /** + * Undefine the edges property. + */ + public void undefineEdges() { + edges.undefine(); + } + + + /** + * Get the blockades property. + * + * @return The blockades property. + */ + public EntityRefListProperty getBlockadesProperty() { + return blockades; + } + + + /** + * Get the blockades in this area. + * + * @return The blockades. + */ + public List<EntityID> getBlockades() { + return blockades.getValue(); + } + + + /** + * Set the blockades in this area. + * + * @param blockades + * The new blockades. + */ + public void setBlockades( List<EntityID> blockades ) { + this.blockades.setValue( blockades ); + } + + + /** + * Find out if the blockades property has been defined. + * + * @return True if the blockades property has been defined, false otherwise. + */ + public boolean isBlockadesDefined() { + return blockades.isDefined(); + } + + + /** + * Undefine the blockades property. + */ + public void undefineBlockades() { + blockades.undefine(); + } + + + /** + * Get the neighbours of this area. + * + * @return The neighbours. + */ + public List<EntityID> getNeighbours() { + if ( neighbours == null ) { + neighbours = new ArrayList<EntityID>(); + for ( Edge next : edges.getValue() ) { + if ( next.isPassable() ) { + neighbours.add( next.getNeighbour() ); + } + } + } + return neighbours; + } + + + /** + * Get the edge that crosses to a particular neighbour. + * + * @param neighbour + * The neighbour ID. + * @return The edge that crosses to the given neighbour, or null if no edges + * border that neighbour. + */ + public Edge getEdgeTo( EntityID neighbour ) { + for ( Edge next : getEdges() ) { + if ( neighbour.equals( next.getNeighbour() ) ) { + return next; + } + } + return null; + } + + + /** + * Get the list of apexes for this area. + * + * @return The list of apexes. + */ + public int[] getApexList() { + if ( apexList == null ) { + List<Edge> e = getEdges(); + apexList = new int[e.size() * 2]; + int i = 0; + for ( Edge next : e ) { + apexList[i++] = next.getStartX(); + apexList[i++] = next.getStartY(); + } + } + return apexList; + } + + + /** + * Get this area as a Java Shape object. + * + * @return A Shape describing this area. + */ + public Shape getShape() { + if ( shape == null ) { + int[] apexes = getApexList(); + int count = apexes.length / 2; + int[] xs = new int[count]; + int[] ys = new int[count]; + for ( int i = 0; i < count; ++i ) { + xs[i] = apexes[i * 2]; + ys[i] = apexes[i * 2 + 1]; + } + shape = new Polygon( xs, ys, count ); + } + return shape; + } + + + private class EdgesListener implements EntityListener { + + @Override + public void propertyChanged( Entity e, Property p, Object oldValue, + Object newValue ) { + if ( p == edges ) { + shape = null; + apexList = null; + neighbours = null; + } + } + } + + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put( StandardPropertyURN.APEXES.toString(), + this.isEdgesDefined() ? this.getApexList() : JSONObject.NULL ); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Blockade.java b/modules/standard/src/rescuecore2/standard/entities/Blockade.java new file mode 100644 index 0000000000000000000000000000000000000000..35405744ca9ba48fe9e00e832723c9a8b3cb65bf --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Blockade.java @@ -0,0 +1,411 @@ +package rescuecore2.standard.entities; + +import java.awt.Polygon; +import java.awt.Shape; + +import org.json.JSONObject; + +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.EntityListener; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.properties.EntityRefProperty; +import rescuecore2.worldmodel.properties.IntArrayProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * A blockade. + */ +public class Blockade extends StandardEntity { + + private IntProperty x; + private IntProperty y; + private EntityRefProperty position; + private IntArrayProperty apexes; + private IntProperty repairCost; + + private Shape shape; + + + /** + * Construct a Blockade object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + public Blockade( EntityID id ) { + super( id ); + x = new IntProperty( StandardPropertyURN.X ); + y = new IntProperty( StandardPropertyURN.Y ); + position = new EntityRefProperty( StandardPropertyURN.POSITION ); + apexes = new IntArrayProperty( StandardPropertyURN.APEXES ); + repairCost = new IntProperty( StandardPropertyURN.REPAIR_COST ); + registerProperties( x, y, position, apexes, repairCost ); + shape = null; + addEntityListener( new ApexesListener() ); + } + + + /** + * Blockade copy constructor. + * + * @param other + * The Blockade to copy. + */ + public Blockade( Blockade other ) { + super( other ); + x = new IntProperty( other.x ); + y = new IntProperty( other.y ); + position = new EntityRefProperty( other.position ); + apexes = new IntArrayProperty( other.apexes ); + repairCost = new IntProperty( other.repairCost ); + registerProperties( x, y, position, apexes, repairCost ); + shape = null; + addEntityListener( new ApexesListener() ); + } + + + @Override + public Pair<Integer, Integer> + getLocation( WorldModel<? extends StandardEntity> world ) { + if ( !x.isDefined() || !y.isDefined() ) { + return null; + } + return new Pair<Integer, Integer>( x.getValue(), y.getValue() ); + } + + + @Override + protected Entity copyImpl() { + return new Blockade( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.BLOCKADE; + } + + + @Override + protected String getEntityName() { + return "Blockade"; + } + + + @Override + public Property getProperty(int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case X: + return x; + case Y: + return y; + case POSITION: + return position; + case APEXES: + return apexes; + case REPAIR_COST: + return repairCost; + default: + return super.getProperty( urn ); + } + } + + + /** + * Get the X property. + * + * @return The X property. + */ + public IntProperty getXProperty() { + return x; + } + + + /** + * Get the X coordinate. + * + * @return The X coordinate. + */ + public int getX() { + return x.getValue(); + } + + + /** + * Set the X coordinate. + * + * @param x + * The new X coordinate. + */ + public void setX( int x ) { + this.x.setValue( x ); + } + + + /** + * Find out if the X property has been defined. + * + * @return True if the X property has been defined, false otherwise. + */ + public boolean isXDefined() { + return x.isDefined(); + } + + + /** + * Undefine the X property. + */ + public void undefineX() { + x.undefine(); + } + + + /** + * Get the Y property. + * + * @return The Y property. + */ + public IntProperty getYProperty() { + return y; + } + + + /** + * Get the Y coordinate. + * + * @return The Y coordinate. + */ + public int getY() { + return y.getValue(); + } + + + /** + * Set the Y coordinate. + * + * @param y + * The new y coordinate. + */ + public void setY( int y ) { + this.y.setValue( y ); + } + + + /** + * Find out if the Y property has been defined. + * + * @return True if the Y property has been defined, false otherwise. + */ + public boolean isYDefined() { + return y.isDefined(); + } + + + /** + * Undefine the Y property. + */ + public void undefineY() { + y.undefine(); + } + + + /** + * Get the apexes property. + * + * @return The apexes property. + */ + public IntArrayProperty getApexesProperty() { + return apexes; + } + + + /** + * Get the apexes of this area. + * + * @return The apexes. + */ + public int[] getApexes() { + return apexes.getValue(); + } + + + /** + * Set the apexes. + * + * @param apexes + * The new apexes. + */ + public void setApexes( int[] apexes ) { + this.apexes.setValue( apexes ); + } + + + /** + * Find out if the apexes property has been defined. + * + * @return True if the apexes property has been defined, false otherwise. + */ + public boolean isApexesDefined() { + return apexes.isDefined(); + } + + + /** + * Undefine the apexes property. + */ + public void undefineApexes() { + apexes.undefine(); + } + + + /** + * Get the position property. + * + * @return The position property. + */ + public EntityRefProperty getPositionProperty() { + return position; + } + + + /** + * Get the position of this blockade. + * + * @return The position. + */ + public EntityID getPosition() { + return position.getValue(); + } + + + /** + * Set the position. + * + * @param position + * The new position. + */ + public void setPosition( EntityID position ) { + this.position.setValue( position ); + } + + + /** + * Find out if the position property has been defined. + * + * @return True if the position property has been defined, false otherwise. + */ + public boolean isPositionDefined() { + return position.isDefined(); + } + + + /** + * Undefine the position property. + */ + public void undefinePosition() { + position.undefine(); + } + + + /** + * Get the repair cost property. + * + * @return The repair cost property. + */ + public IntProperty getRepairCostProperty() { + return repairCost; + } + + + /** + * Get the repair cost of this blockade. + * + * @return The repair cost. + */ + public int getRepairCost() { + return repairCost.getValue(); + } + + + /** + * Set the repair cost. + * + * @param cost + * The new repair cost. + */ + public void setRepairCost( int cost ) { + this.repairCost.setValue( cost ); + } + + + /** + * Find out if the repair cost property has been defined. + * + * @return True if the repair cost property has been defined, false otherwise. + */ + public boolean isRepairCostDefined() { + return repairCost.isDefined(); + } + + + /** + * Undefine the repair cost property. + */ + public void undefineRepairCost() { + repairCost.undefine(); + } + + + /** + * Get this area as a Java Shape object. + * + * @return A Shape describing this area. + */ + public Shape getShape() { + if ( shape == null ) { + int[] allApexes = getApexes(); + int count = allApexes.length / 2; + int[] xs = new int[count]; + int[] ys = new int[count]; + for ( int i = 0; i < count; ++i ) { + xs[i] = allApexes[i * 2]; + ys[i] = allApexes[i * 2 + 1]; + } + shape = new Polygon( xs, ys, count ); + } + return shape; + } + + + private class ApexesListener implements EntityListener { + + @Override + public void propertyChanged( Entity e, Property p, Object oldValue, + Object newValue ) { + if ( p == apexes ) { + shape = null; + } + } + } + + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put( StandardPropertyURN.APEXES.toString(), + this.isApexesDefined() ? this.getApexes() : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.REPAIR_COST.toString(), + this.isRepairCostDefined() ? this.getRepairCost() : JSONObject.NULL ); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Building.java b/modules/standard/src/rescuecore2/standard/entities/Building.java new file mode 100755 index 0000000000000000000000000000000000000000..7fc687d31b391dcb6641f10a68d857035eb1d806 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Building.java @@ -0,0 +1,736 @@ +package rescuecore2.standard.entities; + +import org.json.JSONObject; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.properties.BooleanProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +import java.util.EnumSet; + +/** + * The Building object. + */ +public class Building extends Area { + + /** + * Fieryness levels that indicate burning. + */ + public static final EnumSet<StandardEntityConstants.Fieryness> BURNING = EnumSet + .of( StandardEntityConstants.Fieryness.HEATING, + StandardEntityConstants.Fieryness.BURNING, + StandardEntityConstants.Fieryness.INFERNO ); + + private IntProperty floors; + private BooleanProperty ignition; + private IntProperty fieryness; + private IntProperty brokenness; + private IntProperty code; + private IntProperty attributes; + private IntProperty groundArea; + private IntProperty totalArea; + private IntProperty temperature; + private IntProperty importance; + private IntProperty capacity; + + + /** + * Construct a Building object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + public Building( EntityID id ) { + super( id ); + floors = new IntProperty( StandardPropertyURN.FLOORS ); + ignition = new BooleanProperty( StandardPropertyURN.IGNITION ); + fieryness = new IntProperty( StandardPropertyURN.FIERYNESS ); + brokenness = new IntProperty( StandardPropertyURN.BROKENNESS ); + code = new IntProperty( StandardPropertyURN.BUILDING_CODE ); + attributes = new IntProperty( StandardPropertyURN.BUILDING_ATTRIBUTES ); + groundArea = new IntProperty( StandardPropertyURN.BUILDING_AREA_GROUND ); + totalArea = new IntProperty( StandardPropertyURN.BUILDING_AREA_TOTAL ); + temperature = new IntProperty( StandardPropertyURN.TEMPERATURE ); + importance = new IntProperty( StandardPropertyURN.IMPORTANCE ); + capacity = new IntProperty( StandardPropertyURN.CAPACITY ); + registerProperties( floors, ignition, fieryness, brokenness, code, + attributes, groundArea, totalArea, temperature, importance, capacity ); + } + + + /** + * Building copy constructor. + * + * @param other + * The Building to copy. + */ + public Building( Building other ) { + super( other ); + floors = new IntProperty( other.floors ); + ignition = new BooleanProperty( other.ignition ); + fieryness = new IntProperty( other.fieryness ); + brokenness = new IntProperty( other.brokenness ); + code = new IntProperty( other.code ); + attributes = new IntProperty( other.attributes ); + groundArea = new IntProperty( other.groundArea ); + totalArea = new IntProperty( other.totalArea ); + temperature = new IntProperty( other.temperature ); + importance = new IntProperty( other.importance ); + capacity = new IntProperty( other.capacity ); + registerProperties( floors, ignition, fieryness, brokenness, code, + attributes, groundArea, totalArea, temperature, importance, capacity ); + } + + + @Override + protected Entity copyImpl() { + return new Building( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.BUILDING; + } + + + @Override + protected String getEntityName() { + return "Building"; + } + + + @Override + public Property getProperty(int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case FLOORS: + return floors; + case IGNITION: + return ignition; + case FIERYNESS: + return fieryness; + case BROKENNESS: + return brokenness; + case BUILDING_CODE: + return code; + case BUILDING_ATTRIBUTES: + return attributes; + case BUILDING_AREA_GROUND: + return groundArea; + case BUILDING_AREA_TOTAL: + return totalArea; + case TEMPERATURE: + return temperature; + case IMPORTANCE: + return importance; + case CAPACITY: + return capacity; + default: + return super.getProperty( urn ); + } + } + + + /** + * Get the floors property. + * + * @return The floors property. + */ + public IntProperty getFloorsProperty() { + return floors; + } + + + /** + * Get the number of floors in this building. + * + * @return The number of floors. + */ + public int getFloors() { + return floors.getValue(); + } + + + /** + * Set the number of floors in this building. + * + * @param floors + * The new number of floors. + */ + public void setFloors( int floors ) { + this.floors.setValue( floors ); + } + + + /** + * Find out if the floors property has been defined. + * + * @return True if the floors property has been defined, false otherwise. + */ + public boolean isFloorsDefined() { + return floors.isDefined(); + } + + + /** + * Undefine the floors property. + */ + public void undefineFloors() { + floors.undefine(); + } + + + /** + * Get the ignition property. + * + * @return The ignition property. + */ + public BooleanProperty getIgnitionProperty() { + return ignition; + } + + + /** + * Get the value of the ignition property. + * + * @return The ignition property value. + */ + public boolean getIgnition() { + return ignition.getValue(); + } + + + /** + * Set the ignition property. + * + * @param ignition + * The new ignition value. + */ + public void setIgnition( boolean ignition ) { + this.ignition.setValue( ignition ); + } + + + /** + * Find out if the ignition property has been defined. + * + * @return True if the ignition property has been defined, false otherwise. + */ + public boolean isIgnitionDefined() { + return ignition.isDefined(); + } + + + /** + * Undefine the ingition property. + */ + public void undefineIgnition() { + ignition.undefine(); + } + + + /** + * Get the fieryness property. + * + * @return The fieryness property. + */ + public IntProperty getFierynessProperty() { + return fieryness; + } + + + /** + * Get the fieryness of this building. + * + * @return The fieryness property value. + */ + public int getFieryness() { + return fieryness.getValue(); + } + + + /** + * Get the fieryness of this building as an enum constant. If fieryness is not + * defined then return null. + * + * @return The fieryness property value as a Fieryness enum, or null if + * fieryness is undefined. + */ + public StandardEntityConstants.Fieryness getFierynessEnum() { + if ( !fieryness.isDefined() ) { + return null; + } + return StandardEntityConstants.Fieryness.values()[fieryness.getValue()]; + } + + + /** + * Set the fieryness of this building. + * + * @param fieryness + * The new fieryness value. + */ + public void setFieryness( int fieryness ) { + this.fieryness.setValue( fieryness ); + } + + + /** + * Find out if the fieryness property has been defined. + * + * @return True if the fieryness property has been defined, false otherwise. + */ + public boolean isFierynessDefined() { + return fieryness.isDefined(); + } + + + /** + * Undefine the fieryness property. + */ + public void undefineFieryness() { + fieryness.undefine(); + } + + + /** + * Get the brokenness property. + * + * @return The brokenness property. + */ + public IntProperty getBrokennessProperty() { + return brokenness; + } + + + /** + * Get the brokenness of this building. + * + * @return The brokenness value. + */ + public int getBrokenness() { + return brokenness.getValue(); + } + + + /** + * Set the brokenness of this building. + * + * @param brokenness + * The new brokenness. + */ + public void setBrokenness( int brokenness ) { + this.brokenness.setValue( brokenness ); + } + + + /** + * Find out if the brokenness property has been defined. + * + * @return True if the brokenness property has been defined, false otherwise. + */ + public boolean isBrokennessDefined() { + return brokenness.isDefined(); + } + + + /** + * Undefine the brokenness property. + */ + public void undefineBrokenness() { + brokenness.undefine(); + } + + + /** + * Get the building code property. + * + * @return The building code property. + */ + public IntProperty getBuildingCodeProperty() { + return code; + } + + + /** + * Get the building code of this building. + * + * @return The building code. + */ + public int getBuildingCode() { + return code.getValue(); + } + + + /** + * Get the building code of this building as an enum constant. If building + * code is not defined then return null. + * + * @return The building code property value as a BuildingCode enum, or null if + * building code is undefined. + */ + public StandardEntityConstants.BuildingCode getBuildingCodeEnum() { + if ( !code.isDefined() ) { + return null; + } + return StandardEntityConstants.BuildingCode.values()[code.getValue()]; + } + + + /** + * Set the building code of this building. + * + * @param newCode + * The new building code. + */ + public void setBuildingCode( int newCode ) { + this.code.setValue( newCode ); + } + + + /** + * Find out if the building code has been defined. + * + * @return True if the building code has been defined, false otherwise. + */ + public boolean isBuildingCodeDefined() { + return code.isDefined(); + } + + + /** + * Undefine the building code. + */ + public void undefineBuildingCode() { + code.undefine(); + } + + + /** + * Get the building attributes property. + * + * @return The building attributes property. + */ + public IntProperty getBuildingAttributesProperty() { + return attributes; + } + + + /** + * Get the building attributes of this building. + * + * @return The building attributes. + */ + public int getBuildingAttributes() { + return attributes.getValue(); + } + + + /** + * Set the building attributes of this building. + * + * @param newAttributes + * The new building attributes. + */ + public void setBuildingAttributes( int newAttributes ) { + this.attributes.setValue( newAttributes ); + } + + + /** + * Find out if the building attributes property has been defined. + * + * @return True if the building attributes property has been defined, false + * otherwise. + */ + public boolean isBuildingAttributesDefined() { + return attributes.isDefined(); + } + + + /** + * Undefine the building attributes. + */ + public void undefineBuildingAttributes() { + attributes.undefine(); + } + + + /** + * Get the ground area property. + * + * @return The ground area property. + */ + public IntProperty getGroundAreaProperty() { + return groundArea; + } + + + /** + * Get the ground area of this building. + * + * @return The ground area. + */ + public int getGroundArea() { + return groundArea.getValue(); + } + + + /** + * Set the ground area of this building. + * + * @param ground + * The new ground area. + */ + public void setGroundArea( int ground ) { + this.groundArea.setValue( ground ); + } + + + /** + * Find out if the ground area property has been defined. + * + * @return True if the ground area property has been defined, false otherwise. + */ + public boolean isGroundAreaDefined() { + return groundArea.isDefined(); + } + + + /** + * Undefine the ground area. + */ + public void undefineGroundArea() { + groundArea.undefine(); + } + + + /** + * Get the total area property. + * + * @return The total area property. + */ + public IntProperty getTotalAreaProperty() { + return totalArea; + } + + + /** + * Get the total area of this building. + * + * @return The total area. + */ + public int getTotalArea() { + return totalArea.getValue(); + } + + + /** + * Set the total area of this building. + * + * @param total + * The new total area. + */ + public void setTotalArea( int total ) { + this.totalArea.setValue( total ); + } + + + /** + * Find out if the total area property has been defined. + * + * @return True if the total area property has been defined, false otherwise. + */ + public boolean isTotalAreaDefined() { + return totalArea.isDefined(); + } + + + /** + * Undefine the total area property. + */ + public void undefineTotalArea() { + totalArea.undefine(); + } + + + /** + * Get the temperature property. + * + * @return The temperature property. + */ + public IntProperty getTemperatureProperty() { + return temperature; + } + + + /** + * Get the temperature of this building. + * + * @return The temperature. + */ + public int getTemperature() { + return temperature.getValue(); + } + + + /** + * Set the temperature of this building. + * + * @param temperature + * The new temperature. + */ + public void setTemperature( int temperature ) { + this.temperature.setValue( temperature ); + } + + + /** + * Find out if the temperature property has been defined. + * + * @return True if the temperature property has been defined, false otherwise. + */ + public boolean isTemperatureDefined() { + return temperature.isDefined(); + } + + + /** + * Undefine the temperature property. + */ + public void undefineTemperature() { + temperature.undefine(); + } + + + /** + * Get the building importance property. + * + * @return The importance property. + */ + public IntProperty getImportanceProperty() { + return importance; + } + + + /** + * Get the importance of this building. + * + * @return The importance. + */ + public int getImportance() { + return importance.getValue(); + } + + + /** + * Set the importance of this building. + * + * @param importance + * The new importance. + */ + public void setImportance( int importance ) { + this.importance.setValue( importance ); + } + + + /** + * Find out if the importance property has been defined. + * + * @return True if the importance property has been defined, false otherwise. + */ + public boolean isImportanceDefined() { + return importance.isDefined(); + } + + + /** + * Undefine the importance property. + */ + public void undefineImportance() { + importance.undefine(); + } + + + /** + * Get the capacity property. + * + * @return The capacity property. + */ + public IntProperty getCapacityProperty() { + return capacity; + } + + + /** + * Get the capacity of this building. + * + * @return The capacity. + */ + public int getCapacity() { + return capacity.getValue(); + } + + + /** + * Set the capacity of this building. + * + * @param capacity + * The new temperature. + */ + public void setCapacity( int capacity ) { + this.capacity.setValue( capacity ); + } + + + /** + * Find out if the capacity property has been defined. + * + * @return True if the capacity property has been defined, false otherwise. + */ + public boolean isCapacityDefined() { + return capacity.isDefined(); + } + + + /** + * Undefine the capacity property. + */ + public void undefineCapacity() { + capacity.undefine(); + } + + + /** + * Find out if this building is on fire. + * + * @return True iff this buildings fieryness indicates that it is burning. + */ + public boolean isOnFire() { + if ( !fieryness.isDefined() ) { + return false; + } + return BURNING.contains( getFierynessEnum() ); + } + + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put( StandardPropertyURN.BROKENNESS.toString(), + this.isBrokennessDefined() ? this.getBrokenness() : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.FIERYNESS.toString(), + this.isFierynessDefined() ? this.getFieryness() : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.FLOORS.toString(), + this.isFloorsDefined() ? this.getFloors() : JSONObject.NULL ); + + return jsonObject; + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/Civilian.java b/modules/standard/src/rescuecore2/standard/entities/Civilian.java new file mode 100644 index 0000000000000000000000000000000000000000..da55f4f9fe5ad9f152ad105ec2bb612965939528 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Civilian.java @@ -0,0 +1,49 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The Civilian object. + */ +public class Civilian extends Human { + + /** + * Construct a Civilian object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public Civilian( EntityID id ) { + super( id ); + } + + + /** + * Civilian copy constructor. + * + * @param other + * The Civilian to copy. + */ + public Civilian( Civilian other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new Civilian( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.CIVILIAN; + } + + + @Override + protected String getEntityName() { + return "Civilian"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Edge.java b/modules/standard/src/rescuecore2/standard/entities/Edge.java new file mode 100644 index 0000000000000000000000000000000000000000..35cfae2b284d909165f520de7adf946532758975 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Edge.java @@ -0,0 +1,198 @@ +package rescuecore2.standard.entities; + +import org.json.JSONObject; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +/** + * An edge is a line segment with an optional neighbouring entity. Edges without + * neighbours are impassable, edges with neighbours are passable. + */ +public class Edge { + + private Point2D start; + private Point2D end; + private Line2D line; + private EntityID neighbour; + + + /** + * Construct an impassable Edge. + * + * @param startX + * The X coordinate of the first endpoint. + * @param startY + * The Y coordinate of the first endpoint. + * @param endX + * The X coordinate of the second endpoint. + * @param endY + * The Y coordinate of the second endpoint. + */ + public Edge( int startX, int startY, int endX, int endY ) { + this( new Point2D( startX, startY ), new Point2D( endX, endY ), null ); + } + + + /** + * Construct an impassable Edge. + * + * @param start + * The first endpoint coordinates. + * @param end + * The second endpoint coordinates. + */ + public Edge( Point2D start, Point2D end ) { + this( start, end, null ); + } + + + /** + * Construct an Edge. If the neighbour is null then this edge is impassable; + * if it is non-null then this edge is passable. + * + * @param startX + * The X coordinate of the first endpoint. + * @param startY + * The Y coordinate of the first endpoint. + * @param endX + * The X coordinate of the second endpoint. + * @param endY + * The Y coordinate of the second endpoint. + * @param neighbour + * The ID of the neighbour on the other side of this edge. This may + * be null to indicate an impassable edge. + */ + public Edge( int startX, int startY, int endX, int endY, EntityID neighbour ) { + this( new Point2D( startX, startY ), new Point2D( endX, endY ), neighbour ); + } + + + /** + * Construct an Edge. If the neighbour is null then this edge is impassable; + * if it is non-null then this edge is passable. + * + * @param start + * The first endpoint coordinates. + * @param end + * The second endpoint coordinates. + * @param neighbour + * The ID of the neighbour on the other side of this edge. This may + * be null to indicate an impassable edge. + */ + public Edge( Point2D start, Point2D end, EntityID neighbour ) { + this.start = start; + this.end = end; + this.neighbour = neighbour; + line = new Line2D( start, end ); + } + + + /** + * Get the X coordinate of the first endpoint. + * + * @return The X coordinate of the first endpoint. + */ + public int getStartX() { + return (int) start.getX(); + } + + + /** + * Get the Y coordinate of the first endpoint. + * + * @return The Y coordinate of the first endpoint. + */ + public int getStartY() { + return (int) start.getY(); + } + + + /** + * Get the X coordinate of the second endpoint. + * + * @return The X coordinate of the second endpoint. + */ + public int getEndX() { + return (int) end.getX(); + } + + + /** + * Get the Y coordinate of the second endpoint. + * + * @return The Y coordinate of the second endpoint. + */ + public int getEndY() { + return (int) end.getY(); + } + + + /** + * Get the start point. + * + * @return The start point. + */ + public Point2D getStart() { + return start; + } + + + /** + * Get the end point. + * + * @return The end point. + */ + public Point2D getEnd() { + return end; + } + + + /** + * Get the ID of the neighbour. + * + * @return The ID of the neighbour or null if this edge is impassable. + */ + public EntityID getNeighbour() { + return neighbour; + } + + + /** + * Find out if this edge is passable or not. + * + * @return True iff the neighbour is non-null. + */ + public boolean isPassable() { + return neighbour != null; + } + + + /** + * Get a line representing this edge. + * + * @return A Line2D representing this edge. + */ + public Line2D getLine() { + return line; + } + + + @Override + public String toString() { + return "Edge from " + start + " to " + end + " (" + + ( neighbour == null ? "impassable" : "neighbour: " + neighbour ) + + ")"; + } + + + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put( "StartX", this.getStartX() ); + jsonObject.put( "StartY", this.getStartY() ); + jsonObject.put( "EndX", this.getEndX() ); + jsonObject.put( "EndY", this.getEndY() ); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/EdgeListProperty.java b/modules/standard/src/rescuecore2/standard/entities/EdgeListProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..a8b50ca99a52965a5f5fe5dc796dc3c9272f1707 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/EdgeListProperty.java @@ -0,0 +1,209 @@ +package rescuecore2.standard.entities; + +import static rescuecore2.misc.EncodingTools.readInt32; +import static rescuecore2.misc.EncodingTools.writeInt32; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import rescuecore2.URN; +import rescuecore2.messages.protobuf.RCRSProto.EdgeListProto; +import rescuecore2.messages.protobuf.RCRSProto.EdgeProto; +import rescuecore2.messages.protobuf.RCRSProto.PropertyProto; +import rescuecore2.worldmodel.AbstractProperty; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; + +/** + * A property that defines a list of Edges. + */ +public class EdgeListProperty extends AbstractProperty { + + private List<Edge> edges; + + /** + * Construct a new EdgeListProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EdgeListProperty(int urn) { + super(urn); + edges = new ArrayList<Edge>(); + } + + /** + * Construct a new EdgeListProperty with no defined value. + * + * @param urn The urn of this property. + */ + public EdgeListProperty(URN urn) { + super(urn); + edges = new ArrayList<Edge>(); + } + + /** + * Construct a new EdgeListProperty with no defined value. + * + * @param urn The urn of this property. + * @param edges The edge list. + */ + public EdgeListProperty(int urn, List<Edge> edges) { + super(urn, true); + this.edges = new ArrayList<Edge>(edges); + } + + /** + * Construct a new EdgeListProperty with no defined value. + * + * @param urn The urn of this property. + * @param edges The edge list. + */ + public EdgeListProperty(URN urn, List<Edge> edges) { + super(urn, true); + this.edges = new ArrayList<Edge>(edges); + } + + /** + * EdgeListProperty copy constructor. + * + * @param other The EdgeListProperty to copy. + */ + public EdgeListProperty(EdgeListProperty other) { + super(other); + this.edges = new ArrayList<Edge>(other.edges); + } + + @Override + public List<Edge> getValue() { + if (!isDefined()) { + return null; + } + return Collections.unmodifiableList(edges); + } + + /** + * Set the list of edges. Future calls to {@link #isDefined()} will return true. + * + * @param newEdges The new edge list. + */ + public void setEdges(List<Edge> newEdges) { + edges.clear(); + edges.addAll(newEdges); + setDefined(); + } + + /** + * Add an edge to the list. + * + * @param edge The edge to add. + */ + public void addEdge(Edge edge) { + edges.add(edge); + setDefined(); + } + + /** + * Remove all edges from this list but keep it defined. + */ + public void clearEdges() { + edges.clear(); + } + + @Override + public void takeValue(Property p) { + if (p instanceof EdgeListProperty) { + EdgeListProperty e = (EdgeListProperty) p; + if (e.isDefined()) { + setEdges(e.getValue()); + } else { + undefine(); + } + } else { + throw new IllegalArgumentException(this + " cannot take value from " + p); + } + } + + @Override + public void write(OutputStream out) throws IOException { + writeInt32(edges.size(), out); + for (Edge next : edges) { + writeInt32(next.getStartX(), out); + writeInt32(next.getStartY(), out); + writeInt32(next.getEndX(), out); + writeInt32(next.getEndY(), out); + if (next.isPassable()) { + writeInt32(next.getNeighbour().getValue(), out); + } else { + writeInt32(0, out); + } + } + } + + @Override + public void read(InputStream in) throws IOException { + int count = readInt32(in); + edges.clear(); + for (int i = 0; i < count; ++i) { + int startX = readInt32(in); + int startY = readInt32(in); + int endX = readInt32(in); + int endY = readInt32(in); + EntityID neighbour = null; + int id = readInt32(in); + if (id != 0) { + neighbour = new EntityID(id); + } + edges.add(new Edge(startX, startY, endX, endY, neighbour)); + } + setDefined(); + } + + @Override + public EdgeListProperty copy() { + return new EdgeListProperty(this); + } + + @Override + public PropertyProto toPropertyProto() { + PropertyProto.Builder builder = basePropertyProto(); + if (isDefined()) { + EdgeListProto.Builder edgeListbuilder = EdgeListProto.newBuilder(); + for (Edge next : edges) { + int neighbour = 0; + if (next.isPassable()) { + neighbour = next.getNeighbour().getValue(); + } + edgeListbuilder.addEdges(EdgeProto.newBuilder().setStartX(next.getStartX()).setStartY(next.getStartY()) + .setEndX(next.getEndX()).setEndY(next.getEndY()).setNeighbour(neighbour)); + } + builder.setEdgeList(edgeListbuilder); + } + return builder.build(); + } + + @Override + public void fromPropertyProto(PropertyProto proto) { + if (!proto.getDefined()) + return; + edges.clear(); + List<EdgeProto> edgesProto = proto.getEdgeList().getEdgesList(); + for (EdgeProto edgeProto : edgesProto) { + int startX = edgeProto.getStartX(); + int startY = edgeProto.getStartY(); + int endX = edgeProto.getEndX(); + int endY = edgeProto.getEndY(); + EntityID neighbour = null; + int id = edgeProto.getNeighbour(); + if (id != 0) { + neighbour = new EntityID(id); + } + edges.add(new Edge(startX, startY, endX, endY, neighbour)); + + } + setDefined(); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/FireBrigade.java b/modules/standard/src/rescuecore2/standard/entities/FireBrigade.java new file mode 100644 index 0000000000000000000000000000000000000000..156b805b4c0263b31fc63347732c24b0f7f477ec --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/FireBrigade.java @@ -0,0 +1,124 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * The FireBrigade object. + */ +public class FireBrigade extends Human { + + private IntProperty water; + + + /** + * Construct a FireBrigade object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public FireBrigade( EntityID id ) { + super( id ); + water = new IntProperty( StandardPropertyURN.WATER_QUANTITY ); + registerProperties( water ); + } + + + /** + * FireBrigade copy constructor. + * + * @param other + * The FireBrigade to copy. + */ + public FireBrigade( FireBrigade other ) { + super( other ); + water = new IntProperty( other.water ); + registerProperties( water ); + } + + + @Override + protected Entity copyImpl() { + return new FireBrigade( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.FIRE_BRIGADE; + } + + + @Override + public Property getProperty( int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case WATER_QUANTITY: + return water; + default: + return super.getProperty( urn ); + } + } + + + /** + * Get the water property. + * + * @return The water property. + */ + public IntProperty getWaterProperty() { + return water; + } + + + /** + * Get the amount of water this fire brigade is carrying. + * + * @return The water. + */ + public int getWater() { + return water.getValue(); + } + + + /** + * Set the amount of water this fire brigade is carrying. + * + * @param water + * The new amount of water. + */ + public void setWater( int water ) { + this.water.setValue( water ); + } + + + /** + * Find out if the water property has been defined. + * + * @return True if the water property has been defined, false otherwise. + */ + public boolean isWaterDefined() { + return water.isDefined(); + } + + + /** + * Undefine the water property. + */ + public void undefineWater() { + water.undefine(); + } + + + @Override + protected String getEntityName() { + return "Fire brigade"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/FireStation.java b/modules/standard/src/rescuecore2/standard/entities/FireStation.java new file mode 100644 index 0000000000000000000000000000000000000000..80018d4e74ef2385d3d5f89c86eb3f9b9b157e28 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/FireStation.java @@ -0,0 +1,60 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The FireStation object. + */ +public class FireStation extends Building { + + /** + * Construct a FireStation object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public FireStation( EntityID id ) { + super( id ); + } + + + /** + * FireStation copy constructor. + * + * @param other + * The FireStation to copy. + */ + public FireStation( FireStation other ) { + super( other ); + } + + + /** + * Create a fire station based on another Building. + * + * @param other + * The Building to copy. + */ + public FireStation( Building other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new FireStation( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.FIRE_STATION; + } + + + @Override + protected String getEntityName() { + return "Fire station"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/GasStation.java b/modules/standard/src/rescuecore2/standard/entities/GasStation.java new file mode 100644 index 0000000000000000000000000000000000000000..d0fb6a4b88179188426a670d18cd79cd83acc3e6 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/GasStation.java @@ -0,0 +1,41 @@ +package rescuecore2.standard.entities; + +import org.json.JSONObject; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +public class GasStation extends Building { + + public GasStation( Building other ) { + super( other ); + } + + + public GasStation( EntityID entityID ) { + super( entityID ); + } + + + @Override + protected Entity copyImpl() { + return new GasStation( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.GAS_STATION; + } + + + @Override + protected String getEntityName() { + return "Gas Station"; + } + + + @Override + public JSONObject toJson() { + return super.toJson(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Human.java b/modules/standard/src/rescuecore2/standard/entities/Human.java new file mode 100644 index 0000000000000000000000000000000000000000..2fe02583e3732a523d57f60fa6a95f6cb2434fd8 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Human.java @@ -0,0 +1,669 @@ +package rescuecore2.standard.entities; + +import org.json.JSONObject; +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.properties.EntityRefProperty; +import rescuecore2.worldmodel.properties.IntArrayProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * Abstract base class for Humans. + */ +public abstract class Human extends StandardEntity { + + private IntProperty x; + private IntProperty y; + private EntityRefProperty position; + private IntArrayProperty positionHistory; + private IntProperty travelDistance; + private IntProperty direction; + private IntProperty stamina; + private IntProperty hp; + private IntProperty damage; + private IntProperty buriedness; + + + /** + * Construct a Human object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + protected Human( EntityID id ) { + super( id ); + x = new IntProperty( StandardPropertyURN.X ); + y = new IntProperty( StandardPropertyURN.Y ); + travelDistance = new IntProperty( StandardPropertyURN.TRAVEL_DISTANCE ); + position = new EntityRefProperty( StandardPropertyURN.POSITION ); + positionHistory = new IntArrayProperty( + StandardPropertyURN.POSITION_HISTORY ); + direction = new IntProperty( StandardPropertyURN.DIRECTION ); + stamina = new IntProperty( StandardPropertyURN.STAMINA ); + hp = new IntProperty( StandardPropertyURN.HP ); + damage = new IntProperty( StandardPropertyURN.DAMAGE ); + buriedness = new IntProperty( StandardPropertyURN.BURIEDNESS ); + registerProperties( x, y, position, positionHistory, travelDistance, + direction, stamina, hp, damage, buriedness ); + } + + + /** + * Human copy constructor. + * + * @param other + * The Human to copy. + */ + public Human( Human other ) { + super( other ); + x = new IntProperty( other.x ); + y = new IntProperty( other.y ); + travelDistance = new IntProperty( other.travelDistance ); + position = new EntityRefProperty( other.position ); + positionHistory = new IntArrayProperty( other.positionHistory ); + direction = new IntProperty( other.direction ); + stamina = new IntProperty( other.stamina ); + hp = new IntProperty( other.hp ); + damage = new IntProperty( other.damage ); + buriedness = new IntProperty( other.buriedness ); + registerProperties( x, y, position, positionHistory, travelDistance, + direction, stamina, hp, damage, buriedness ); + } + + + @Override + public Property getProperty( int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case POSITION: + return position; + case POSITION_HISTORY: + return positionHistory; + case DIRECTION: + return direction; + case STAMINA: + return stamina; + case HP: + return hp; + case X: + return x; + case Y: + return y; + case DAMAGE: + return damage; + case BURIEDNESS: + return buriedness; + case TRAVEL_DISTANCE: + return travelDistance; + default: + return super.getProperty( urn ); + } + } + + + @Override + public Pair<Integer, Integer> + getLocation( WorldModel<? extends StandardEntity> world ) { + if ( x.isDefined() && y.isDefined() ) { + return new Pair<Integer, Integer>( x.getValue(), y.getValue() ); + } + if ( position.isDefined() ) { + EntityID pos = getPosition(); + StandardEntity e = world.getEntity( pos ); + return e.getLocation( world ); + } + return null; + } + + + /** + * Get the position property. + * + * @return The position property. + */ + public EntityRefProperty getPositionProperty() { + return position; + } + + + /** + * Get the position of this human. + * + * @return The position. + */ + public EntityID getPosition() { + return position.getValue(); + } + + + /** + * Set the position of this human. + * + * @param position + * The new position. + */ + public void setPosition( EntityID position ) { + this.position.setValue( position ); + } + + + /** + * Find out if the position property has been defined. + * + * @return True if the position property has been defined, false otherwise. + */ + public boolean isPositionDefined() { + return position.isDefined(); + } + + + /** + * Undefine the position property. + */ + public void undefinePosition() { + position.undefine(); + } + + + /** + * Get the position history property. + * + * @return The position history property. + */ + public IntArrayProperty getPositionHistoryProperty() { + return positionHistory; + } + + + /** + * Get the position history. + * + * @return The position history. + */ + public int[] getPositionHistory() { + return positionHistory.getValue(); + } + + + /** + * Set the position history. + * + * @param history + * The new position history. + */ + public void setPositionHistory( int[] history ) { + this.positionHistory.setValue( history ); + } + + + /** + * Find out if the position history property has been defined. + * + * @return True if the position history property has been defined, false + * otherwise. + */ + public boolean isPositionHistoryDefined() { + return positionHistory.isDefined(); + } + + + /** + * Undefine the position history property. + */ + public void undefinePositionHistory() { + positionHistory.undefine(); + } + + + /** + * Get the direction property. + * + * @return The direction property. + */ + public IntProperty getDirectionProperty() { + return direction; + } + + + /** + * Get the direction. + * + * @return The direction. + */ + public int getDirection() { + return direction.getValue(); + } + + + /** + * Set the direction. + * + * @param direction + * The new direction. + */ + public void setDirection( int direction ) { + this.direction.setValue( direction ); + } + + + /** + * Find out if the direction property has been defined. + * + * @return True if the direction property has been defined, false otherwise. + */ + public boolean isDirectionDefined() { + return direction.isDefined(); + } + + + /** + * Undefine the direction property. + */ + public void undefineDirection() { + direction.undefine(); + } + + + /** + * Get the stamina property. + * + * @return The stamina property. + */ + public IntProperty getStaminaProperty() { + return stamina; + } + + + /** + * Get the stamina of this human. + * + * @return The stamina. + */ + public int getStamina() { + return stamina.getValue(); + } + + + /** + * Set the stamina of this human. + * + * @param stamina + * The new stamina. + */ + public void setStamina( int stamina ) { + this.stamina.setValue( stamina ); + } + + + /** + * Find out if the stamina property has been defined. + * + * @return True if the stamina property has been defined, false otherwise. + */ + public boolean isStaminaDefined() { + return stamina.isDefined(); + } + + + /** + * Undefine the stamina property. + */ + public void undefineStamina() { + stamina.undefine(); + } + + + /** + * Get the hp property. + * + * @return The hp property. + */ + public IntProperty getHPProperty() { + return hp; + } + + + /** + * Get the hp of this human. + * + * @return The hp of this human. + */ + public int getHP() { + return hp.getValue(); + } + + + /** + * Set the hp of this human. + * + * @param newHP + * The new hp. + */ + public void setHP( int newHP ) { + this.hp.setValue( newHP ); + } + + + /** + * Find out if the hp property has been defined. + * + * @return True if the hp property has been defined, false otherwise. + */ + public boolean isHPDefined() { + return hp.isDefined(); + } + + + /** + * Undefine the hp property. + */ + public void undefineHP() { + hp.undefine(); + } + + + /** + * Get the damage property. + * + * @return The damage property. + */ + public IntProperty getDamageProperty() { + return damage; + } + + + /** + * Get the damage of this human. + * + * @return The damage of this human. + */ + public int getDamage() { + return damage.getValue(); + } + + + /** + * Set the damage of this human. + * + * @param damage + * The new damage. + */ + public void setDamage( int damage ) { + this.damage.setValue( damage ); + } + + + /** + * Find out if the damage property has been defined. + * + * @return True if the damage property has been defined, false otherwise. + */ + public boolean isDamageDefined() { + return damage.isDefined(); + } + + + /** + * Undefine the damage property. + */ + public void undefineDamage() { + damage.undefine(); + } + + + /** + * Get the buriedness property. + * + * @return The buriedness property. + */ + public IntProperty getBuriednessProperty() { + return buriedness; + } + + + /** + * Get the buriedness of this human. + * + * @return The buriedness of this human. + */ + public int getBuriedness() { + return buriedness.getValue(); + } + + + /** + * Set the buriedness of this human. + * + * @param buriedness + * The new buriedness. + */ + public void setBuriedness( int buriedness ) { + this.buriedness.setValue( buriedness ); + } + + + /** + * Find out if the buriedness property has been defined. + * + * @return True if the buriedness property has been defined, false otherwise. + */ + public boolean isBuriednessDefined() { + return buriedness.isDefined(); + } + + + /** + * Undefine the buriedness property. + */ + public void undefineBuriedness() { + buriedness.undefine(); + } + + + /** + * Get the X property. + * + * @return The X property. + */ + public IntProperty getXProperty() { + return x; + } + + + /** + * Get the X coordinate of this human. + * + * @return The x coordinate of this human. + */ + public int getX() { + return x.getValue(); + } + + + /** + * Set the X coordinate of this human. + * + * @param x + * The new x coordinate. + */ + public void setX( int x ) { + this.x.setValue( x ); + } + + + /** + * Find out if the x property has been defined. + * + * @return True if the x property has been defined, false otherwise. + */ + public boolean isXDefined() { + return x.isDefined(); + } + + + /** + * Undefine the X property. + */ + public void undefineX() { + x.undefine(); + } + + + /** + * Get the y property. + * + * @return The y property. + */ + public IntProperty getYProperty() { + return y; + } + + + /** + * Get the y coordinate of this human. + * + * @return The y coordinate of this human. + */ + public int getY() { + return y.getValue(); + } + + + /** + * Set the y coordinate of this human. + * + * @param y + * The new y coordinate. + */ + public void setY( int y ) { + this.y.setValue( y ); + } + + + /** + * Find out if the y property has been defined. + * + * @return True if the y property has been defined, false otherwise. + */ + public boolean isYDefined() { + return y.isDefined(); + } + + + /** + * Undefine the y property. + */ + public void undefineY() { + y.undefine(); + } + + + /** + * Get the travel distance property. + * + * @return The travel distance property. + */ + public IntProperty getTravelDistanceProperty() { + return travelDistance; + } + + + /** + * Get the travel distance. + * + * @return The travel distance. + */ + public int getTravelDistance() { + return travelDistance.getValue(); + } + + + /** + * Set the travel distance. + * + * @param d + * The new travel distance. + */ + public void setTravelDistance( int d ) { + this.travelDistance.setValue( d ); + } + + + /** + * Find out if the travel distance property has been defined. + * + * @return True if the travel distance property has been defined, false + * otherwise. + */ + public boolean isTravelDistanceDefined() { + return travelDistance.isDefined(); + } + + + /** + * Undefine the travel distance property. + */ + public void undefineTravelDistance() { + travelDistance.undefine(); + } + + + /** + * Get the entity represented by the position property. The result will be + * null if the position property has not been set or if the entity reference + * is invalid. + * + * @param model + * The WorldModel to look up entity references. + * @return The entity represented by the position property. + */ + public StandardEntity + getPosition( WorldModel<? extends StandardEntity> model ) { + if ( !position.isDefined() ) { + return null; + } + return model.getEntity( position.getValue() ); + } + + + /** + * Set the position of this human. + * + * @param newPosition + * The new position. + * @param newX + * The x coordinate of this agent. + * @param newYStandardPropertyURN + * The y coordinate if this agent. + */ + public void setPosition( EntityID newPosition, int newX, int newY ) { + this.position.setValue( newPosition ); + this.x.setValue( newX ); + this.y.setValue( newY ); + } + + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put( StandardPropertyURN.DAMAGE.toString(), + this.isDamageDefined() ? this.getDamage() : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.HP.toString(), + this.isHPDefined() ? this.getHP() : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.POSITION.toString(), + this.isPositionDefined() ? new int[]{ getX(), getY() } + : JSONObject.NULL ); + jsonObject.put( StandardPropertyURN.POSITION_HISTORY.toString(), + this.isPositionHistoryDefined() ? this.getPositionHistory() + : JSONObject.NULL ); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Hydrant.java b/modules/standard/src/rescuecore2/standard/entities/Hydrant.java new file mode 100644 index 0000000000000000000000000000000000000000..5271761b492a61d87b54f14aff09cd023d22d7f4 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Hydrant.java @@ -0,0 +1,45 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +public class Hydrant extends Road { + + public Hydrant( EntityID id ) { + super( id ); + } + + + public Hydrant( Hydrant other ) { + super( other ); + } + + + /** + * Create a Hydrant based on another Road. + * + * @param other + * The Road to copy. + */ + public Hydrant( Road other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new Hydrant( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.HYDRANT; + } + + + @Override + protected String getEntityName() { + return "Hydrant"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/PoliceForce.java b/modules/standard/src/rescuecore2/standard/entities/PoliceForce.java new file mode 100644 index 0000000000000000000000000000000000000000..bd2ead60b07c7d545aba13346459a58a2fd5342c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/PoliceForce.java @@ -0,0 +1,50 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The PoliceForce object. + */ +public class PoliceForce extends Human { + + /** + * Construct a PoliceForce object with entirely undefined values. + * + * @param id + * The ID of this entity. + */ + public PoliceForce( EntityID id ) { + super( id ); + } + + + /** + * PoliceForce copy constructor. + * + * @param other + * The PoliceForce to copy. + */ + public PoliceForce( PoliceForce other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new PoliceForce( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.POLICE_FORCE; + } + + + @Override + protected String getEntityName() { + return "Police force"; + } + +} diff --git a/modules/standard/src/rescuecore2/standard/entities/PoliceOffice.java b/modules/standard/src/rescuecore2/standard/entities/PoliceOffice.java new file mode 100644 index 0000000000000000000000000000000000000000..00158c72825363e4ad15782085dbac3f4e2040a5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/PoliceOffice.java @@ -0,0 +1,60 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The PoliceOffice object. + */ +public class PoliceOffice extends Building { + + /** + * Construct a PoliceOffice object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + public PoliceOffice( EntityID id ) { + super( id ); + } + + + /** + * PoliceOffice copy constructor. + * + * @param other + * The PoliceOffice to copy. + */ + public PoliceOffice( PoliceOffice other ) { + super( other ); + } + + + /** + * Create a police office based on another Building. + * + * @param other + * The Building to copy. + */ + public PoliceOffice( Building other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new PoliceOffice( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.POLICE_OFFICE; + } + + + @Override + protected String getEntityName() { + return "Police office"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Refuge.java b/modules/standard/src/rescuecore2/standard/entities/Refuge.java new file mode 100755 index 0000000000000000000000000000000000000000..dcdaefd6f7374a4006c227f3968926302d7b823b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Refuge.java @@ -0,0 +1,296 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * The Refuge object. + */ +/* + * Implementation of Refuge Bed Capacity + * + * @author Farshid Faraji May 2020 During Covid-19 :-))) + */ +public class Refuge extends Building { + + private IntProperty bedCapacity; + private IntProperty occupiedBeds; + private IntProperty refillCapacity; + private IntProperty waitingListSize; + + /** + * Construct a Refuge object with entirely undefined values. + * + * @param id The ID of this entity. + */ + public Refuge(EntityID id) { + super(id); + bedCapacity = new IntProperty(StandardPropertyURN.BED_CAPACITY); + occupiedBeds = new IntProperty(StandardPropertyURN.OCCUPIED_BEDS); + refillCapacity = new IntProperty(StandardPropertyURN.REFILL_CAPACITY); + waitingListSize = new IntProperty(StandardPropertyURN.WAITING_LIST_SIZE); + registerProperties(bedCapacity, occupiedBeds, refillCapacity, waitingListSize); + } + + /** + * Refuge copy constructor. + * + * @param other The Refuge to copy. + */ + public Refuge(Refuge other) { + super(other); + bedCapacity = new IntProperty(StandardPropertyURN.BED_CAPACITY); + occupiedBeds = new IntProperty(StandardPropertyURN.OCCUPIED_BEDS); + refillCapacity = new IntProperty(StandardPropertyURN.REFILL_CAPACITY); + waitingListSize = new IntProperty(StandardPropertyURN.WAITING_LIST_SIZE); + registerProperties(bedCapacity, occupiedBeds, refillCapacity, waitingListSize); + } + + /** + * Create a refuge based on another Building. + * + * @param other The Building to copy. + */ + public Refuge(Building other) { + super(other); + bedCapacity = new IntProperty(StandardPropertyURN.BED_CAPACITY); + occupiedBeds = new IntProperty(StandardPropertyURN.OCCUPIED_BEDS); + refillCapacity = new IntProperty(StandardPropertyURN.REFILL_CAPACITY); + waitingListSize = new IntProperty(StandardPropertyURN.WAITING_LIST_SIZE); + registerProperties(bedCapacity, occupiedBeds, refillCapacity, waitingListSize); + } + + @Override + protected Entity copyImpl() { + return new Refuge(getID()); + } + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.REFUGE; + } + + @Override + protected String getEntityName() { + return "Refuge"; + } + + @Override + public Property getProperty(int urn) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt(urn); + } catch (IllegalArgumentException e) { + return super.getProperty(urn); + } + switch (type) { + case BED_CAPACITY: + return bedCapacity; + case OCCUPIED_BEDS: + return occupiedBeds; + case REFILL_CAPACITY: + return refillCapacity; + case WAITING_LIST_SIZE: + return waitingListSize; + default: + return super.getProperty(urn); + } + } + + /** + * Get the occupiedBeds property. + * + * @return The occupiedBeds property. + */ + public IntProperty getOccupiedBedsProperty() { + return occupiedBeds; + } + + /** + * Get the occupiedBeds of this refuge. + * + * @return The occupiedBeds. + */ + public int getOccupiedBeds() { + return occupiedBeds.getValue(); + } + + /** + * Set the occupiedBeds of this refuge. + * + * @param capacity The new occupiedBeds. + */ + public void setOccupiedBeds(int capacity) { + this.occupiedBeds.setValue(capacity); + } + + /** + * Increase the occupiedBeds of this refuge by one. + */ + public int increaseOccupiedBeds() { + if (occupiedBeds.getValue() < bedCapacity.getValue()) { + occupiedBeds.setValue(occupiedBeds.getValue() + 1); + } + return occupiedBeds.getValue(); + } + + /** + * Decrease the occupiedBeds of this refuge by one. + */ + public int decreaseOccupiedBeds() { + if (occupiedBeds.getValue() > 0) { + occupiedBeds.setValue(occupiedBeds.getValue() - 1); + } + return occupiedBeds.getValue(); + } + + /** + * Find out if the occupiedBeds property has been defined. + * + * @return True if the occupiedBeds property has been defined, false otherwise. + */ + public boolean isOccupiedBedsDefined() { + return occupiedBeds.isDefined(); + } + + /** + * Undefine the occupiedBeds property. + */ + public void undefineOccupiedBeds() { + occupiedBeds.undefine(); + } + + /** + * Get the bedCapacity property. + * + * @return The bedCapacity property. + */ + public IntProperty getBedCapacityProperty() { + return bedCapacity; + } + + /** + * Get the bedCapacity of this refuge. + * + * @return The bedCapacity. + */ + public int getBedCapacity() { + return bedCapacity.getValue(); + } + + /** + * Set the bedCapacity of this refuge. + * + * @param capacity The new bedCapacity. + */ + public void setBedCapacity(int capacity) { + // if((Object) capacity != null) + this.bedCapacity.setValue(capacity); + } + + /** + * Find out if the bedCapacity property has been defined. + * + * @return True if the bedCapacity property has been defined, false otherwise. + */ + public boolean isBedCapacityDefined() { + return bedCapacity.isDefined(); + } + + /** + * Undefine the bedCapacity property. + */ + public void undefineBedCapacity() { + bedCapacity.undefine(); + } + + /** + * Get the refillCapacity property. + * + * @return The refillCapacity property. + */ + public IntProperty getRefillCapacityProperty() { + return refillCapacity; + } + + /** + * Get the refillCapacity of this refuge. + * + * @return The refillCapacity. + */ + public int getRefillCapacity() { + return refillCapacity.getValue(); + } + + /** + * Set the refillCapacity of this refuge. + * + * @param capacity The new refillCapacity. + */ + public void setRefillCapacity(int capacity) { + this.refillCapacity.setValue(capacity); + } + + /** + * Find out if the refillCapacity property has been defined. + * + * @return True if the refillCapacity property has been defined, false + * otherwise. + */ + public boolean isRefillCapacityDefined() { + return refillCapacity.isDefined(); + } + + /** + * Undefine the refillCapacity property. + */ + public void undefineRefillCapacity() { + refillCapacity.undefine(); + } + + /** + * Get the waitingListSize property. + * + * @return The waitingListSize property. + */ + public IntProperty getWaitingListSizeProperty() { + return waitingListSize; + } + + /** + * Get the waitingListSize of this refuge. + * + * @return The waitingListSize. + */ + public int getWaitingListSize() { + return waitingListSize.getValue(); + } + + /** + * Set the waitingListSize of this refuge. + * + * @param size The new waitingList. + */ + public void setWaitingListSize(int size) { + this.waitingListSize.setValue(size); + } + + /** + * Find out if the waitingListSize property has been defined. + * + * @return True if the waitingListSize property has been defined, false + * otherwise. + */ + public boolean isWaitingListSizeDefined() { + return waitingListSize.isDefined(); + } + + /** + * Undefine the waitingListSize property. + */ + public void undefineWaitingListSize() { + waitingListSize.undefine(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/Road.java b/modules/standard/src/rescuecore2/standard/entities/Road.java new file mode 100644 index 0000000000000000000000000000000000000000..0977460fe68dd3a96a1cea71090a343d4c8dfdd2 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/Road.java @@ -0,0 +1,49 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; + +/** + * The Road object. + */ +public class Road extends Area { + + /** + * Construct a Road object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + public Road( EntityID id ) { + super( id ); + } + + + /** + * Road copy constructor. + * + * @param other + * The Road to copy. + */ + public Road( Road other ) { + super( other ); + } + + + @Override + protected Entity copyImpl() { + return new Road( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.ROAD; + } + + + @Override + protected String getEntityName() { + return "Road"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardEntity.java b/modules/standard/src/rescuecore2/standard/entities/StandardEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..2da09571b582bd9ec63dd4d6ad50f384efce3e4c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardEntity.java @@ -0,0 +1,63 @@ +package rescuecore2.standard.entities; + +import org.json.JSONObject; + +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.AbstractEntity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; + +/** + * Abstract base class for all standard entities. + */ +public abstract class StandardEntity extends AbstractEntity { + + /** + * Construct a StandardEntity with entirely undefined property values. + * + * @param id The ID of this entity. + */ + protected StandardEntity(EntityID id) { + super(id); + } + + /** + * StandardEntity copy constructor. + * + * @param other The StandardEntity to copy. + */ + protected StandardEntity(StandardEntity other) { + super(other); + } + + /** + * Get the location of this entity. + * + * @param world The world model to look up for entity references. + * @return The coordinates of this entity, or null if the location cannot be + * determined. + */ + public Pair<Integer, Integer> getLocation(WorldModel<? extends StandardEntity> world) { + return null; + } + + /** + * Get the URN of this entity type as an instanceof StandardEntityURN. + * + * @return A StandardEntityURN. + */ + public abstract StandardEntityURN getStandardURN(); + + @Override + public final int getURN() { + return getStandardURN().getURNId(); + } + + @Override + public JSONObject toJson() { + JSONObject json = new JSONObject(); + json.put("Id", getID()); + json.put("EntityName", this.getEntityName()); + return json; + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardEntityConstants.java b/modules/standard/src/rescuecore2/standard/entities/StandardEntityConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..12ca139ffa20e9ba4b2f407d78f49f152aafb460 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardEntityConstants.java @@ -0,0 +1,46 @@ +package rescuecore2.standard.entities; + +/** + * Constants for the standard entity package. + */ +public final class StandardEntityConstants { + + /** + * Enum defining building codes. + */ + public enum BuildingCode { + /** Wooden construction. */ + WOOD, + /** Steel frame construction. */ + STEEL, + /** Reinforced concrete construction. */ + CONCRETE; + } + + /** + * Enum defining different levels of fieryness. + */ + public enum Fieryness { + /** Not burnt at all. */ + UNBURNT, + /** On fire a bit. */ + HEATING, + /** On fire a bit more. */ + BURNING, + /** On fire a lot. */ + INFERNO, + /** Not burnt at all, but has water damage. */ + WATER_DAMAGE, + /** Extinguished but minor damage. */ + MINOR_DAMAGE, + /** Extinguished but moderate damage. */ + MODERATE_DAMAGE, + /** Extinguished but major damage. */ + SEVERE_DAMAGE, + /** Completely burnt out. */ + BURNT_OUT; + } + + private StandardEntityConstants() { + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardEntityFactory.java b/modules/standard/src/rescuecore2/standard/entities/StandardEntityFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2467fb57005aea7bbebbb7520c6c120b886823e6 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardEntityFactory.java @@ -0,0 +1,58 @@ +package rescuecore2.standard.entities; + +import rescuecore2.registry.AbstractEntityFactory; +import rescuecore2.worldmodel.EntityID; + +/** + * EntityFactory that builds standard Robocup Standard objects. + */ +public final class StandardEntityFactory extends AbstractEntityFactory<StandardEntityURN> { + + /** + * Singleton class. Use this instance to do stuff. + */ + public static final StandardEntityFactory INSTANCE = new StandardEntityFactory(); + + /** + * Singleton class: private constructor. + */ + private StandardEntityFactory() { + super(StandardEntityURN.class); + } + + @Override + public StandardEntity makeEntity(StandardEntityURN urn, EntityID id) { + switch (urn) { + case WORLD: + return new World(id); + case ROAD: + return new Road(id); + case BUILDING: + return new Building(id); + case BLOCKADE: + return new Blockade(id); + case REFUGE: + return new Refuge(id); + case HYDRANT: + return new Hydrant(id); + case GAS_STATION: + return new GasStation(id); + case FIRE_STATION: + return new FireStation(id); + case AMBULANCE_CENTRE: + return new AmbulanceCentre(id); + case POLICE_OFFICE: + return new PoliceOffice(id); + case CIVILIAN: + return new Civilian(id); + case FIRE_BRIGADE: + return new FireBrigade(id); + case AMBULANCE_TEAM: + return new AmbulanceTeam(id); + case POLICE_FORCE: + return new PoliceForce(id); + default: + throw new IllegalArgumentException("Unrecognised entity urn: " + urn); + } + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardEntityURN.java b/modules/standard/src/rescuecore2/standard/entities/StandardEntityURN.java new file mode 100644 index 0000000000000000000000000000000000000000..947030ef832370b3e397dd1c59b4ca1ee61f7fa6 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardEntityURN.java @@ -0,0 +1,70 @@ +package rescuecore2.standard.entities; + +import static rescuecore2.standard.Constants.ENTITY_URN_PREFIX; +import static rescuecore2.standard.Constants.ENTITY_URN_PREFIX_STR; + +import java.util.Map; + +import rescuecore2.URN; + +/** + * URNs for standard entities. + */ +public enum StandardEntityURN implements URN { + /** World entity */ + WORLD(ENTITY_URN_PREFIX | 1, ENTITY_URN_PREFIX_STR + "world"), + /** Road entity */ + ROAD(ENTITY_URN_PREFIX | 2, ENTITY_URN_PREFIX_STR + "road"), + /** Blockade entity */ + BLOCKADE(ENTITY_URN_PREFIX | 3, ENTITY_URN_PREFIX_STR + "blockade"), + /** Building entity */ + BUILDING(ENTITY_URN_PREFIX | 4, ENTITY_URN_PREFIX_STR + "building"), + /** Refuge entity */ + REFUGE(ENTITY_URN_PREFIX | 5, ENTITY_URN_PREFIX_STR + "refuge"), + /** Hydrant entity */ + HYDRANT(ENTITY_URN_PREFIX | 6, ENTITY_URN_PREFIX_STR + "hydrant"), + /** Gas Station entity */ + GAS_STATION(ENTITY_URN_PREFIX | 7, ENTITY_URN_PREFIX_STR + "gasstation"), + /** Fire Station entity */ + FIRE_STATION(ENTITY_URN_PREFIX | 8, ENTITY_URN_PREFIX_STR + "firestation"), + /** Ambulance Centre entity */ + AMBULANCE_CENTRE(ENTITY_URN_PREFIX | 9, ENTITY_URN_PREFIX_STR + "ambulancecentre"), + /** Police Office entity */ + POLICE_OFFICE(ENTITY_URN_PREFIX | 10, ENTITY_URN_PREFIX_STR + "policeoffice"), + /** Civilian entity */ + CIVILIAN(ENTITY_URN_PREFIX | 11, ENTITY_URN_PREFIX_STR + "civilian"), + /** Fire Brigade entity */ + FIRE_BRIGADE(ENTITY_URN_PREFIX | 12, ENTITY_URN_PREFIX_STR + "firebrigade"), + /** Ambulance Team entity */ + AMBULANCE_TEAM(ENTITY_URN_PREFIX | 13, ENTITY_URN_PREFIX_STR + "ambulanceteam"), + /** Police Force entity */ + POLICE_FORCE(ENTITY_URN_PREFIX | 14, ENTITY_URN_PREFIX_STR + "policeforce"); + + private int urnId; + private String urnStr; + public static final Map<Integer, StandardEntityURN> MAP = URN.generateMap(StandardEntityURN.class); + public static final Map<String, StandardEntityURN> MAPSTR = URN.generateMapStr(StandardEntityURN.class); + + private StandardEntityURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return this.urnId; + } + + @Override + public String getURNStr() { + return this.urnStr; + } + + public static StandardEntityURN fromInt(int urn) { + return MAP.get(urn); + } + + public static StandardEntityURN fromString(String urn) { + return MAPSTR.get(urn); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardPropertyFactory.java b/modules/standard/src/rescuecore2/standard/entities/StandardPropertyFactory.java new file mode 100755 index 0000000000000000000000000000000000000000..ac7d58fbd0188e819fce24257c5079d8d33494af --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardPropertyFactory.java @@ -0,0 +1,76 @@ +package rescuecore2.standard.entities; + +import rescuecore2.registry.AbstractPropertyFactory; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.properties.BooleanProperty; +import rescuecore2.worldmodel.properties.EntityRefListProperty; +import rescuecore2.worldmodel.properties.EntityRefProperty; +import rescuecore2.worldmodel.properties.IntArrayProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * PropertyFactory that builds standard Robocup Standard properties. + */ +public final class StandardPropertyFactory extends AbstractPropertyFactory<StandardPropertyURN> { + + /** + * Singleton class. Use this instance to do stuff. + */ + public static final StandardPropertyFactory INSTANCE = new StandardPropertyFactory(); + + /** + * Singleton class: private constructor. + */ + private StandardPropertyFactory() { + super(StandardPropertyURN.class); + } + + @Override + public Property makeProperty(StandardPropertyURN urn) { + switch (urn) { + case START_TIME: + case LONGITUDE: + case LATITUDE: + case WIND_FORCE: + case WIND_DIRECTION: + case X: + case Y: + case FLOORS: + case BUILDING_ATTRIBUTES: + case FIERYNESS: + case BROKENNESS: + case BUILDING_CODE: + case BUILDING_AREA_GROUND: + case BUILDING_AREA_TOTAL: + case DIRECTION: + case STAMINA: + case HP: + case DAMAGE: + case BURIEDNESS: + case WATER_QUANTITY: + case TEMPERATURE: + case IMPORTANCE: + case TRAVEL_DISTANCE: + case REPAIR_COST: + case CAPACITY: + case BED_CAPACITY: + case REFILL_CAPACITY: + case OCCUPIED_BEDS: + case WAITING_LIST_SIZE: + return new IntProperty(urn); + case APEXES: + case POSITION_HISTORY: + return new IntArrayProperty(urn); + case IGNITION: + return new BooleanProperty(urn); + case POSITION: + return new EntityRefProperty(urn); + case BLOCKADES: + return new EntityRefListProperty(urn); + case EDGES: + return new EdgeListProperty(urn); + default: + throw new IllegalArgumentException("Unrecognised property urn: " + urn); + } + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardPropertyURN.java b/modules/standard/src/rescuecore2/standard/entities/StandardPropertyURN.java new file mode 100755 index 0000000000000000000000000000000000000000..afcb8285eb5e50fdb4fa07933ebd5f42b31cf180 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardPropertyURN.java @@ -0,0 +1,118 @@ +package rescuecore2.standard.entities; + +import static rescuecore2.standard.Constants.PROPERTY_URN_PREFIX; +import static rescuecore2.standard.Constants.PROPERTY_URN_PREFIX_STR; + +import java.util.Map; + +import rescuecore2.URN;; + +/** + * URNs for standard property types. + */ +public enum StandardPropertyURN implements URN { + /** Start Time property */ + START_TIME(PROPERTY_URN_PREFIX | 1, PROPERTY_URN_PREFIX_STR + "starttime"), + /** Longitude property */ + LONGITUDE(PROPERTY_URN_PREFIX | 2, PROPERTY_URN_PREFIX_STR + "longitude"), + /** Latitude property */ + LATITUDE(PROPERTY_URN_PREFIX | 3, PROPERTY_URN_PREFIX_STR + "latitude"), + /** Wind Force property */ + WIND_FORCE(PROPERTY_URN_PREFIX | 4, PROPERTY_URN_PREFIX_STR + "windforce"), + /** Wind Direction property */ + WIND_DIRECTION(PROPERTY_URN_PREFIX | 5, PROPERTY_URN_PREFIX_STR + "winddirection"), + /** X property */ + X(PROPERTY_URN_PREFIX | 6, PROPERTY_URN_PREFIX_STR + "x"), + /** Y property */ + Y(PROPERTY_URN_PREFIX | 7, PROPERTY_URN_PREFIX_STR + "y"), + /** Blockades property */ + BLOCKADES(PROPERTY_URN_PREFIX | 8, PROPERTY_URN_PREFIX_STR + "blockades"), + /** Repair Cost property */ + REPAIR_COST(PROPERTY_URN_PREFIX | 9, PROPERTY_URN_PREFIX_STR + "repaircost"), + /** Floors property */ + FLOORS(PROPERTY_URN_PREFIX | 10, PROPERTY_URN_PREFIX_STR + "floors"), + /** Building Attributes property */ + BUILDING_ATTRIBUTES(PROPERTY_URN_PREFIX | 11, PROPERTY_URN_PREFIX_STR + "buildingattributes"), + /** Ignition property */ + IGNITION(PROPERTY_URN_PREFIX | 12, PROPERTY_URN_PREFIX_STR + "ignition"), + /** Fieryness property */ + FIERYNESS(PROPERTY_URN_PREFIX | 13, PROPERTY_URN_PREFIX_STR + "fieryness"), + /** Brokeness property */ + BROKENNESS(PROPERTY_URN_PREFIX | 14, PROPERTY_URN_PREFIX_STR + "brokenness"), + /** Building Code property */ + BUILDING_CODE(PROPERTY_URN_PREFIX | 15, PROPERTY_URN_PREFIX_STR + "buildingcode"), + /** Building Ground Area property */ + BUILDING_AREA_GROUND(PROPERTY_URN_PREFIX | 16, PROPERTY_URN_PREFIX_STR + "buildingareaground"), + /** Building Total Area property */ + BUILDING_AREA_TOTAL(PROPERTY_URN_PREFIX | 17, PROPERTY_URN_PREFIX_STR + "buildingareatotal"), + /** Apexes property */ + APEXES(PROPERTY_URN_PREFIX | 18, PROPERTY_URN_PREFIX_STR + "apexes"), + /** Edges property */ + EDGES(PROPERTY_URN_PREFIX | 19, PROPERTY_URN_PREFIX_STR + "edges"), + /** Position property */ + POSITION(PROPERTY_URN_PREFIX | 20, PROPERTY_URN_PREFIX_STR + "position"), + /** Direction property */ + DIRECTION(PROPERTY_URN_PREFIX | 21, PROPERTY_URN_PREFIX_STR + "direction"), + /** Position History property */ + POSITION_HISTORY(PROPERTY_URN_PREFIX | 22, PROPERTY_URN_PREFIX_STR + "positionhistory"), + /** Stamina property */ + STAMINA(PROPERTY_URN_PREFIX | 23, PROPERTY_URN_PREFIX_STR + "stamina"), + /** HP property */ + HP(PROPERTY_URN_PREFIX | 24, PROPERTY_URN_PREFIX_STR + "hp"), + /** Damage property */ + DAMAGE(PROPERTY_URN_PREFIX | 25, PROPERTY_URN_PREFIX_STR + "damage"), + /** Buriedness property */ + BURIEDNESS(PROPERTY_URN_PREFIX | 26, PROPERTY_URN_PREFIX_STR + "buriedness"), + /** Travel Distance property */ + TRAVEL_DISTANCE(PROPERTY_URN_PREFIX | 27, PROPERTY_URN_PREFIX_STR + "traveldistance"), + /** Water Quantity property */ + WATER_QUANTITY(PROPERTY_URN_PREFIX | 28, PROPERTY_URN_PREFIX_STR + "waterquantity"), + /** Temperature property */ + TEMPERATURE(PROPERTY_URN_PREFIX | 29, PROPERTY_URN_PREFIX_STR + "temperature"), + /** Importance property */ + IMPORTANCE(PROPERTY_URN_PREFIX | 30, PROPERTY_URN_PREFIX_STR + "importance"), + /** Capacity property */ + CAPACITY(PROPERTY_URN_PREFIX | 31, PROPERTY_URN_PREFIX_STR + "capacity"), + /** Bed Capacity property */ + BED_CAPACITY(PROPERTY_URN_PREFIX | 32, PROPERTY_URN_PREFIX_STR + "bedcapacity"), + /** Floors property */ + OCCUPIED_BEDS(PROPERTY_URN_PREFIX | 33, PROPERTY_URN_PREFIX_STR + "occupiedbeds"), + /** Refill Capacity property */ + REFILL_CAPACITY(PROPERTY_URN_PREFIX | 34, PROPERTY_URN_PREFIX_STR + "refillcapacity"), + /** Waiting List Size property */ + WAITING_LIST_SIZE(PROPERTY_URN_PREFIX | 35, PROPERTY_URN_PREFIX_STR + "waitinglistlize"); + + private int urnId; + private String urnStr; + public static final Map<Integer, StandardPropertyURN> MAP = URN.generateMap(StandardPropertyURN.class); + public static final Map<String, StandardPropertyURN> MAPSTR = URN.generateMapStr(StandardPropertyURN.class); + + private StandardPropertyURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return this.urnId; + } + + @Override + public String getURNStr() { + return this.urnStr; + } + + /** + * Convert a String to a StandardPropertyURN. + * + * @param s The String to convert. + * @return A StandardPropertyURN. + */ + public static StandardPropertyURN fromInt(int s) { + return MAP.get(s); + } + + public static StandardPropertyURN fromString(String s) { + return MAPSTR.get(s); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/entities/StandardWorldModel.java b/modules/standard/src/rescuecore2/standard/entities/StandardWorldModel.java new file mode 100644 index 0000000000000000000000000000000000000000..c73a593a0fc70d3894f2f601ff1a84ab786d0cf9 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/StandardWorldModel.java @@ -0,0 +1,455 @@ +package rescuecore2.standard.entities; + +import com.infomatiq.jsi.Rectangle; +import com.infomatiq.jsi.SpatialIndex; +import com.infomatiq.jsi.rtree.RTree; +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import rescuecore2.log.Logger; +import rescuecore2.misc.Pair; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.DefaultWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.WorldModelListener; +import gnu.trove.TIntProcedure; + +/** + * A wrapper around a WorldModel that indexes Entities by location. + */ +public class StandardWorldModel extends DefaultWorldModel<StandardEntity> { + + private SpatialIndex index; + + private Map<StandardEntityURN, Collection<StandardEntity>> storedTypes; + private Set<StandardEntity> unindexedEntities; + private Map<Human, Rectangle> humanRectangles; + + private boolean indexed; + private int minX; + private int maxX; + private int minY; + private int maxY; + + /** + * Create a StandardWorldModel. + */ + public StandardWorldModel() { + super(StandardEntity.class); + storedTypes = new EnumMap<StandardEntityURN, Collection<StandardEntity>>( + StandardEntityURN.class); + unindexedEntities = new HashSet<StandardEntity>(); + humanRectangles = new HashMap<Human, Rectangle>(); + addWorldModelListener(new AddRemoveListener()); + indexed = false; + } + + + @Override + public void merge(ChangeSet changeSet) { + super.merge(changeSet); + // Update human rectangles + for (Map.Entry<Human, Rectangle> next : humanRectangles.entrySet()) { + Human h = next.getKey(); + Rectangle r = next.getValue(); + index.delete(r, h.getID().getValue()); + r = makeRectangle(h); + if (r != null) { + index.add(r, h.getID().getValue()); + next.setValue(r); + } + } + } + + + /** + * Tell this index to remember a certain class of entities. + * + * @param urns + * The type URNs to remember. + */ + public void indexClass(StandardEntityURN... urns) { + for (StandardEntityURN urn : urns) { + Collection<StandardEntity> bucket = new HashSet<StandardEntity>(); + for (StandardEntity next : this) { + if (next.getStandardURN().equals(urn)) { + bucket.add(next); + } + } + storedTypes.put(urn, bucket); + } + } + + + /** + * Re-index the world model. + */ + public void index() { + if (indexed && unindexedEntities.isEmpty()) { + Logger.debug( + "Not bothering with reindex: No entities are currently unindexed"); + return; + } + Logger.debug("Re-indexing world model"); + long start = System.currentTimeMillis(); + index = new RTree(); + index.init(new Properties()); + humanRectangles.clear(); + unindexedEntities.clear(); + minX = Integer.MAX_VALUE; + maxX = Integer.MIN_VALUE; + minY = Integer.MAX_VALUE; + maxY = Integer.MIN_VALUE; + // Add all rectangles + for (StandardEntity next : this) { + Rectangle r = makeRectangle(next); + if (r != null) { + index.add(r, next.getID().getValue()); + minX = Math.min(minX, (int) r.minX); + maxX = Math.max(maxX, (int) r.maxX); + minY = Math.min(minY, (int) r.minY); + maxY = Math.max(maxY, (int) r.maxY); + if (next instanceof Human) { + humanRectangles.put((Human) next, r); + } + } + } + long end = System.currentTimeMillis(); + Logger.debug("Finished re-index. Took " + (end - start) + "ms"); + indexed = true; + } + + + /** + * Get objects within a certain range of an entity. + * + * @param entity + * The entity to centre the search on. + * @param range + * The range to look up. + * + * @return A collection of StandardEntitys that are within range. + */ + public Collection<StandardEntity> getObjectsInRange(EntityID entity, + int range) { + return getObjectsInRange(getEntity(entity), range); + } + + + /** + * Get objects within a certain range of an entity. + * + * @param entity + * The entity to centre the search on. + * @param range + * The range to look up. + * + * @return A collection of StandardEntitys that are within range. + */ + public Collection<StandardEntity> getObjectsInRange(StandardEntity entity, + int range) { + Pair<Integer, Integer> location = entity.getLocation(this); + if (location == null) { + return new HashSet<StandardEntity>(); + } + return getObjectsInRange(location.first(), location.second(), range); + } + + + /** + * Get objects within a certain range of a location. + * + * @param x + * The x coordinate of the location. + * @param y + * The y coordinate of the location. + * @param range + * The range to look up. + * + * @return A collection of StandardEntitys that are within range. + */ + public Collection<StandardEntity> getObjectsInRange(int x, int y, int range) { + if (!indexed) { + index(); + } + return getObjectsInRectangle(x - range, y - range, x + range, y + range); + } + + + /** + * Get objects inside a given rectangle. + * + * @param x1 + * The x coordinate of the top left corner. + * @param y1 + * The y coordinate of the top left corner. + * @param x2 + * The x coordinate of the bottom right corner. + * @param y2 + * The y coordinate of the bottom right corner. + * + * @return A collection of StandardEntitys that are inside the rectangle. + */ + public Collection<StandardEntity> getObjectsInRectangle(int x1, int y1, + int x2, int y2) { + if (!indexed) { + index(); + } + final Collection<StandardEntity> result = new HashSet<StandardEntity>(); + Rectangle r = new Rectangle(x1, y1, x2, y2); + index.intersects(r, new TIntProcedure() { + + @Override + public boolean execute(int id) { + StandardEntity e = getEntity(new EntityID(id)); + if (e != null) { + result.add(e); + } + return true; + } + }); + return result; + } + + + /** + * Get all entities of a particular type. + * + * @param urn + * The type urn to look up. + * + * @return A new Collection of entities of the specified type. + */ + public Collection<StandardEntity> getEntitiesOfType(StandardEntityURN urn) { + if (storedTypes.containsKey(urn)) { + return storedTypes.get(urn); + } + indexClass(urn); + return storedTypes.get(urn); + } + + + /** + * Get all entities of a set of types. + * + * @param urns + * The type urns to look up. + * + * @return A new Collection of entities of the specified types. + */ + public Collection<StandardEntity> + getEntitiesOfType(StandardEntityURN... urns) { + Collection<StandardEntity> result = new HashSet<StandardEntity>(); + for (StandardEntityURN urn : urns) { + result.addAll(getEntitiesOfType(urn)); + } + return result; + } + + + /** + * Get the distance between two entities. + * + * @param first + * The ID of the first entity. + * @param second + * The ID of the second entity. + * + * @return The distance between the two entities. A negative value indicates + * that one or both objects either doesn't exist or could not be + * located. + */ + public int getDistance(EntityID first, EntityID second) { + StandardEntity a = getEntity(first); + StandardEntity b = getEntity(second); + if (a == null || b == null) { + return -1; + } + return getDistance(a, b); + } + + + /** + * Get the distance between two entities. + * + * @param first + * The first entity. + * @param second + * The second entity. + * + * @return The distance between the two entities. A negative value indicates + * that one or both objects could not be located. + */ + public int getDistance(StandardEntity first, StandardEntity second) { + Pair<Integer, Integer> a = first.getLocation(this); + Pair<Integer, Integer> b = second.getLocation(this); + if (a == null || b == null) { + return -1; + } + return distance(a, b); + } + + + /** + * Get the world bounds. + * + * @return A Rectangle2D describing the bounds of the world. + */ + public Rectangle2D getBounds() { + if (!indexed) { + index(); + } + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } + + + /** + * Get the world bounds. + * + * @return A pair of coordinates for the top left and bottom right corners. + */ + public Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> getWorldBounds() { + if (!indexed) { + index(); + } + Pair<Integer, Integer> topLeft = new Pair<Integer, Integer>(minX, minY); + Pair<Integer, Integer> bottomRight = new Pair<Integer, Integer>(maxX, maxY); + return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(topLeft, + bottomRight); + } + + + /** + * Create a StandardWorldModel that wraps an existing world model. If the + * existing model is already a StandardWorldModel then it will be returned + * directly, otherwise a new StandardWorldModel will be created that contains + * all the entities in the existing model that are instances of + * StandardEntity. Changes to the existing world model will be reflected in + * the returned StandardWorldModel. + * + * @param existing + * The existing world model to wrap. This may be null. + * + * @return The existing world model if it is an instance of + * StandardWorldModel; a new model otherwise. + */ + public static StandardWorldModel + createStandardWorldModel(WorldModel<? extends Entity> existing) { + if (existing instanceof StandardWorldModel) { + return (StandardWorldModel) existing; + } else { + final StandardWorldModel result = new StandardWorldModel(); + if (existing != null) { + result.addEntities(existing.getAllEntities()); + existing.addWorldModelListener(new WorldModelListener<Entity>() { + + @Override + public void entityAdded(WorldModel<? extends Entity> model, + Entity e) { + result.addEntity(e); + } + + + @Override + public void entityRemoved(WorldModel<? extends Entity> model, + Entity e) { + if (e instanceof StandardEntity) { + result.removeEntity((StandardEntity) e); + } + } + }); + } + return result; + } + } + + + private Rectangle makeRectangle(StandardEntity e) { + int x1 = Integer.MAX_VALUE; + int x2 = Integer.MIN_VALUE; + int y1 = Integer.MAX_VALUE; + int y2 = Integer.MIN_VALUE; + if (e instanceof Area) { + int[] apexes = ((Area) e).getApexList(); + if (apexes.length == 0) { + return null; + } + for (int i = 0; i < apexes.length - 1; i += 2) { + x1 = Math.min(x1, apexes[i]); + x2 = Math.max(x2, apexes[i]); + y1 = Math.min(y1, apexes[i + 1]); + y2 = Math.max(y2, apexes[i + 1]); + } + } else if (e instanceof Blockade) { + int[] apexes = ((Blockade) e).getApexes(); + if (apexes.length == 0) { + return null; + } + for (int i = 0; i < apexes.length - 1; i += 2) { + x1 = Math.min(x1, apexes[i]); + x2 = Math.max(x2, apexes[i]); + y1 = Math.min(y1, apexes[i + 1]); + y2 = Math.max(y2, apexes[i + 1]); + } + } else if (e instanceof Human) { + Human h = (Human) e; + Pair<Integer, Integer> location = h.getLocation(this); + if (location == null) { + return null; + } + x1 = location.first(); + x2 = location.first(); + y1 = location.second(); + y2 = location.second(); + } else { + return null; + } + return new Rectangle(x1, y1, x2, y2); + } + + + private int distance(Pair<Integer, Integer> a, Pair<Integer, Integer> b) { + return distance(a.first(), a.second(), b.first(), b.second()); + } + + + private int distance(int x1, int y1, int x2, int y2) { + double dx = x1 - x2; + double dy = y1 - y2; + return (int) Math.hypot(dx, dy); + } + + private class AddRemoveListener + implements WorldModelListener<StandardEntity> { + + @Override + public void entityAdded(WorldModel<? extends StandardEntity> model, + StandardEntity e) { + StandardEntityURN type = e.getStandardURN(); + if (storedTypes.containsKey(type)) { + Collection<StandardEntity> bucket = storedTypes.get(type); + bucket.add(e); + } + unindexedEntities.add(e); + } + + + @Override + public void entityRemoved(WorldModel<? extends StandardEntity> model, + StandardEntity e) { + StandardEntityURN type = e.getStandardURN(); + if (storedTypes.containsKey(type)) { + Collection<StandardEntity> bucket = storedTypes.get(type); + bucket.remove(e); + } + unindexedEntities.remove(e); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/entities/World.java b/modules/standard/src/rescuecore2/standard/entities/World.java new file mode 100644 index 0000000000000000000000000000000000000000..c192bf527a944cf8dab1c76b1781b419ee67ab3c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/entities/World.java @@ -0,0 +1,337 @@ +package rescuecore2.standard.entities; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * The World object. + */ +public class World extends StandardEntity { + + private IntProperty startTime; + private IntProperty longitude; + private IntProperty latitude; + private IntProperty windForce; + private IntProperty windDirection; + + + /** + * Construct a World object with entirely undefined property values. + * + * @param id + * The ID of this entity. + */ + public World( EntityID id ) { + super( id ); + startTime = new IntProperty( StandardPropertyURN.START_TIME ); + longitude = new IntProperty( StandardPropertyURN.LONGITUDE ); + latitude = new IntProperty( StandardPropertyURN.LATITUDE ); + windForce = new IntProperty( StandardPropertyURN.WIND_FORCE ); + windDirection = new IntProperty( StandardPropertyURN.WIND_DIRECTION ); + registerProperties( startTime, longitude, latitude, windForce, + windDirection ); + } + + + /** + * World copy constructor. + * + * @param other + * The World to copy. + */ + public World( World other ) { + super( other ); + startTime = new IntProperty( other.startTime ); + longitude = new IntProperty( other.longitude ); + latitude = new IntProperty( other.latitude ); + windForce = new IntProperty( other.windForce ); + windDirection = new IntProperty( other.windDirection ); + registerProperties( startTime, longitude, latitude, windForce, + windDirection ); + } + + + @Override + protected Entity copyImpl() { + return new World( getID() ); + } + + + @Override + public StandardEntityURN getStandardURN() { + return StandardEntityURN.WORLD; + } + + + @Override + public Property getProperty( int urn ) { + StandardPropertyURN type; + try { + type = StandardPropertyURN.fromInt( urn ); + } catch ( IllegalArgumentException e ) { + return super.getProperty( urn ); + } + switch ( type ) { + case START_TIME: + return startTime; + case LONGITUDE: + return longitude; + case LATITUDE: + return latitude; + case WIND_FORCE: + return windForce; + case WIND_DIRECTION: + return windDirection; + default: + return super.getProperty( urn ); + } + } + + + /** + * Get the startTime property. + * + * @return The startTime property. + */ + public IntProperty getStartTimeProperty() { + return startTime; + } + + + /** + * Get the value of the startTime property. + * + * @return The value of the startTime property. + */ + public int getStartTime() { + return startTime.getValue(); + } + + + /** + * Set the startTime property. + * + * @param startTime + * The new startTime. + */ + public void setStartTime( int startTime ) { + this.startTime.setValue( startTime ); + } + + + /** + * Find out if the startTime property has been defined. + * + * @return True if the startTime property has been defined, false otherwise. + */ + public boolean isStartTimeDefined() { + return startTime.isDefined(); + } + + + /** + * Undefine the startTime property. + */ + public void undefineStartTime() { + startTime.undefine(); + } + + + /** + * Get the latitude property. + * + * @return The latitude property. + */ + public IntProperty getLatitudeProperty() { + return latitude; + } + + + /** + * Get the value of the latitude property. + * + * @return The value of the latitude property. + */ + public int getLatitude() { + return latitude.getValue(); + } + + + /** + * Set the latitude property. + * + * @param latitude + * The new latitude. + */ + public void setLatitude( int latitude ) { + this.latitude.setValue( latitude ); + } + + + /** + * Find out if the latitude property has been defined. + * + * @return True if the latitude property has been defined, false otherwise. + */ + public boolean isLatitudeDefined() { + return latitude.isDefined(); + } + + + /** + * Undefine the latitude property. + */ + public void undefineLatitude() { + latitude.undefine(); + } + + + /** + * Get the longitude property. + * + * @return The longitude property. + */ + public IntProperty getLongitudeProperty() { + return longitude; + } + + + /** + * Get the value of the longitude property. + * + * @return The value of the longitude property. + */ + public int getLongitude() { + return longitude.getValue(); + } + + + /** + * Set the longitude property. + * + * @param longitude + * The new longitude. + */ + public void setLongitude( int longitude ) { + this.longitude.setValue( longitude ); + } + + + /** + * Find out if the longitude property has been defined. + * + * @return True if the longitude property has been defined, false otherwise. + */ + public boolean isLongitudeDefined() { + return longitude.isDefined(); + } + + + /** + * Undefine the longitude property. + */ + public void undefineLongitude() { + longitude.undefine(); + } + + + /** + * Get the windForce property. + * + * @return The windForce property. + */ + public IntProperty getWindForceProperty() { + return windForce; + } + + + /** + * Get the value of the windForce property. + * + * @return The value of the windForce property. + */ + public int getWindForce() { + return windForce.getValue(); + } + + + /** + * Set the windForce property. + * + * @param windForce + * The new windForce. + */ + public void setWindForce( int windForce ) { + this.windForce.setValue( windForce ); + } + + + /** + * Find out if the windForce property has been defined. + * + * @return True if the windForce property has been defined, false otherwise. + */ + public boolean isWindForceDefined() { + return windForce.isDefined(); + } + + + /** + * Undefine the windForce property. + */ + public void undefineWindForce() { + windForce.undefine(); + } + + + /** + * Get the windDirection property. + * + * @return The windDirection property. + */ + public IntProperty getWindDirectionProperty() { + return windDirection; + } + + + /** + * Get the value of the windDirection property. + * + * @return The value of the windDirection property. + */ + public int getWindDirection() { + return windDirection.getValue(); + } + + + /** + * Set the windDirection property. + * + * @param windDirection + * The new windDirection. + */ + public void setWindDirection( int windDirection ) { + this.windDirection.setValue( windDirection ); + } + + + /** + * Find out if the windDirection property has been defined. + * + * @return True if the windDirection property has been defined, false + * otherwise. + */ + public boolean isWindDirectionDefined() { + return windDirection.isDefined(); + } + + + /** + * Undefine the windDirection property. + */ + public void undefineWindDirection() { + windDirection.undefine(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/BuriedAgentsCommandFilter.java b/modules/standard/src/rescuecore2/standard/kernel/BuriedAgentsCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..6a89f56e5a2a52c90f59e168e61535baff3a63aa --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/BuriedAgentsCommandFilter.java @@ -0,0 +1,38 @@ +package rescuecore2.standard.kernel; + +import kernel.AbstractCommandFilter; +import kernel.KernelState; + +import rescuecore2.messages.Command; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.messages.AKSubscribe; +import rescuecore2.standard.messages.AKSpeak; +import rescuecore2.standard.messages.AKSay; + +/** + A CommandFilter that discards commands from buried agents. + */ +public class BuriedAgentsCommandFilter extends AbstractCommandFilter { + @Override + protected boolean allowed(Command c, KernelState state) { + EntityID id = c.getAgentID(); + Entity e = state.getWorldModel().getEntity(id); + if ((c instanceof AKSubscribe) + || (c instanceof AKSpeak) + || (c instanceof AKSay)) { + return true; + } + if (e instanceof Human) { + Human h = (Human)e; + if (h.isBuriednessDefined() && h.getBuriedness() > 0) { + Logger.info("Ignoring command " + c + ": Agent " + h + " is buried"); + return false; + } + } + return true; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/CiviliansDeadOrRescuedTerminationCondition.java b/modules/standard/src/rescuecore2/standard/kernel/CiviliansDeadOrRescuedTerminationCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..eca473715e52699535fe70094ca8dcc681936233 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/CiviliansDeadOrRescuedTerminationCondition.java @@ -0,0 +1,53 @@ +package rescuecore2.standard.kernel; + +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.worldmodel.Entity; +import rescuecore2.config.Config; +import rescuecore2.log.Logger; + +import kernel.KernelState; +import kernel.TerminationCondition; + +/** + A TerminationCondition that terminates the simulation once all Civilians are either rescued or dead. +*/ +public class CiviliansDeadOrRescuedTerminationCondition implements TerminationCondition { + @Override + public void initialise(Config config) { + } + + @Override + public boolean shouldStop(KernelState k) { + for (Entity next : k.getWorldModel()) { + if (next instanceof Civilian) { + Civilian c = (Civilian)next; + if (!c.isHPDefined() || !c.isDamageDefined() || !c.isBuriednessDefined() || !c.isPositionDefined()) { + // Civilian with unknown state: we're not ready to stop. + return false; + } + if (c.getHP() <= 0) { + // Dead - ignore + continue; + } + if (c.getDamage() > 0 || c.getBuriedness() > 0) { + // Hurt or buried - keep running + return false; + } + Entity position = k.getWorldModel().getEntity(c.getPosition()); + if (!(position instanceof Refuge)) { + // Alive but not in a refuge - keep running + return false; + } + } + } + // Found no reason to keep going so stop. + Logger.debug("CiviliansDeadOrRescuedTerminationCondition fired"); + return true; + } + + @Override + public String toString() { + return "All civilians rescued or dead"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/DeadAgentsCommandFilter.java b/modules/standard/src/rescuecore2/standard/kernel/DeadAgentsCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..f68621c7997f476e8b3b91b8c254a7ae6006b8d5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/DeadAgentsCommandFilter.java @@ -0,0 +1,38 @@ +package rescuecore2.standard.kernel; + +import kernel.AbstractCommandFilter; +import kernel.KernelState; + +import rescuecore2.messages.Command; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.log.Logger; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntityConstants; + +/** + A CommandFilter that discards commands from dead agents. + */ +public class DeadAgentsCommandFilter extends AbstractCommandFilter { + @Override + protected boolean allowed(Command c, KernelState state) { + EntityID id = c.getAgentID(); + Entity e = state.getWorldModel().getEntity(id); + if (e instanceof Human) { + Human h = (Human)e; + if (h.isHPDefined() && h.getHP() <= 0) { + Logger.info("Ignoring command " + c + ": Agent " + h + " is dead"); + return false; + } + } + if (e instanceof Building) { + Building b = (Building)e; + if (b.isFierynessDefined() && b.getFierynessEnum() == StandardEntityConstants.Fieryness.BURNT_OUT) { + Logger.info("Ignoring command " + c + ": Centre " + b + " is burnt out"); + return false; + } + } + return true; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/LineOfSightPerception.java b/modules/standard/src/rescuecore2/standard/kernel/LineOfSightPerception.java new file mode 100755 index 0000000000000000000000000000000000000000..4c5ea71e33518925ac549ca544eaa00cc64a23d1 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/LineOfSightPerception.java @@ -0,0 +1,569 @@ +package rescuecore2.standard.kernel; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; + +import kernel.Perception; +import kernel.AgentProxy; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Edge; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.properties.IntProperty; +import rescuecore2.config.Config; +import rescuecore2.view.RenderedObject; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.ViewListener; +import rescuecore2.misc.Pair; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.log.Logger; +import rescuecore2.GUIComponent; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.BorderLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import rescuecore2.standard.view.StandardWorldModelViewer; +import rescuecore2.standard.view.StandardViewLayer; +import rescuecore2.standard.view.BuildingLayer; +import rescuecore2.standard.view.RoadLayer; +import rescuecore2.standard.view.RoadBlockageLayer; +import rescuecore2.standard.view.HumanLayer; + +/** + Line of sight perception. + */ + +/* + * Implementation of Refuge Bed Capacity + * @author Farshid Faraji + * May 2020 During Covid-19 :-))) + * */ +public class LineOfSightPerception implements Perception, GUIComponent { + private static final int DEFAULT_VIEW_DISTANCE = 30000; + private static final int DEFAULT_HP_PRECISION = 1000; + private static final int DEFAULT_DAMAGE_PRECISION = 100; + private static final int DEFAULT_RAY_COUNT = 720; + + private static final String VIEW_DISTANCE_KEY = "perception.los.max-distance"; + private static final String RAY_COUNT_KEY = "perception.los.ray-count"; + private static final String HP_PRECISION_KEY = "perception.los.precision.hp"; + private static final String DAMAGE_PRECISION_KEY = "perception.los.precision.damage"; + + private static final IntersectionSorter INTERSECTION_SORTER = new IntersectionSorter(); + + private int viewDistance; + private int hpPrecision; + private int damagePrecision; + private int rayCount; + + private StandardWorldModel world; + private Config config; + + private LOSView view; + + /** + Create a LineOfSightPerception object. + */ + public LineOfSightPerception() { + } + + @Override + public void initialise(Config newConfig, WorldModel<? extends Entity> model) { + world = StandardWorldModel.createStandardWorldModel(model); + this.config = newConfig; + viewDistance = config.getIntValue(VIEW_DISTANCE_KEY, DEFAULT_VIEW_DISTANCE); + hpPrecision = config.getIntValue(HP_PRECISION_KEY, DEFAULT_HP_PRECISION); + damagePrecision = config.getIntValue(DAMAGE_PRECISION_KEY, DEFAULT_DAMAGE_PRECISION); + rayCount = config.getIntValue(RAY_COUNT_KEY, DEFAULT_RAY_COUNT); + view = null; + } + + @Override + public String toString() { + return "Line of sight perception"; + } + + @Override + public JComponent getGUIComponent() { + if (view == null) { + view = new LOSView(); + view.refresh(); + } + return view; + } + + @Override + public String getGUIComponentName() { + return "Line of sight"; + } + + @Override + public void setTime(int timestep) { + if (view != null) { + view.clear(); + view.refresh(); + } + } + + @Override + public ChangeSet getVisibleEntities(AgentProxy agent) { + StandardEntity agentEntity = (StandardEntity)agent.getControlledEntity(); + Logger.debug("Finding visible entities for " + agentEntity); + ChangeSet result = new ChangeSet(); + // Look for objects within range + Pair<Integer, Integer> location = agentEntity.getLocation(world); + if (location != null) { + Point2D point = new Point2D(location.first(), location.second()); + Collection<StandardEntity> nearby = world.getObjectsInRange(location.first(), location.second(), viewDistance); + Collection<StandardEntity> visible = findVisible(agentEntity, point, nearby); + for (StandardEntity next : visible) { + StandardEntityURN urn = next.getStandardURN(); + switch (urn) { + case ROAD: + case HYDRANT: + addRoadProperties((Road)next, result); + break; + // the refuge bed and waiting list information is only given to AT + case REFUGE: + if(agentEntity instanceof Human) + if (((Human) agentEntity).getPosition(world) == next) { + addRefugeProperties((Refuge) next, result); + } + addBuildingProperties((Building) next, result); + break; + case BUILDING: + case GAS_STATION: + case FIRE_STATION: + case AMBULANCE_CENTRE: + case POLICE_OFFICE: + addBuildingProperties((Building)next, result); + break; + case CIVILIAN: + case FIRE_BRIGADE: + case AMBULANCE_TEAM: + case POLICE_FORCE: + // Always send all properties of the agent-controlled object + if (next == agentEntity) { + addSelfProperties((Human)next, result); + } + else { + addHumanProperties((Human)next, result); + } + break; + case BLOCKADE: + addBlockadeProperties((Blockade)next, result); + break; + default: + // Ignore other types + break; + } + } + + if(agentEntity instanceof AmbulanceCentre) + { + Collection<StandardEntity> refuges = world.getEntitiesOfType(StandardEntityURN.REFUGE); + for (StandardEntity next : refuges) { + addRefugeProperties((Refuge)next, result); + } + } + + } + if (view != null) { + view.repaint(); + } + return result; + } + + private void addRoadProperties(Road road, ChangeSet result) { + addAreaProperties(road, result); + // Only update blockades + result.addChange(road, road.getBlockadesProperty()); + // Also update each blockade + if (road.isBlockadesDefined()) { + for (EntityID id : road.getBlockades()) { + Blockade blockade = (Blockade)world.getEntity(id); + if (blockade == null) { + Logger.error("Blockade " + id + " is null!"); + Logger.error(road.getFullDescription()); + } + else { + addBlockadeProperties(blockade, result); + } + } + } + } + + private void addBuildingProperties(Building building, ChangeSet result) { + addAreaProperties(building, result); + // Update TEMPERATURE, FIERYNESS and BROKENNESS + result.addChange(building, building.getTemperatureProperty()); + result.addChange(building, building.getFierynessProperty()); + result.addChange(building, building.getBrokennessProperty()); + result.addChange(building, building.getCapacityProperty()); + } + + public void addRefugeProperties(Refuge refuge, ChangeSet result) { + result.addChange(refuge, refuge.getBedCapacityProperty()); + result.addChange(refuge, refuge.getOccupiedBedsProperty()); + result.addChange(refuge, refuge.getWaitingListSizeProperty()); + //TODO send other information e.g civilians info in the refuge + } + + + private void addAreaProperties(Area area, ChangeSet result) { + } + + private void addFarBuildingProperties(Building building, ChangeSet result) { + // Update FIERYNESS only + result.addChange(building, building.getFierynessProperty()); + } + + private void addHumanProperties(Human human, ChangeSet result) { + // Update POSITION, X, Y, DIRECTION, STAMINA, BURIEDNESS, HP, DAMAGE + result.addChange(human, human.getPositionProperty()); + result.addChange(human, human.getXProperty()); + result.addChange(human, human.getYProperty()); + result.addChange(human, human.getDirectionProperty()); + result.addChange(human, human.getStaminaProperty()); + result.addChange(human, human.getBuriednessProperty()); + // Round HP and damage + IntProperty hp = (IntProperty)human.getHPProperty().copy(); + roundProperty(hp, hpPrecision); + result.addChange(human, hp); + IntProperty damage = (IntProperty)human.getDamageProperty().copy(); + roundProperty(damage, damagePrecision); + result.addChange(human, damage); + } + + private void addSelfProperties(Human human, ChangeSet result) { + // Update human properties and POSITION_HISTORY + addHumanProperties(human, result); + result.addChange(human, human.getPositionHistoryProperty()); + // Un-round hp and damage + result.addChange(human, human.getHPProperty()); + result.addChange(human, human.getDamageProperty()); + if (human instanceof FireBrigade) { + FireBrigade fb = (FireBrigade)human; + result.addChange(fb, fb.getWaterProperty()); + } + } + + private void addBlockadeProperties(Blockade blockade, ChangeSet result) { + result.addChange(blockade, blockade.getXProperty()); + result.addChange(blockade, blockade.getYProperty()); + result.addChange(blockade, blockade.getPositionProperty()); + result.addChange(blockade, blockade.getApexesProperty()); + result.addChange(blockade, blockade.getRepairCostProperty()); + } + + private void roundProperty(IntProperty p, int precision) { + if (precision != 1 && p.isDefined()) { + p.setValue(round(p.getValue(), precision)); + } + } + + private int round(int value, int precision) { + int remainder = value % precision; + value -= remainder; + if (remainder >= precision / 2) { + value += precision; + } + return value; + } + + private Collection<StandardEntity> findVisible(StandardEntity agentEntity, Point2D location, Collection<StandardEntity> nearby) { + Logger.debug("Finding visible entities from " + location); + Logger.debug(nearby.size() + " nearby entities"); + Collection<LineInfo> lines = getAllLines(nearby); + // Cast rays + // CHECKSTYLE:OFF:MagicNumber + double dAngle = Math.PI * 2 / rayCount; + // CHECKSTYLE:ON:MagicNumber + Collection<StandardEntity> result = new HashSet<StandardEntity>(); + for (int i = 0; i < rayCount; ++i) { + double angle = i * dAngle; + Vector2D vector = new Vector2D(Math.sin(angle), Math.cos(angle)).scale(viewDistance); + Ray ray = new Ray(new Line2D(location, vector), lines); + for (LineInfo hit : ray.getLinesHit()) { + StandardEntity e = hit.getEntity(); + result.add(e); + } + if (view != null) { + view.addRay(agentEntity, ray); + } + } + // Now look for humans + for (StandardEntity next : nearby) { + if (next instanceof Human) { + Human h = (Human)next; + if (canSee(agentEntity, location, h, lines)) { + result.add(h); + } + } + } + // Add self + result.add(agentEntity); + Logger.debug(agentEntity + " can see " + result); + return result; + } + + private boolean canSee(StandardEntity agent, Point2D location, Human h, Collection<LineInfo> lines) { + if (h.isXDefined() && h.isYDefined()) { + int x = h.getX(); + int y = h.getY(); + Point2D humanLocation = new Point2D(x, y); + Ray ray = new Ray(new Line2D(location, humanLocation), lines); + if (ray.getVisibleLength() >= 1) { + if (view != null) { + view.addRay(agent, ray); + } + return true; + } + } + else if (h.isPositionDefined()) { + if (h.getPosition().equals(agent.getID())) { + return true; + } + Entity e = world.getEntity(h.getPosition()); + if (e instanceof AmbulanceTeam) { + return canSee(agent, location, (Human)e, lines); + } + } + return false; + } + + private Collection<LineInfo> getAllLines(Collection<StandardEntity> entities) { + Collection<LineInfo> result = new LinkedList<LineInfo>(); + for (StandardEntity next : entities) { + if (next instanceof Building) { + for (Edge edge : ((Building)next).getEdges()) { + Line2D line = edge.getLine(); + result.add(new LineInfo(line, next, !edge.isPassable())); + } + } + if (next instanceof Road) { + for (Edge edge : ((Road)next).getEdges()) { + Line2D line = edge.getLine(); + result.add(new LineInfo(line, next, false)); + } + } + else if (next instanceof Blockade) { + int[] apexes = ((Blockade)next).getApexes(); + List<Point2D> points = GeometryTools2D.vertexArrayToPoints(apexes); + List<Line2D> lines = GeometryTools2D.pointsToLines(points, true); + for (Line2D line : lines) { + result.add(new LineInfo(line, next, false)); + } + } + else { + continue; + } + } + return result; + } + + private static class Ray { + /** The ray itself. */ + private Line2D ray; + /** The visible length of the ray. */ + private double length; + /** List of lines hit in order. */ + private List<LineInfo> hit; + + public Ray(Line2D ray, Collection<LineInfo> otherLines) { + this.ray = ray; + List<Pair<LineInfo, Double>> intersections = new ArrayList<Pair<LineInfo, Double>>(); + // Find intersections with other lines + for (LineInfo other : otherLines) { + double d1 = ray.getIntersection(other.getLine()); + double d2 = other.getLine().getIntersection(ray); + if (d2 >= 0 && d2 <= 1 && d1 > 0 && d1 <= 1) { + intersections.add(new Pair<LineInfo, Double>(other, d1)); + } + } + Collections.sort(intersections, INTERSECTION_SORTER); + hit = new ArrayList<LineInfo>(); + length = 1; + for (Pair<LineInfo, Double> next : intersections) { + LineInfo l = next.first(); + hit.add(l); + if (l.isBlocking()) { + length = next.second(); + break; + } + } + } + + public Line2D getRay() { + return ray; + } + + public double getVisibleLength() { + return length; + } + + public List<LineInfo> getLinesHit() { + return Collections.unmodifiableList(hit); + } + } + + private static class LineInfo { + private Line2D line; + private StandardEntity entity; + private boolean blocking; + + public LineInfo(Line2D line, StandardEntity entity, boolean blocking) { + this.line = line; + this.entity = entity; + this.blocking = blocking; + } + + public Line2D getLine() { + return line; + } + + public StandardEntity getEntity() { + return entity; + } + + public boolean isBlocking() { + return blocking; + } + } + + private static class IntersectionSorter implements Comparator<Pair<LineInfo, Double>>, java.io.Serializable { + @Override + public int compare(Pair<LineInfo, Double> a, Pair<LineInfo, Double> b) { + double d1 = a.second(); + double d2 = b.second(); + if (d1 < d2) { + return -1; + } + if (d1 > d2) { + return 1; + } + return 0; + } + } + + private class LOSView extends JPanel { + private transient StandardWorldModelViewer viewer; + private transient Collection<Ray> rays; + private transient Map<StandardEntity, Collection<Ray>> sources; + private transient StandardEntity selected; + + public LOSView() { + super(new BorderLayout()); + viewer = new StandardWorldModelViewer(); + viewer.removeAllLayers(); + viewer.addLayer(new BuildingLayer()); + viewer.addLayer(new RoadLayer()); + viewer.addLayer(new RoadBlockageLayer()); + viewer.addLayer(new HumanLayer()); + viewer.addLayer(new RayLayer()); + rays = new ArrayList<Ray>(); + sources = new LazyMap<StandardEntity, Collection<Ray>>() { + @Override + public Collection<Ray> createValue() { + return new HashSet<Ray>(); + } + }; + selected = null; + viewer.addViewListener(new ViewListener() { + @Override + public void objectsClicked(ViewComponent v, List<RenderedObject> objects) { + selected = null; + for (RenderedObject o : objects) { + if (o.getObject() instanceof Human) { + selected = (StandardEntity)o.getObject(); + viewer.repaint(); + } + } + } + + @Override + public void objectsRollover(ViewComponent v, List<RenderedObject> objects) { + } + }); + add(viewer, BorderLayout.CENTER); + } + + public void clear() { + synchronized (rays) { + rays.clear(); + sources.clear(); + } + } + + public void addRay(StandardEntity source, Ray ray) { + synchronized (rays) { + rays.add(ray); + sources.get(source).add(ray); + } + } + + public void refresh() { + viewer.view(world); + } + + private class RayLayer extends StandardViewLayer { + @Override + public Collection<RenderedObject> render(Graphics2D g, ScreenTransform transform, int width, int height) { + Collection<Ray> toDraw = new HashSet<Ray>(); + synchronized (rays) { + if (selected == null) { + toDraw.addAll(rays); + } + else { + toDraw.addAll(sources.get(selected)); + } + } + g.setColor(Color.CYAN); + for (Ray next : toDraw) { + Line2D line = next.getRay(); + Point2D origin = line.getOrigin(); + Point2D end = line.getPoint(next.getVisibleLength()); + int x1 = transform.xToScreen(origin.getX()); + int y1 = transform.yToScreen(origin.getY()); + int x2 = transform.xToScreen(end.getX()); + int y2 = transform.yToScreen(end.getY()); + g.drawLine(x1, y1, x2, y2); + } + return new ArrayList<RenderedObject>(); + } + + @Override + public String getName() { + return "Line of sight rays"; + } + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/MapValidationException.java b/modules/standard/src/rescuecore2/standard/kernel/MapValidationException.java new file mode 100644 index 0000000000000000000000000000000000000000..fa9ac275421ab752c5283947a4b3370a2951d8ea --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/MapValidationException.java @@ -0,0 +1,38 @@ +package rescuecore2.standard.kernel; + +/** + An exception class for map validation errors. + */ +public class MapValidationException extends Exception { + /** + Construct a MapValidationException with no error message. + */ + public MapValidationException() { + super(); + } + + /** + Construct a MapValidationException with an error message. + @param msg The error message. + */ + public MapValidationException(String msg) { + super(msg); + } + + /** + Construct a MapValidationException with an underlying cause. + @param cause The underlying cause. + */ + public MapValidationException(Throwable cause) { + super(cause); + } + + /** + Construct a MapValidationException with an error message and an underlying cause. + @param msg The error message. + @param cause The underlying cause. + */ + public MapValidationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/SingleCommandFilter.java b/modules/standard/src/rescuecore2/standard/kernel/SingleCommandFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..ab2d45688e84834dad2ef83f0486a131d12a93e4 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/SingleCommandFilter.java @@ -0,0 +1,63 @@ +package rescuecore2.standard.kernel; + +import kernel.CommandFilter; +import kernel.KernelState; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.standard.messages.AKRest; +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 java.util.Set; +import java.util.HashSet; +import java.util.Collection; +import java.util.Iterator; + +/** + A CommandFilter that ensures only one non-communication command is allowed per agent. + */ +public class SingleCommandFilter implements CommandFilter { + @Override + public void initialise(Config config) { + } + + @Override + public void filter(Collection<Command> commands, KernelState state) { + Set<EntityID> sent = new HashSet<EntityID>(); + Iterator<Command> it = commands.iterator(); + while (it.hasNext()) { + Command c = it.next(); + if (filterable(c)) { + EntityID sender = c.getAgentID(); + if (sent.contains(sender)) { + it.remove(); + Logger.info("Ignoring command " + c + ": Agent " + sender + " already sent a command"); + } + else { + sent.add(sender); + Logger.debug(sender + " sent command " + c); + } + } + } + } + + private boolean filterable(Command c) { + return (c instanceof AKRest) + || (c instanceof AKMove) + || (c instanceof AKLoad) + || (c instanceof AKUnload) + || (c instanceof AKRescue) + || (c instanceof AKClear) + || (c instanceof AKClearArea) + || (c instanceof AKExtinguish); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/StandardAgentRegistrar.java b/modules/standard/src/rescuecore2/standard/kernel/StandardAgentRegistrar.java new file mode 100644 index 0000000000000000000000000000000000000000..71dffc87ce3b1c73d1578b6bd32470c1e0a0605b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/StandardAgentRegistrar.java @@ -0,0 +1,191 @@ +package rescuecore2.standard.kernel; + +import java.util.Set; +import java.util.HashSet; + +import java.util.regex.PatternSyntaxException; + +import kernel.AgentRegistrar; +import kernel.ComponentManager; +import kernel.KernelException; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.Property; +import rescuecore2.Constants; + +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.FireStation; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.PoliceOffice; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardPropertyURN; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; + +import rescuecore2.standard.StandardConstants; + +/** + Class that registers standard agents. + */ +public class StandardAgentRegistrar implements AgentRegistrar { + private static final Set<String> VISIBLE_CONFIG_OPTIONS = new HashSet<String>(); + + static { + VISIBLE_CONFIG_OPTIONS.add("kernel\\.agents\\.think-time"); + VISIBLE_CONFIG_OPTIONS.add("kernel\\.agents\\.ignoreuntil"); + VISIBLE_CONFIG_OPTIONS.add("kernel\\.startup\\.connect-time"); + VISIBLE_CONFIG_OPTIONS.add(Constants.COMMUNICATION_MODEL_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(Constants.PERCEPTION_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add("fire\\.tank\\.maximum"); + VISIBLE_CONFIG_OPTIONS.add("fire\\.tank\\.refill_rate"); + VISIBLE_CONFIG_OPTIONS.add("fire\\.tank\\.refill_hydrant_rate"); + VISIBLE_CONFIG_OPTIONS.add("fire\\.extinguish\\.max-sum"); + VISIBLE_CONFIG_OPTIONS.add("fire\\.extinguish\\.max-distance"); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.FIRE_BRIGADE_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.FIRE_STATION_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.AMBULANCE_TEAM_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.AMBULANCE_CENTRE_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.POLICE_FORCE_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add(StandardConstants.POLICE_OFFICE_COUNT_KEY.replace(".", "\\.")); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.count"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.max\\.platoon"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.max\\.centre"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.\\d+\\.type"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.\\d+\\.range"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.\\d+\\.messages\\.size"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.\\d+\\.messages\\.max"); + VISIBLE_CONFIG_OPTIONS.add("comms\\.channels\\.\\d+\\.bandwidth"); + VISIBLE_CONFIG_OPTIONS.add("clear\\.repair\\.rate"); + VISIBLE_CONFIG_OPTIONS.add("clear\\.repair\\.distance"); + VISIBLE_CONFIG_OPTIONS.add("clear\\.repair\\.rad"); + VISIBLE_CONFIG_OPTIONS.add("perception\\.los\\.max-distance"); + VISIBLE_CONFIG_OPTIONS.add("perception\\.los\\.precision\\.hp"); + VISIBLE_CONFIG_OPTIONS.add("perception\\.los\\.precision\\.damage"); + } + + @Override + public void registerAgents(WorldModel<? extends Entity> world, Config config, ComponentManager manager) throws KernelException { + StandardWorldModel model = StandardWorldModel.createStandardWorldModel(world); + Config agentConfig = new Config(config); + try { + agentConfig.removeExceptRegex(VISIBLE_CONFIG_OPTIONS); + } + catch (PatternSyntaxException e) { + throw new KernelException(e); + } + agentConfig.setIntValue(StandardConstants.FIRE_BRIGADE_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE).size()); + agentConfig.setIntValue(StandardConstants.FIRE_STATION_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.FIRE_STATION).size()); + agentConfig.setIntValue(StandardConstants.AMBULANCE_TEAM_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.AMBULANCE_TEAM).size()); + agentConfig.setIntValue(StandardConstants.AMBULANCE_CENTRE_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.AMBULANCE_CENTRE).size()); + agentConfig.setIntValue(StandardConstants.POLICE_FORCE_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.POLICE_FORCE).size()); + agentConfig.setIntValue(StandardConstants.POLICE_OFFICE_COUNT_KEY, model.getEntitiesOfType(StandardEntityURN.POLICE_OFFICE).size()); + Set<Entity> initialEntities = new HashSet<Entity>(); + for (Entity e : world) { + maybeAddInitialEntity(e, initialEntities); + } + for (Entity e : world) { + if (e instanceof FireBrigade + || e instanceof FireStation + || e instanceof AmbulanceTeam + || e instanceof AmbulanceCentre + || e instanceof PoliceForce + || e instanceof PoliceOffice + ) { + Set<Entity> s = new HashSet<Entity>(initialEntities); + s.remove(e); + s.add(e); + manager.registerAgentControlledEntity(e, s, agentConfig); + } + if(e instanceof Civilian){ + Config civilianConfig = new Config(agentConfig); + Set<Entity> s = new HashSet<Entity>(initialEntities); + s.remove(e); + s.add(e); + String configSeed = config.getValue(Constants.RANDOM_SEED_KEY, ""); + if(!configSeed.equals("")){ + int seed = Integer.parseInt(configSeed)+e.getID().getValue(); + civilianConfig.setValue(Constants.RANDOM_SEED_KEY, seed+""); + } + manager.registerAgentControlledEntity(e, s, civilianConfig); + } + + } + } + + private void maybeAddInitialEntity(Entity e, Set<Entity> initialEntities) { + if (e instanceof Road) { + Road r = (Road)e.copy(); + filterAreaProperties(r); + initialEntities.add(r); + } + if (e instanceof Building) { + Building b = (Building)e.copy(); + filterBuildingProperties(b); + initialEntities.add(b); + } + if (e instanceof Human) { + if (!(e instanceof Civilian)) { + Human h = (Human)e.copy(); + filterHumanProperties(h); + initialEntities.add(h); + } + } + } + + private void filterAreaProperties(Area a) { + for (Property next : a.getProperties()) { + // Hide blockades + StandardPropertyURN urn = StandardPropertyURN.fromInt(next.getURN()); + switch (urn) { + case BLOCKADES: + next.undefine(); + break; + default: + break; + } + } + } + + private void filterBuildingProperties(Building b) { + filterAreaProperties(b); + for (Property next : b.getProperties()) { + // Hide ignition, fieryness, brokenness, temperature + StandardPropertyURN urn = StandardPropertyURN.fromInt(next.getURN()); + switch (urn) { + case IGNITION: + case FIERYNESS: + case BROKENNESS: + case TEMPERATURE: + next.undefine(); + break; + default: + // Ignore + } + } + } + + private void filterHumanProperties(Human h) { + for (Property next : h.getProperties()) { + // Human properties: POSITION, X, Y, WATER_QUANTITY + // Everything else should be undefined + StandardPropertyURN urn = StandardPropertyURN.fromInt(next.getURN()); + switch (urn) { + case X: + case Y: + case POSITION: + case WATER_QUANTITY: + break; + default: + next.undefine(); + } + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/StandardCommandCollector.java b/modules/standard/src/rescuecore2/standard/kernel/StandardCommandCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..f03c55adc4ddef76a8a0b197808a528cfdaf682b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/StandardCommandCollector.java @@ -0,0 +1,73 @@ +package rescuecore2.standard.kernel; + +import rescuecore2.config.Config; +import rescuecore2.messages.Command; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.standard.messages.AKExtinguish; +import rescuecore2.standard.messages.AKMove; +import rescuecore2.standard.messages.AKClear; +import rescuecore2.standard.messages.AKRescue; +import rescuecore2.standard.messages.AKLoad; +import rescuecore2.standard.messages.AKUnload; +import rescuecore2.standard.messages.AKRest; + +import kernel.CommandCollector; +import kernel.AgentProxy; + +import java.util.Collection; +import java.util.Set; +import java.util.HashSet; +import java.util.ArrayList; + +/** + A CommandCollector that will wait until a non-communication command has been received from each agent. +*/ +public class StandardCommandCollector implements CommandCollector { + private static final long WAIT_TIME = 100; + + @Override + public void initialise(Config config) { + } + + @Override + public Collection<Command> getAgentCommands(Collection<AgentProxy> agents, int timestep) throws InterruptedException { + Set<AgentProxy> waiting = new HashSet<AgentProxy>(agents); + while (!waiting.isEmpty()) { + for (AgentProxy next : agents) { + Collection<Command> commands = next.getAgentCommands(timestep); + for (Command c : commands) { + if (isTriggerCommand(c)) { + Logger.debug(next + " sent a trigger command"); + waiting.remove(next); + } + } + } + Logger.info(this + " waiting for commands from " + waiting.size() + " agents"); + Thread.sleep(WAIT_TIME); + } + Collection<Command> result = new ArrayList<Command>(); + for (AgentProxy next : agents) { + result.addAll(next.getAgentCommands(timestep)); + } + Logger.trace(this + " returning " + result.size() + " commands"); + return result; + } + + @Override + public String toString() { + return "Standard command collector"; + } + + private boolean isTriggerCommand(Command c) { + return ((c instanceof AKMove) + || (c instanceof AKRest) + || (c instanceof AKExtinguish) + || (c instanceof AKClear) + || (c instanceof AKClearArea) + || (c instanceof AKRescue) + || (c instanceof AKLoad) + || (c instanceof AKUnload)); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/StandardPerception.java b/modules/standard/src/rescuecore2/standard/kernel/StandardPerception.java new file mode 100755 index 0000000000000000000000000000000000000000..ce3c5b26feafd6cc32d968fb6f12c3222b0ed397 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/StandardPerception.java @@ -0,0 +1,433 @@ +package rescuecore2.standard.kernel; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import kernel.AgentProxy; +import kernel.Perception; + +import rescuecore2.GUIComponent; +import rescuecore2.config.Config; +import rescuecore2.misc.Pair; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.misc.SliderComponent; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.properties.IntProperty; + +/** + * Legacy implementation of perception with a GUI. + */ +/* + * Implementation of Refuge Bed Capacity + * + * @author Farshid Faraji May 2020 During Covid-19 :-))) + */ +public class StandardPerception implements Perception, GUIComponent { + private static final boolean DEFAULT_USE_FAR_FIRES = true; + private static final int DEFAULT_HP_PRECISION = 1000; + private static final int DEFAULT_DAMAGE_PRECISION = 100; + + private static final String VIEW_DISTANCE_KEY = "perception.standard.view-distance"; + private static final String FAR_FIRE_DISTANCE_KEY = "perception.standard.far-fire-distance"; + private static final String USE_FAR_FIRES_KEY = "perception.standard.use-far-fires"; + private static final String HP_PRECISION_KEY = "perception.standard.hp-precision"; + private static final String DAMAGE_PRECISION_KEY = "perception.standard.damage-precision"; + + private static final int PRECISION_STEP_SIZE = 1000; + private static final int PRECISION_MAX = 10000; + private static final int VIEW_DISTANCE_MAX = 1000000; + private static final int VIEW_DISTANCE_MAJOR_TICK = 100000; + private static final int VIEW_DISTANCE_MINOR_TICK = 10000; + private static final int PRECISION_MAJOR_TICK = 1000; + private static final int PRECISION_MINOR_TICK = 100; + + private int viewDistance; + private int farFireDistance; + private boolean useFarFires; + private int hpPrecision; + private int damagePrecision; + private StandardWorldModel world; + private int time; + private Set<Building> unburntBuildings; + private Map<Building, Integer> ignitionTimes; + private Config config; + + // Lock object for updating via the GUI. + private final Object lock = new Object(); + + /** + * Create a StandardPerception object. + */ + public StandardPerception() { + } + + @Override + public void initialise(Config newConfig, WorldModel<? extends Entity> model) { + world = StandardWorldModel.createStandardWorldModel(model); + this.config = newConfig; + viewDistance = config.getIntValue(VIEW_DISTANCE_KEY); + farFireDistance = config.getIntValue(FAR_FIRE_DISTANCE_KEY); + useFarFires = config.getBooleanValue(USE_FAR_FIRES_KEY, DEFAULT_USE_FAR_FIRES); + hpPrecision = config.getIntValue(HP_PRECISION_KEY, DEFAULT_HP_PRECISION); + damagePrecision = config.getIntValue(DAMAGE_PRECISION_KEY, DEFAULT_DAMAGE_PRECISION); + ignitionTimes = new HashMap<Building, Integer>(); + unburntBuildings = new HashSet<Building>(); + time = 0; + for (StandardEntity next : world) { + if (next instanceof Building) { + Building b = (Building) next; + if (!b.isFierynessDefined() || b.getFieryness() == 0) { + unburntBuildings.add(b); + } else { + ignitionTimes.put(b, time); + } + } + } + } + + @Override + public String toString() { + return "Standard perception"; + } + + @Override + public JComponent getGUIComponent() { + return new TunePanel(); + } + + @Override + public String getGUIComponentName() { + return "Perception parameters"; + } + + @Override + public void setTime(int timestep) { + // Look for buildings that caught fire last timestep + for (Iterator<Building> it = unburntBuildings.iterator(); it.hasNext();) { + Building next = it.next(); + if (next.isFierynessDefined()) { + switch (next.getFierynessEnum()) { + case HEATING: + case BURNING: + case INFERNO: + ignitionTimes.put(next, time); + it.remove(); + break; + default: + // Ignore + } + } + } + time = timestep; + // Look for scripting elements in the config file + checkForScript(); + } + + @Override + public ChangeSet getVisibleEntities(AgentProxy agent) { + synchronized (lock) { + StandardEntity agentEntity = (StandardEntity) agent.getControlledEntity(); + ChangeSet result = new ChangeSet(); + // Look for roads/nodes/buildings/humans within range + Pair<Integer, Integer> location = agentEntity.getLocation(world); + if (location != null) { + int x = location.first().intValue(); + int y = location.second().intValue(); + Collection<StandardEntity> nearby = world.getObjectsInRange(x, y, viewDistance); + // Copy entities and set property values + for (StandardEntity next : nearby) { + StandardEntityURN urn = next.getStandardURN(); + switch (urn) { + case ROAD: + case HYDRANT: + addRoadProperties((Road) next, result); + break; + + case REFUGE: + if (agentEntity instanceof Human) + if (((Human) agentEntity).getPosition(world) == next) { + addRefugeProperties((Refuge) next, result); + } + addBuildingProperties((Building) next, result); + break; + case BUILDING: + case GAS_STATION: + case FIRE_STATION: + case AMBULANCE_CENTRE: + case POLICE_OFFICE: + addBuildingProperties((Building) next, result); + break; + case CIVILIAN: + case FIRE_BRIGADE: + case AMBULANCE_TEAM: + case POLICE_FORCE: + // Always send all properties of the agent-controlled object + if (next == agentEntity) { + addSelfProperties((Human) next, result); + } else { + addHumanProperties((Human) next, result); + } + break; + case BLOCKADE: + addBlockadeProperties((Blockade) next, result); + break; + default: + // Ignore other types + break; + } + } + // Now look for far fires + if (useFarFires) { + for (Map.Entry<Building, Integer> next : ignitionTimes.entrySet()) { + Building b = next.getKey(); + int ignitionTime = next.getValue(); + int timeDelta = time - ignitionTime; + int visibleRange = timeDelta * farFireDistance; + int range = world.getDistance(agentEntity, b); + if (range <= visibleRange) { + addFarBuildingProperties(b, result); + } + } + } + + if (agentEntity instanceof AmbulanceCentre) { + Collection<StandardEntity> refuges = world.getEntitiesOfType(StandardEntityURN.REFUGE); + for (StandardEntity next : refuges) { + addRefugeProperties((Refuge) next, result); + } + } + } + return result; + } + } + + private void addRoadProperties(Road road, ChangeSet result) { + addAreaProperties(road, result); + // Only update blockades + result.addChange(road, road.getBlockadesProperty()); + // Also update each blockade + if (road.isBlockadesDefined()) { + for (EntityID id : road.getBlockades()) { + Blockade blockade = (Blockade) world.getEntity(id); + addBlockadeProperties(blockade, result); + } + } + } + + private void addBuildingProperties(Building building, ChangeSet result) { + addAreaProperties(building, result); + // Update TEMPERATURE, FIERYNESS and BROKENNESS + result.addChange(building, building.getTemperatureProperty()); + result.addChange(building, building.getFierynessProperty()); + result.addChange(building, building.getBrokennessProperty()); + } + + public void addRefugeProperties(Refuge refuge, ChangeSet result) { + result.addChange(refuge, refuge.getBedCapacityProperty()); + result.addChange(refuge, refuge.getOccupiedBedsProperty()); + result.addChange(refuge, refuge.getWaitingListSizeProperty()); + // TODO send other information e.g civilians info in the refuge + } + + private void addAreaProperties(Area area, ChangeSet result) { + } + + private void addFarBuildingProperties(Building building, ChangeSet result) { + // Update FIERYNESS only + result.addChange(building, building.getFierynessProperty()); + } + + private void addHumanProperties(Human human, ChangeSet result) { + // Update POSITION, POSITION_EXTRA, DIRECTION, STAMINA, HP, DAMAGE, BURIEDNESS + result.addChange(human, human.getPositionProperty()); + // result.addChange(human, human.getPositionExtraProperty()); + result.addChange(human, human.getXProperty()); + result.addChange(human, human.getYProperty()); + result.addChange(human, human.getDirectionProperty()); + result.addChange(human, human.getStaminaProperty()); + result.addChange(human, human.getBuriednessProperty()); + // Round HP and damage + IntProperty hp = (IntProperty) human.getHPProperty().copy(); + roundProperty(hp, hpPrecision); + result.addChange(human, hp); + IntProperty damage = (IntProperty) human.getDamageProperty().copy(); + roundProperty(damage, damagePrecision); + result.addChange(human, damage); + } + + private void addSelfProperties(Human human, ChangeSet result) { + // Update human properties and POSITION_HISTORY + addHumanProperties(human, result); + result.addChange(human, human.getPositionHistoryProperty()); + // Un-round hp and damage + result.addChange(human, human.getHPProperty()); + result.addChange(human, human.getDamageProperty()); + if (human instanceof FireBrigade) + result.addChange(human, ((FireBrigade) human).getWaterProperty()); + } + + private void addBlockadeProperties(Blockade blockade, ChangeSet result) { + result.addChange(blockade, blockade.getXProperty()); + result.addChange(blockade, blockade.getYProperty()); + result.addChange(blockade, blockade.getPositionProperty()); + result.addChange(blockade, blockade.getApexesProperty()); + result.addChange(blockade, blockade.getRepairCostProperty()); + } + + private void roundProperty(IntProperty p, int precision) { + if (precision != 1 && p.isDefined()) { + p.setValue(round(p.getValue(), precision)); + } + } + + private int round(int value, int precision) { + int remainder = value % precision; + value -= remainder; + if (remainder >= precision / 2) { + value += precision; + } + return value; + } + + private void updateViewDistance(int value) { + synchronized (lock) { + viewDistance = value; + } + } + + private void updateHPPrecision(int value) { + synchronized (lock) { + if (value == 0) { + value = 1; + } + hpPrecision = value; + } + } + + private void updateDamagePrecision(int value) { + synchronized (lock) { + if (value == 0) { + value = 1; + } + damagePrecision = value; + } + } + + private void updateUseFarFires(boolean value) { + synchronized (lock) { + useFarFires = value; + } + } + + private void checkForScript() { + viewDistance = config.getIntValue(VIEW_DISTANCE_KEY + ".script." + time, viewDistance); + farFireDistance = config.getIntValue(FAR_FIRE_DISTANCE_KEY + ".script." + time, farFireDistance); + useFarFires = config.getBooleanValue(USE_FAR_FIRES_KEY + ".script." + time, useFarFires); + hpPrecision = config.getIntValue(HP_PRECISION_KEY + ".script." + time, hpPrecision); + damagePrecision = config.getIntValue(DAMAGE_PRECISION_KEY + ".script." + time, damagePrecision); + } + + private class TunePanel extends JPanel { + private JSlider viewDistanceSlider; + private JSlider hpPrecisionSlider; + private JSlider damagePrecisionSlider; + private JCheckBox farFiresBox; + + public TunePanel() { + // CHECKSTYLE:OFF:MagicNumber + super(new GridLayout(1, 4)); + // CHECKSTYLE:ON:MagicNumber + viewDistanceSlider = new JSlider(SwingConstants.VERTICAL, 0, VIEW_DISTANCE_MAX, viewDistance); + hpPrecisionSlider = new JSlider(SwingConstants.VERTICAL, 0, PRECISION_MAX, hpPrecision); + damagePrecisionSlider = new JSlider(SwingConstants.VERTICAL, 0, PRECISION_MAX, damagePrecision); + farFiresBox = new JCheckBox("Use far fires?", useFarFires); + SliderComponent s = new SliderComponent(viewDistanceSlider); + s.setBorder(BorderFactory.createTitledBorder("View distance")); + add(s); + s = new SliderComponent(hpPrecisionSlider); + s.setBorder(BorderFactory.createTitledBorder("HP precision")); + add(s); + s = new SliderComponent(damagePrecisionSlider); + s.setBorder(BorderFactory.createTitledBorder("Damage precision")); + add(s); + add(farFiresBox); + viewDistanceSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + updateViewDistance(viewDistanceSlider.getValue()); + } + }); + hpPrecisionSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + updateHPPrecision(hpPrecisionSlider.getValue()); + } + }); + damagePrecisionSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + updateDamagePrecision(damagePrecisionSlider.getValue()); + } + }); + farFiresBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateUseFarFires(farFiresBox.isSelected()); + } + }); + // Add tick marks to sliders + viewDistanceSlider.setPaintLabels(true); + viewDistanceSlider.setPaintTicks(true); + viewDistanceSlider.setMajorTickSpacing(VIEW_DISTANCE_MAJOR_TICK); + viewDistanceSlider.setMinorTickSpacing(VIEW_DISTANCE_MINOR_TICK); + hpPrecisionSlider.setPaintLabels(true); + hpPrecisionSlider.setPaintTicks(true); + hpPrecisionSlider.setMajorTickSpacing(PRECISION_MAJOR_TICK); + hpPrecisionSlider.setMinorTickSpacing(PRECISION_MINOR_TICK); + damagePrecisionSlider.setPaintLabels(true); + damagePrecisionSlider.setPaintTicks(true); + damagePrecisionSlider.setMajorTickSpacing(PRECISION_MAJOR_TICK); + damagePrecisionSlider.setMinorTickSpacing(PRECISION_MINOR_TICK); + + Dictionary<Integer, JComponent> labels = new Hashtable<Integer, JComponent>(); + labels.put(1, new JLabel("Accurate")); + for (int i = PRECISION_STEP_SIZE; i <= PRECISION_MAX; i += PRECISION_STEP_SIZE) { + labels.put(i, new JLabel(String.valueOf(i))); + } + hpPrecisionSlider.setLabelTable(labels); + damagePrecisionSlider.setLabelTable(labels); + } + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/kernel/StandardWorldModelViewerComponent.java b/modules/standard/src/rescuecore2/standard/kernel/StandardWorldModelViewerComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..7526af8f0bf71ce10165ec083bd91ea257371894 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/StandardWorldModelViewerComponent.java @@ -0,0 +1,111 @@ +package rescuecore2.standard.kernel; + +import java.awt.Dimension; +import java.awt.BorderLayout; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import javax.swing.JComponent; +import javax.swing.JSplitPane; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JPanel; + +import java.util.List; + +import rescuecore2.view.EntityInspector; +import rescuecore2.view.ViewListener; +import rescuecore2.view.ViewComponent; +import rescuecore2.view.RenderedObject; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +//import rescuecore2.config.Config; +import rescuecore2.Timestep; +import rescuecore2.GUIComponent; + +import rescuecore2.standard.view.StandardWorldModelViewer; + +import kernel.Kernel; +import kernel.KernelListenerAdapter; + +/** + A KernelGUIComponent that will view a standard world model. +*/ +public class StandardWorldModelViewerComponent extends KernelListenerAdapter implements GUIComponent { + private static final int SIZE = 500; + + private StandardWorldModelViewer viewer; + private EntityInspector inspector; + private JTextField field; + private JComponent view; + private WorldModel<? extends Entity> world; + + /** + Construct a StandardWorldModelViewerComponent. + */ + public StandardWorldModelViewerComponent() { + viewer = new StandardWorldModelViewer(); + inspector = new EntityInspector(); + field = new JTextField(); + viewer.setPreferredSize(new Dimension(SIZE, SIZE)); + viewer.addViewListener(new ViewListener() { + @Override + public void objectsClicked(ViewComponent v, List<RenderedObject> objects) { + for (RenderedObject next : objects) { + if (next.getObject() instanceof Entity) { + inspector.inspect((Entity)next.getObject()); + field.setText(""); + return; + } + } + } + + @Override + public void objectsRollover(ViewComponent v, List<RenderedObject> objects) { + } + }); + field.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + String s = field.getText(); + try { + int id = Integer.parseInt(s); + EntityID eid = new EntityID(id); + Entity e = world.getEntity(eid); + inspector.inspect(e); + } + catch (NumberFormatException e) { + field.setText(""); + } + } + }); + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewer, new JScrollPane(inspector)); + split.setResizeWeight(0.8); + view = new JPanel(new BorderLayout()); + view.add(split, BorderLayout.CENTER); + view.add(field, BorderLayout.NORTH); + } + + @Override + public void simulationStarted(Kernel kernel) { + viewer.initialise(kernel.getConfig()); + world = kernel.getWorldModel(); + viewer.view(world); + viewer.repaint(); + } + + @Override + public void timestepCompleted(Kernel kernel, Timestep time) { + viewer.view(kernel.getWorldModel(), time.getCommands(), time.getChangeSet()); + viewer.repaint(); + } + + @Override + public JComponent getGUIComponent() { + return view; + } + + @Override + public String getGUIComponentName() { + return "World view"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/AbstractChannel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/AbstractChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..6c3785d66fe8ce0c0d439fd971dfba78be1f192a --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/AbstractChannel.java @@ -0,0 +1,164 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Collection; +import java.util.Map; +import java.util.HashSet; +import java.util.ArrayList; + +import rescuecore2.worldmodel.Entity; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Abstract base class for channels. +*/ +public abstract class AbstractChannel implements Channel { + /** The set of subscribers. */ + protected Collection<Entity> subscribers; + + /** This channels's ID. */ + protected int channelID; + + private Map<Entity, Collection<AKSpeak>> messagesForAgents; + private Noise inputNoise; + private Noise outputNoise; + + /** + Construct an AbstractChannel. + @param channelID The ID of this channel. + */ + protected AbstractChannel(int channelID) { + this.channelID = channelID; + subscribers = new HashSet<Entity>(); + messagesForAgents = new LazyMap<Entity, Collection<AKSpeak>>() { + @Override + public Collection<AKSpeak> createValue() { + return new ArrayList<AKSpeak>(); + } + }; + inputNoise = null; + outputNoise = null; + } + + @Override + public void setInputNoise(Noise noise) { + inputNoise = noise; + } + + @Override + public void setOutputNoise(Noise noise) { + outputNoise = noise; + } + + @Override + public void timestep() { + messagesForAgents.clear(); + } + + @Override + public void addSubscriber(Entity a) { + subscribers.add(a); + } + + @Override + public void removeSubscriber(Entity a) { + subscribers.remove(a); + } + + @Override + public Collection<Entity> getSubscribers() { + return subscribers; + } + + @Override + public Collection<AKSpeak> getMessagesForAgent(Entity agent) { + Collection<AKSpeak> c = messagesForAgents.get(agent); + return new ArrayList<AKSpeak>(c); + } + + @Override + public final void push(AKSpeak speak) throws InvalidMessageException { + int channel = speak.getChannel(); + if (channel != channelID) { + throw new InvalidMessageException("Tried to push '" + speak + "' to channel " + channelID); + } + int originalSize = speak.getContent().length; + if(originalSize == 0){ + throw new InvalidMessageException("Tried to push empty message to channel " + channelID); + } + Logger.debug("Pushing " + speak + " through channel " + channelID); + + speak = applyInputNoise(speak); + Logger.debug("Input noise result: " + speak); +// if (speak != null) { + pushImpl(speak,originalSize); +// } + } + + /** + Push a message after input noise has been applied. + @param msg The message. + @throws InvalidMessageException If the message is invalid. + */ + protected abstract void pushImpl(AKSpeak msg, int originalSize) throws InvalidMessageException; + + /** + Register a message that should be send to an agent on the next call to @{link #getMessagesForAgent(AgentProxy)}. This method will ignore the subscribers list so subclasses should use @{link #isSubscribed(Entity)} if they wish to restrict messages to subscribers only. + @param a The agent. + @param msg The message. + */ + protected void addMessageForAgent(Entity a, AKSpeak msg) { + Logger.debug("Adding message " + msg + " for agent " + a); + msg = applyOutputNoise(msg); + Logger.debug("Output noise result: " + msg); + if (msg != null) { + Collection<AKSpeak> c = messagesForAgents.get(a); + c.add(msg); + } + } + + /** + Register a message that should be send to all subscribers. + @param msg The message. + */ + protected void addMessageForSubscribers(AKSpeak msg) { + for (Entity e : subscribers) { + addMessageForAgent(e, msg); + } + } + + /** + Find out if an entity is subscribed to this channel. + @param e The entity to check. + @return True iff the entity is subscribed to this channel. + */ + protected boolean isSubscribed(Entity e) { + return subscribers.contains(e); + } + + /** + Apply the input noise to a message. + @param msg The message to apply input noise to. + @return A message with noise added, or the original message, or null. + */ + private AKSpeak applyInputNoise(AKSpeak msg) { + if (inputNoise != null) { + return inputNoise.applyNoise(msg); + } + return msg; + } + + /** + Apply the output noise to a message. + @param msg The message to apply output noise to. + @return A message with noise added, or the original message, or null. + */ + private AKSpeak applyOutputNoise(AKSpeak msg) { + if (outputNoise != null) { + return outputNoise.applyNoise(msg); + } + return msg; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/ChainedNoise.java b/modules/standard/src/rescuecore2/standard/kernel/comms/ChainedNoise.java new file mode 100644 index 0000000000000000000000000000000000000000..80727dd1ef050160a067be0a4a89f489e1084deb --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/ChainedNoise.java @@ -0,0 +1,61 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; + +import rescuecore2.standard.messages.AKSpeak; + +/** + A Noise implementation that chains Noise objects together. +*/ +public class ChainedNoise implements Noise { + private List<Noise> chain; + + /** + Create a ChainedNoise object with no children. + */ + public ChainedNoise() { + chain = new ArrayList<Noise>(); + } + + /** + Create a ChainedNoise object with a set of children. + @param chain The child noise objects. + */ + public ChainedNoise(Collection<Noise> chain) { + this(); + this.chain.addAll(chain); + } + + @Override + public AKSpeak applyNoise(AKSpeak message) { + AKSpeak current = message; + for (Noise next : chain) { + if (current == null) { + return null; + } + current = next.applyNoise(current); + } + return current; + } + + /** + Add a child. + @param child The child to add. This may be null. + */ + public void addChild(Noise child) { + if (child != null) { + chain.add(child); + } + } + + /** + Remove a child. + @param child The child to remove. + */ + public void removeChild(Noise child) { + chain.remove(child); + } +} + diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/Channel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/Channel.java new file mode 100644 index 0000000000000000000000000000000000000000..c50a1b5001b7cb10e4d372818088c6cd2d4df337 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/Channel.java @@ -0,0 +1,62 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Collection; + +import rescuecore2.worldmodel.Entity; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Interface for Channels used by the ChannelCommunicationModel. +*/ +public interface Channel { + /** + Notify the channel that a timestep has elapsed. + */ + void timestep(); + + /** + Add a subscriber to this channel. + @param e The subscriber. + */ + void addSubscriber(Entity e); + + /** + Remove a subscriber from this channel. + @param e The subscriber. + */ + void removeSubscriber(Entity e); + + /** + Get all subscribers. + @return All subscribers. + */ + Collection<Entity> getSubscribers(); + + /** + Push a message through this channel. + @param message The message to push. + @throws InvalidMessageException If the message is invalid. + */ + void push(AKSpeak message) throws InvalidMessageException; + + /** + Get the messages that should be send to an agent. + @param agent The agent to look up. + @return All messages for that agent. + */ + Collection<AKSpeak> getMessagesForAgent(Entity agent); + + /** + Set the input noise object for this channel. Input noise is applied to the message once on arrival. + @param noise The input noise. + */ + void setInputNoise(Noise noise); + + /** + Set the output noise object for this channel. Output noise is applied to the message once for each listener as it is sent. + @param noise The output noise. + */ + void setOutputNoise(Noise noise); +} + diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/ChannelCommunicationModel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/ChannelCommunicationModel.java new file mode 100644 index 0000000000000000000000000000000000000000..3eb9e093de46539746142a35a12ae01fbffb3433 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/ChannelCommunicationModel.java @@ -0,0 +1,226 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.List; + +import kernel.AbstractCommunicationModel; + +import rescuecore2.messages.Command; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.FireStation; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.PoliceOffice; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.standard.messages.AKSpeak; +import rescuecore2.standard.messages.AKSubscribe; + +/** + The channel-based communication model. + */ +public class ChannelCommunicationModel extends AbstractCommunicationModel { + /** The prefix for channels config options. */ + public static final String PREFIX = "comms.channels."; + + private static final String COUNT_KEY = "comms.channels.count"; + private static final String PLATOON_MAX_CHANNELS_KEY = "comms.channels.max.platoon"; + private static final String CENTRE_MAX_CHANNELS_KEY = "comms.channels.max.centre"; + + private static final String TYPE_SUFFIX = ".type"; + private static final String NOISE_SUFFIX = ".noise"; + private static final String INPUT_SUFFIX = ".input"; + private static final String OUTPUT_SUFFIX = ".output"; + + private static final String TYPE_VOICE = "voice"; + private static final String TYPE_RADIO = "radio"; + + private static final String NOISE_TYPE_DROPOUT = "dropout"; + private static final String NOISE_TYPE_STATIC = "static"; + + private Map<Integer, Channel> channels; + private int platoonMax; + private int centreMax; + private StandardWorldModel world; + + /** + Construct a ChannelCommunicationModel. + */ + public ChannelCommunicationModel() { + channels = new HashMap<Integer, Channel>(); + } + + @Override + public String toString() { + return "Channel communication model"; + } + + @Override + public void initialise(Config config, WorldModel<? extends Entity> model) { + super.initialise(config, model); + channels.clear(); + world = StandardWorldModel.createStandardWorldModel(model); + // Read the channel information + int count = config.getIntValue(COUNT_KEY); + for (int i = 0; i < count; ++i) { + String type = config.getValue(PREFIX + i + TYPE_SUFFIX); + Channel channel = null; + if (TYPE_VOICE.equals(type)) { + channel = new VoiceChannel(config, i, world); + } + else if (TYPE_RADIO.equals(type)) { + channel = new RadioChannel(config, i); + } + else { + Logger.error("Unrecognised channel type: " + PREFIX + i + TYPE_SUFFIX + " = '" + type + "'"); + } + if (channel != null) { + String key = PREFIX + i + NOISE_SUFFIX; + Noise input = createNoiseObjects(config, key + INPUT_SUFFIX); + Noise output = createNoiseObjects(config, key + OUTPUT_SUFFIX); + channel.setInputNoise(input); + channel.setOutputNoise(output); + channels.put(i, channel); + Logger.info("Created channel: " + channel); + } + } + platoonMax = config.getIntValue(PLATOON_MAX_CHANNELS_KEY, 1); + centreMax = config.getIntValue(CENTRE_MAX_CHANNELS_KEY, 2); + } + + @Override + public void process(int time, Collection<? extends Command> agentCommands) { + Logger.debug("ChannelCommunicationModel processing commands at time " + time + ": " + agentCommands); + super.process(time, agentCommands); + // Update all channels + for (Channel next : channels.values()) { + next.timestep(); + } + // Look for subscription commands + for (Command next : agentCommands) { + if (next instanceof AKSubscribe) { + processSubscribe((AKSubscribe)next); + } + } + // Now push all speak commands through the right channels + for (Command next : agentCommands) { + if (next instanceof AKSpeak) { + try { + AKSpeak speak = (AKSpeak)next; + int channelNumber = speak.getChannel(); + Channel channel = channels.get(channelNumber); + Logger.debug("Processing speak: " + speak); + if (channel == null) { + throw new InvalidMessageException("Unrecognised channel: " + channelNumber); + } + else { + channel.push(speak); + } + } + catch (InvalidMessageException e) { + Logger.warn("Invalid message: " + next + ": " + e.getMessage()); + } + } + } + // And find out what each agent can hear + for (Entity agent : world.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE, + StandardEntityURN.FIRE_STATION, + StandardEntityURN.POLICE_FORCE, + StandardEntityURN.POLICE_OFFICE, + StandardEntityURN.AMBULANCE_TEAM, + StandardEntityURN.AMBULANCE_CENTRE, + StandardEntityURN.CIVILIAN)) { + for (Channel channel : channels.values()) { + addHearing(agent, channel.getMessagesForAgent(agent)); + } + } + } + + /** + Get a view of all registered channels. + @return All channels. + */ + public Collection<Channel> getAllChannels() { + return Collections.unmodifiableCollection(channels.values()); + } + + private Noise createNoiseObjects(Config config, String key) { + ChainedNoise result = new ChainedNoise(); + result.addChild(lookForFailure(config, key)); + result.addChild(lookForDropout(config, key)); + result.addChild(lookForStatic(config, key)); + return result; + } + + private Noise lookForFailure(Config config, String key) { + if (config.getBooleanValue(key + ".failure.use", false)) { + return new FailureNoise(config.getFloatValue(key + ".failure.p"), config.getRandom()); + } + return null; + } + + private Noise lookForDropout(Config config, String key) { + if (config.getBooleanValue(key + ".dropout.use", false)) { + return new DropoutNoise(config.getFloatValue(key + ".dropout.p"), config.getRandom()); + } + return null; + } + + private Noise lookForStatic(Config config, String key) { + if (config.getBooleanValue(key + ".static.use", false)) { + return new StaticNoise(config.getFloatValue(key + ".static.p"), config.getRandom()); + } + return null; + } + + private void processSubscribe(AKSubscribe sub) { + Logger.debug("Processing subscribe message : " + sub); + List<Integer> requested = sub.getChannels(); + EntityID id = sub.getAgentID(); + Entity entity = world.getEntity(id); + if (entity == null) { + Logger.warn("Couldn't find entity " + id); + return; + } + int max; + if (entity instanceof FireBrigade || entity instanceof PoliceForce || entity instanceof AmbulanceTeam || entity instanceof Civilian) { + max = platoonMax; + } + else if (entity instanceof FireStation || entity instanceof PoliceOffice || entity instanceof AmbulanceCentre) { + max = centreMax; + } + else { + Logger.warn("I don't know how to handle subscriptions for this entity: " + entity); + return; + } + if (requested.size() > max) { + Logger.warn("Agent " + id + " tried to subscribe to " + requested.size() + " channels but only " + max + " allowed"); + return; + } + // Unsubscribe from all old channels + for (Channel next : channels.values()) { + next.removeSubscriber(entity); + } + // Subscribe to new channels + for (int next : requested) { + Channel channel = channels.get(next); + if (channel == null) { + Logger.warn("Agent " + id + " tried to subscribe to non-existant channel " + next); + } + else { + channel.addSubscriber(entity); + } + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/DropoutNoise.java b/modules/standard/src/rescuecore2/standard/kernel/comms/DropoutNoise.java new file mode 100644 index 0000000000000000000000000000000000000000..74e5c1ddc700bcf1655383a913022d3d032e8f0a --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/DropoutNoise.java @@ -0,0 +1,32 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Random; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Dropout noise completely zeroes a message with some probability. +*/ +public class DropoutNoise implements Noise { + private double p; + private Random random; + + /** + Construct a DropoutNoise object that will wipe out messages with some probability. + @param p The probability of destroying a message. + @param random The RNG to use. + */ + public DropoutNoise(double p, Random random) { + this.p = p; + this.random = random; + } + + @Override + public AKSpeak applyNoise(AKSpeak message) { + if (random.nextDouble() >= p) { + return message; + } + return new AKSpeak(message.getAgentID(), message.getTime(), message.getChannel(), new byte[0]); + } +} + diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/FailureNoise.java b/modules/standard/src/rescuecore2/standard/kernel/comms/FailureNoise.java new file mode 100644 index 0000000000000000000000000000000000000000..0fc870262c1144d18e4dfeadcae4c7ac3e82666b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/FailureNoise.java @@ -0,0 +1,31 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Random; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Failure noise drops an entire message with some probability. +*/ +public class FailureNoise implements Noise { + private double p; + private Random random; + + /** + Construct a FailureNoise object. + @param p The probability of dropping a message. + @param random The RNG to use. + */ + public FailureNoise(double p, Random random) { + this.p = p; + this.random = random; + } + + @Override + public AKSpeak applyNoise(AKSpeak message) { + if (random.nextDouble() < p) { + return null; + } + return message; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/InvalidMessageException.java b/modules/standard/src/rescuecore2/standard/kernel/comms/InvalidMessageException.java new file mode 100644 index 0000000000000000000000000000000000000000..fc4886f235debe68e7f4cee8649f0da77d6966ca --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/InvalidMessageException.java @@ -0,0 +1,14 @@ +package rescuecore2.standard.kernel.comms; + +/** + An exception indicating that an agent communication message was invalid. + */ +public class InvalidMessageException extends Exception { + /** + Create an InvalidMessageException with a particular error message. + @param msg The error message. + */ + public InvalidMessageException(String msg) { + super(msg); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/Noise.java b/modules/standard/src/rescuecore2/standard/kernel/comms/Noise.java new file mode 100644 index 0000000000000000000000000000000000000000..ef734a7dfbd19cf773208b6495003960b7416c86 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/Noise.java @@ -0,0 +1,16 @@ +package rescuecore2.standard.kernel.comms; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Noise implementations mess with messages in some way. +*/ +public interface Noise { + /** + Optionally apply some noise to a message and return either the original message or a replacement. + @param message The message to tinker with. + @return The original message or a replacement. + */ + AKSpeak applyNoise(AKSpeak message); +} + diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/RadioChannel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/RadioChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..0d0d445e535ee4575b96c779ddeee45c7e53a83f --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/RadioChannel.java @@ -0,0 +1,53 @@ +package rescuecore2.standard.kernel.comms; + +import rescuecore2.config.Config; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKSpeak; + +/** + A radio channel. +*/ +public class RadioChannel extends AbstractChannel { + private static final String BANDWIDTH_SUFFIX = ".bandwidth"; + + private int bandwidth; + private int usedBandwidth; + + /** + Create a RadioChannel. + @param config The configuration to read. + @param channelID The id of this channel. + */ + public RadioChannel(Config config, int channelID) { + super(channelID); + bandwidth = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + BANDWIDTH_SUFFIX); + } + + @Override + public void timestep() { + super.timestep(); + usedBandwidth = 0; + } + + @Override + public void pushImpl(AKSpeak speak, int originalSize) throws InvalidMessageException { + usedBandwidth += originalSize; + if(speak==null) + return; + + byte[] data = speak.getContent(); + if (usedBandwidth > bandwidth) { + throw new InvalidMessageException("Discarding message on channel " + channelID + ": already used " + usedBandwidth + " of " + bandwidth + " bytes, new message is " + data.length + " bytes."); + } + Logger.debug(this + " accepted message from " + speak.getAgentID()); + addMessageForSubscribers(speak); +// usedBandwidth += data.length; + } + + @Override + public String toString() { + return "Radio channel " + channelID + " (bandwidth = " + bandwidth + ")"; + } +} + diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/StandardCommunicationModel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/StandardCommunicationModel.java new file mode 100644 index 0000000000000000000000000000000000000000..e076a3232a6845352d0e2c24775f40f49ae4a70d --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/StandardCommunicationModel.java @@ -0,0 +1,217 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Collection; +import java.util.Map; + +import kernel.AbstractCommunicationModel; + +import rescuecore2.messages.Command; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.FireStation; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.PoliceOffice; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.AmbulanceCentre; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.messages.AKSay; +import rescuecore2.standard.messages.AKTell; + +/** + The legacy communication model: fire brigades talk to fire brigades and the fire station, police to police, ambulance to ambulance and centres talk to centres. + */ +public class StandardCommunicationModel extends AbstractCommunicationModel { + private static final String SAY_RANGE_KEY = "comms.standard.say.range"; + private static final String PLATOON_MAX_KEY = "comms.standard.platoon.max"; + private static final String MAX_SIZE_KEY = "comms.standard.size.max"; + + private StandardWorldModel world; + private int sayDistance; + private int maxSize; + private int platoonMax; + private int fsMax; + private int poMax; + private int acMax; + private Map<EntityID, Integer> uttered; + private Map<EntityID, Integer> heard; + + /** + Construct a StandardCommunicationModel. + */ + public StandardCommunicationModel() { + uttered = new LazyMap<EntityID, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + heard = new LazyMap<EntityID, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + } + + @Override + public String toString() { + return "Standard communication model"; + } + + @Override + public void initialise(Config config, WorldModel<? extends Entity> model) { + super.initialise(config, model); + world = StandardWorldModel.createStandardWorldModel(model); + sayDistance = config.getIntValue(SAY_RANGE_KEY); + maxSize = config.getIntValue(MAX_SIZE_KEY); + platoonMax = config.getIntValue(PLATOON_MAX_KEY); + fsMax = world.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE).size() * 2; + acMax = world.getEntitiesOfType(StandardEntityURN.AMBULANCE_TEAM).size() * 2; + poMax = world.getEntitiesOfType(StandardEntityURN.POLICE_FORCE).size() * 2; + } + + @Override + public void process(int time, Collection<? extends Command> agentCommands) { + super.process(time, agentCommands); + uttered.clear(); + heard.clear(); + for (Command next : agentCommands) { + try { + if (next instanceof AKSay) { + processSay((AKSay)next); + } + if (next instanceof AKTell) { + processTell((AKTell)next); + } + } + catch (InvalidMessageException e) { + Logger.warn("Invalid message: " + next, e); + } + } + } + + private void processSay(AKSay say) throws InvalidMessageException { + EntityID senderID = say.getAgentID(); + StandardEntity sender = world.getEntity(senderID); + if (!(sender instanceof Human)) { + throw new InvalidMessageException("Agent " + senderID + " is not a human: " + (sender == null ? "null" : sender.getClass().getName())); + } + byte[] data = say.getContent(); + if (data.length > maxSize) { + throw new InvalidMessageException("Agent " + senderID + " sent an oversize message: " + data.length + " > " + maxSize); + } + int count = uttered.get(senderID); + int max = getMessageMax(sender); + if (count >= getMessageMax(sender)) { + throw new InvalidMessageException("Agent " + senderID + " has uttered too many messages: " + count + " >= " + max); + } + uttered.put(senderID, count + 1); + for (StandardEntity receiver : world.getEntitiesOfType(StandardEntityURN.CIVILIAN, + StandardEntityURN.FIRE_BRIGADE, + StandardEntityURN.FIRE_STATION, + StandardEntityURN.AMBULANCE_TEAM, + StandardEntityURN.AMBULANCE_CENTRE, + StandardEntityURN.POLICE_FORCE, + StandardEntityURN.POLICE_OFFICE)) { + int h = heard.get(receiver.getID()); + if (h >= getMessageMax(receiver)) { + continue; + } + int distance = world.getDistance(sender, receiver); + if (distance <= sayDistance) { + addHearing(receiver, say); + heard.put(receiver.getID(), h + 1); + } + } + } + + private void processTell(AKTell tell) throws InvalidMessageException { + EntityID senderID = tell.getAgentID(); + StandardEntity sender = world.getEntity(senderID); + if (!(sender instanceof Human)) { + throw new InvalidMessageException("Agent " + senderID + " is not a human: " + (sender == null ? "null" : sender.getClass().getName())); + } + byte[] data = tell.getContent(); + if (data.length > maxSize) { + throw new InvalidMessageException("Agent " + senderID + " sent an oversize message: " + data.length + " > " + maxSize); + } + int count = uttered.get(senderID); + int max = getMessageMax(sender); + if (count >= getMessageMax(sender)) { + throw new InvalidMessageException("Agent " + senderID + " has uttered too many messages: " + count + " >= " + max); + } + uttered.put(senderID, count + 1); + for (StandardEntity receiver : world.getEntitiesOfType(StandardEntityURN.CIVILIAN, + StandardEntityURN.FIRE_BRIGADE, + StandardEntityURN.FIRE_STATION, + StandardEntityURN.AMBULANCE_TEAM, + StandardEntityURN.AMBULANCE_CENTRE, + StandardEntityURN.POLICE_FORCE, + StandardEntityURN.POLICE_OFFICE)) { + int h = heard.get(receiver.getID()); + if (h >= getMessageMax(receiver)) { + continue; + } + if (canHear(receiver, sender)) { + addHearing(receiver, tell); + heard.put(receiver.getID(), h + 1); + } + } + } + + private int getMessageMax(StandardEntity e) { + if (e instanceof FireStation) { + return fsMax; + } + if (e instanceof PoliceOffice) { + return poMax; + } + if (e instanceof AmbulanceCentre) { + return acMax; + } + if (e instanceof Human) { + return platoonMax; + } + return 0; + } + + private boolean canHear(StandardEntity receiver, StandardEntity sender) { + if (receiver instanceof FireBrigade) { + return sender instanceof FireBrigade || sender instanceof FireStation; + } + if (receiver instanceof FireStation) { + return sender instanceof FireBrigade + || sender instanceof FireStation + || sender instanceof PoliceOffice + || sender instanceof AmbulanceCentre; + } + if (receiver instanceof PoliceForce) { + return sender instanceof PoliceForce || sender instanceof PoliceOffice; + } + if (receiver instanceof PoliceOffice) { + return sender instanceof PoliceForce + || sender instanceof FireStation + || sender instanceof PoliceOffice + || sender instanceof AmbulanceCentre; + } + if (receiver instanceof AmbulanceTeam) { + return sender instanceof AmbulanceTeam || sender instanceof AmbulanceCentre; + } + if (receiver instanceof AmbulanceCentre) { + return sender instanceof AmbulanceTeam + || sender instanceof FireStation + || sender instanceof PoliceOffice + || sender instanceof AmbulanceCentre; + } + return false; + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/StaticNoise.java b/modules/standard/src/rescuecore2/standard/kernel/comms/StaticNoise.java new file mode 100644 index 0000000000000000000000000000000000000000..c113cfe451be900141af13cc7de339aa8910212c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/StaticNoise.java @@ -0,0 +1,39 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Random; + +import rescuecore2.standard.messages.AKSpeak; + +/** + Static noise flips bits in the message with some probability. +*/ +public class StaticNoise implements Noise { + private static final int BITS = 8; + + private double p; + private Random random; + + /** + Construct a StaticNoise object that will flip bits with some probability. + @param p The probability of flipping a bit. + @param random The RNG to use. + */ + public StaticNoise(double p, Random random) { + this.p = p; + this.random = random; + } + + @Override + public AKSpeak applyNoise(AKSpeak message) { + byte[] data = message.getContent(); + for (int i = 0; i < data.length; ++i) { + for (int j = 0; j < BITS; ++j) { + if (random.nextDouble() < p) { + // Flip this bit + data[i] = (byte)(data[i] ^ (1 << j)); + } + } + } + return new AKSpeak(message.getAgentID(), message.getTime(), message.getChannel(), data); + } +} diff --git a/modules/standard/src/rescuecore2/standard/kernel/comms/VoiceChannel.java b/modules/standard/src/rescuecore2/standard/kernel/comms/VoiceChannel.java new file mode 100644 index 0000000000000000000000000000000000000000..65dc62ff57d60736f1f0af31f3d5ffb0a43d3cd9 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/kernel/comms/VoiceChannel.java @@ -0,0 +1,91 @@ +package rescuecore2.standard.kernel.comms; + +import java.util.Map; + +import rescuecore2.config.Config; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.log.Logger; + +import rescuecore2.standard.messages.AKSpeak; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.Human; + +/** + A voice channel. +*/ +public class VoiceChannel extends AbstractChannel { + // Config option suffixes + private static final String RANGE_SUFFIX = ".range"; + private static final String MESSAGE_SIZE_SUFFIX = ".messages.size"; + private static final String MESSAGE_MAX_SUFFIX = ".messages.max"; + + private int range; + private int maxSize; + private int maxMessages; + private Map<EntityID, Integer> uttered; + private StandardWorldModel world; + + /** + Create a VoiceChannel. + @param config The configuration to read. + @param channelID The id of this channel. + @param world The world model. + */ + public VoiceChannel(Config config, int channelID, StandardWorldModel world) { + super(channelID); + this.world = world; + range = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + RANGE_SUFFIX); + maxSize = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + MESSAGE_SIZE_SUFFIX); + maxMessages = config.getIntValue(ChannelCommunicationModel.PREFIX + channelID + MESSAGE_MAX_SUFFIX); + uttered = new LazyMap<EntityID, Integer>() { + @Override + public Integer createValue() { + return 0; + } + }; + } + + @Override + public void timestep() { + super.timestep(); + uttered.clear(); + } + + @Override + protected void pushImpl(AKSpeak speak, int originalSize) throws InvalidMessageException { + if(speak==null) + return; + + EntityID agentID = speak.getAgentID(); + Entity e = world.getEntity(agentID); + if (!(e instanceof Human)) { + throw new InvalidMessageException("Agent " + agentID + " is not a human: " + (e == null ? "null" : e.getClass().getName())); + } + byte[] data = speak.getContent(); + int count = uttered.get(agentID); + if (count >= maxMessages) { + throw new InvalidMessageException("Agent " + agentID + " has uttered too many voice messages on " + this); + } + if (originalSize > maxSize) { + throw new InvalidMessageException("Agent " + agentID + " tried to send an oversize voice message: " + data.length + " bytes but the limit is " + maxSize); + } + uttered.put(agentID, count + 1); + // Find out who can hear it + StandardEntity sender = world.getEntity(agentID); + for (StandardEntity target : world.getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.POLICE_FORCE, StandardEntityURN.CIVILIAN)) { + if (world.getDistance(sender, target) <= range) { + Logger.debug(target + " can hear voice message from " + sender); + addMessageForAgent(target, speak); + } + } + } + + @Override + public String toString() { + return "Voice channel " + channelID + " (range = " + range + ", max " + maxMessages + " messages of size " + maxSize + ")"; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKClear.java b/modules/standard/src/rescuecore2/standard/messages/AKClear.java new file mode 100644 index 0000000000000000000000000000000000000000..ca3995472633b42901749efea676742fedad6795 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKClear.java @@ -0,0 +1,72 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Clear command. + */ +public class AKClear extends AbstractCommand { + + private EntityIDComponent target; + + /** + * An AKClear message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKClear(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKClear command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param target The id of the entity to clear. + */ + public AKClear(EntityID agent, int time, EntityID target) { + this(); + setAgentID(agent); + setTime(time); + this.target.setValue(target); + } + + private AKClear() { + super(StandardMessageURN.AK_CLEAR); + target = new EntityIDComponent(StandardMessageComponentURN.Target); + addMessageComponent(target); + } + + public AKClear(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the desired target. + * + * @return The target ID. + */ + public EntityID getTarget() { + return target.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("Target", this.getTarget()); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKClearArea.java b/modules/standard/src/rescuecore2/standard/messages/AKClearArea.java new file mode 100644 index 0000000000000000000000000000000000000000..85553bc2dc5f5d1d3192e9ce588178a41c872957 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKClearArea.java @@ -0,0 +1,88 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Clear command. + */ +public class AKClearArea extends AbstractCommand { + + private IntComponent x; + private IntComponent y; + + /** + * An AKClearArea message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKClearArea(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKClearArea command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param destinationX The X coordinate of the desired destination to clear. + * @param destinationY The Y coordinate of the desired destination to clear. + */ + public AKClearArea(EntityID agent, int time, int destinationX, + int destinationY) { + this(); + setAgentID(agent); + setTime(time); + this.x.setValue(destinationX); + this.y.setValue(destinationY); + } + + private AKClearArea() { + super(StandardMessageURN.AK_CLEAR_AREA); + x = new IntComponent(StandardMessageComponentURN.DestinationX); + y = new IntComponent(StandardMessageComponentURN.DestinationY); + addMessageComponent(x); + addMessageComponent(y); + } + + public AKClearArea(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the destination X coordinate. + * + * @return The destination X coordination. + */ + public int getDestinationX() { + return x.getValue(); + } + + /** + * Get the destination Y coordinate. + * + * @return The destination Y coordination. + */ + public int getDestinationY() { + return y.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("X", this.getDestinationX()); + jsonObject.put("Y", this.getDestinationY()); + + return jsonObject; + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/messages/AKExtinguish.java b/modules/standard/src/rescuecore2/standard/messages/AKExtinguish.java new file mode 100644 index 0000000000000000000000000000000000000000..2aca9fc1c20e83c4802648af3152bad83e5a54f5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKExtinguish.java @@ -0,0 +1,87 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Extinguish command. + */ +public class AKExtinguish extends AbstractCommand { + + private EntityIDComponent target; + private IntComponent water; + + /** + * An AKExtinguish message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKExtinguish(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKExtinguish command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param target The id of the entity to extinguish. + * @param water The amount of water to use. + */ + public AKExtinguish(EntityID agent, int time, EntityID target, int water) { + this(); + setAgentID(agent); + setTime(time); + this.target.setValue(target); + this.water.setValue(water); + } + + private AKExtinguish() { + super(StandardMessageURN.AK_EXTINGUISH); + target = new EntityIDComponent(StandardMessageComponentURN.Target); + water = new IntComponent(StandardMessageComponentURN.Water); + addMessageComponent(target); + addMessageComponent(water); + } + + public AKExtinguish(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the desired target. + * + * @return The target ID. + */ + public EntityID getTarget() { + return target.getValue(); + } + + /** + * Get the amount of water. + * + * @return The amount of water to use. + */ + public int getWater() { + return water.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("Target", this.getTarget()); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKLoad.java b/modules/standard/src/rescuecore2/standard/messages/AKLoad.java new file mode 100644 index 0000000000000000000000000000000000000000..455d923d4253a3addba257d28665328ae45f976b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKLoad.java @@ -0,0 +1,72 @@ +package rescuecore2.standard.messages; + +import java.io.IOException; +import java.io.InputStream; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Load command. + */ +public class AKLoad extends AbstractCommand { + + private EntityIDComponent target; + + /** + * An AKLoad message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKLoad(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKLoad command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param target The id of the entity to load. + */ + public AKLoad(EntityID agent, int time, EntityID target) { + this(); + setAgentID(agent); + setTime(time); + this.target.setValue(target); + } + + private AKLoad() { + super(StandardMessageURN.AK_LOAD); + target = new EntityIDComponent(StandardMessageComponentURN.Target); + addMessageComponent(target); + } + + public AKLoad(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the desired target. + * + * @return The target ID. + */ + public EntityID getTarget() { + return target.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("Target", this.getTarget()); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKMove.java b/modules/standard/src/rescuecore2/standard/messages/AKMove.java new file mode 100644 index 0000000000000000000000000000000000000000..583efdaaa1bbbd29434a9c81fff8a451c19e1abe --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKMove.java @@ -0,0 +1,121 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; +import java.util.List; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.EntityIDListComponent; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent move command. + */ +public class AKMove extends AbstractCommand { + + private EntityIDListComponent path; + private IntComponent x; + private IntComponent y; + + /** + * An AKMove message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKMove(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct a move command. + * + * @param time The time the command was issued. + * @param agent The ID of the agent issuing the command. + * @param path The path to move. + */ + public AKMove(EntityID agent, int time, List<EntityID> path) { + this(); + setAgentID(agent); + setTime(time); + this.path.setIDs(path); + this.x.setValue(-1); + this.y.setValue(-1); + } + + /** + * Construct a move command. + * + * @param time The time the command was issued. + * @param agent The ID of the agent issuing the command. + * @param path The path to move. + * @param destinationX The X coordinate of the desired destination. + * @param destinationY The Y coordinate of the desired destination. + */ + public AKMove(EntityID agent, int time, List<EntityID> path, + int destinationX, int destinationY) { + this(); + setAgentID(agent); + setTime(time); + this.path.setIDs(path); + this.x.setValue(destinationX); + this.y.setValue(destinationY); + } + + private AKMove() { + super(StandardMessageURN.AK_MOVE); + path = new EntityIDListComponent(StandardMessageComponentURN.Path); + x = new IntComponent(StandardMessageComponentURN.DestinationX); + y = new IntComponent(StandardMessageComponentURN.DestinationY); + addMessageComponent(path); + addMessageComponent(x); + addMessageComponent(y); + } + + public AKMove(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the desired movement path. + * + * @return The movement path. + */ + public List<EntityID> getPath() { + return path.getIDs(); + } + + /** + * Get the destination X coordinate. + * + * @return The destination X coordination. + */ + public int getDestinationX() { + return x.getValue(); + } + + /** + * Get the destination Y coordinate. + * + * @return The destination Y coordination. + */ + public int getDestinationY() { + return y.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("Path", this.getPath()); + jsonObject.put("X", this.getDestinationX()); + jsonObject.put("Y", this.getDestinationX()); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKRescue.java b/modules/standard/src/rescuecore2/standard/messages/AKRescue.java new file mode 100644 index 0000000000000000000000000000000000000000..919c1ff6c224c9ce7ffc19056f8aa5e858e496fa --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKRescue.java @@ -0,0 +1,72 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import org.json.JSONObject; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.EntityIDComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Rescue command. + */ +public class AKRescue extends AbstractCommand { + + private EntityIDComponent target; + + /** + * An AKRescue message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKRescue(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKRescue command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param target The id of the entity to rescue. + */ + public AKRescue(EntityID agent, int time, EntityID target) { + this(); + setAgentID(agent); + setTime(time); + this.target.setValue(target); + } + + private AKRescue() { + super(StandardMessageURN.AK_RESCUE); + target = new EntityIDComponent(StandardMessageComponentURN.Target); + addMessageComponent(target); + } + + public AKRescue(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the desired target. + * + * @return The target ID. + */ + public EntityID getTarget() { + return target.getValue(); + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = super.toJson(); + jsonObject.put("Target", this.getTarget()); + + return jsonObject; + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKRest.java b/modules/standard/src/rescuecore2/standard/messages/AKRest.java new file mode 100644 index 0000000000000000000000000000000000000000..2cbbeaeb230f63a62e994831664796b87932c671 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKRest.java @@ -0,0 +1,47 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent rest command. + */ +public class AKRest extends AbstractCommand { + + /** + * An AKRest message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKRest(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct a rest command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + */ + public AKRest(EntityID agent, int time) { + this(); + setAgentID(agent); + setTime(time); + } + + private AKRest() { + super(StandardMessageURN.AK_REST); + } + + public AKRest(MessageProto proto) { + this(); + fromMessageProto(proto); + } + +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKSay.java b/modules/standard/src/rescuecore2/standard/messages/AKSay.java new file mode 100644 index 0000000000000000000000000000000000000000..326b69791ea8bfcf32d01f382409ceeffb6fd3c3 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKSay.java @@ -0,0 +1,76 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.RawDataComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent say command. + */ +public class AKSay extends AbstractCommand { + + private RawDataComponent data; + + + /** + * An AKSay message that populates its data from a stream. + * + * @param in + * The InputStream to read. + * @throws IOException + * If there is a problem reading the stream. + */ + public AKSay( InputStream in ) throws IOException { + this(); + read( in ); + } + + + /** + * Construct a say command. + * + * @param agent + * The ID of the agent issuing the command. + * @param data + * The content of the command. + * @param time + * The time the command was issued. + */ + public AKSay( EntityID agent, int time, byte[] data ) { + this(); + setAgentID( agent ); + setTime( time ); + this.data.setData( data ); + } + + + /** + * Create an empty AKSay command. + */ + private AKSay() { + super( StandardMessageURN.AK_SAY ); + data = new RawDataComponent( StandardMessageComponentURN.Message ); + addMessageComponent( data ); + } + + + public AKSay(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + +/** + * Get the content of the message. + * + * @return The message content. + */ + public byte[] getContent() { + return data.getData(); + } + +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKSpeak.java b/modules/standard/src/rescuecore2/standard/messages/AKSpeak.java new file mode 100644 index 0000000000000000000000000000000000000000..3aba9438f5ab8c8a18a1016ddf9af48c9fc8aa6a --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKSpeak.java @@ -0,0 +1,77 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.RawDataComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent speak (channel) command. + */ +public class AKSpeak extends AbstractCommand { + + private IntComponent channel; + private RawDataComponent data; + + /** + * An AKSpeak message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKSpeak(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct a speak command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param channel The ID of the channel to speak on. + * @param data The content of the message. + */ + public AKSpeak(EntityID agent, int time, int channel, byte[] data) { + this(); + setAgentID(agent); + setTime(time); + this.channel.setValue(channel); + this.data.setData(data); + } + + private AKSpeak() { + super(StandardMessageURN.AK_SPEAK); + channel = new IntComponent(StandardMessageComponentURN.Channel); + data = new RawDataComponent(StandardMessageComponentURN.Message); + addMessageComponent(channel); + addMessageComponent(data); + } + + public AKSpeak(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the channel that was used. + * + * @return The channel. + */ + public int getChannel() { + return channel.getValue(); + } + + /** + * Get the content of the message. + * + * @return The message content. + */ + public byte[] getContent() { + return data.getData(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKSubscribe.java b/modules/standard/src/rescuecore2/standard/messages/AKSubscribe.java new file mode 100644 index 0000000000000000000000000000000000000000..dda59989643fa9aba1fbf666456bb709b0e7501d --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKSubscribe.java @@ -0,0 +1,64 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import java.util.List; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.IntListComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent channel subscription command. + */ +public class AKSubscribe extends AbstractCommand { + + private IntListComponent channels; + + /** + * An AKSubscribe message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKSubscribe(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct a subscribe command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param channels The IDs of the channels to speak on. + */ + public AKSubscribe(EntityID agent, int time, int... channels) { + this(); + setAgentID(agent); + setTime(time); + this.channels.setValues(channels); + } + + private AKSubscribe() { + super(StandardMessageURN.AK_SUBSCRIBE); + channels = new IntListComponent(StandardMessageComponentURN.Channels); + addMessageComponent(channels); + } + + public AKSubscribe(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the channels that have been requested. + * + * @return The requested channels. + */ + public List<Integer> getChannels() { + return channels.getValues(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKTell.java b/modules/standard/src/rescuecore2/standard/messages/AKTell.java new file mode 100644 index 0000000000000000000000000000000000000000..5727363aa44dc660dfc1cff90ed6baf2f7d061ac --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKTell.java @@ -0,0 +1,66 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.components.IntComponent; +import rescuecore2.messages.components.RawDataComponent; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent tell command. + */ +public class AKTell extends AbstractCommand { + + private IntComponent channel; + private RawDataComponent data; + + /** + * An AKTell message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKTell(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an AKTell command. + * + * @param agent The ID of the agent issuing the command. + * @param time The time the command was issued. + * @param data The content of the command. + */ + public AKTell(EntityID agent, int time, byte[] data) { + this(); + setAgentID(agent); + setTime(time); + this.data.setData(data); + } + + private AKTell() { + super(StandardMessageURN.AK_TELL); + channel = new IntComponent(StandardMessageComponentURN.Channel); + data = new RawDataComponent(StandardMessageComponentURN.Message); + addMessageComponent(channel); + addMessageComponent(data); + } + + public AKTell(MessageProto proto) { + this(); + fromMessageProto(proto); + } + + /** + * Get the content of the message. + * + * @return The message content. + */ + public byte[] getContent() { + return data.getData(); + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/AKUnload.java b/modules/standard/src/rescuecore2/standard/messages/AKUnload.java new file mode 100644 index 0000000000000000000000000000000000000000..044cf4b70cec7c570f1adf30c9b57efe6a42e924 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/AKUnload.java @@ -0,0 +1,47 @@ +package rescuecore2.standard.messages; + +import java.io.InputStream; +import java.io.IOException; + +import rescuecore2.messages.AbstractCommand; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.worldmodel.EntityID; + +/** + * An agent Unload command. + */ +public class AKUnload extends AbstractCommand { + + /** + * An AKUnload message that populates its data from a stream. + * + * @param in The InputStream to read. + * @throws IOException If there is a problem reading the stream. + */ + public AKUnload(InputStream in) throws IOException { + this(); + read(in); + } + + /** + * Construct an unload command. + * + * @param agentID The ID of the agent issuing the command. + * @param time The time the command was issued. + */ + public AKUnload(EntityID agentID, int time) { + this(); + setAgentID(agentID); + setTime(time); + } + + private AKUnload() { + super(StandardMessageURN.AK_UNLOAD); + } + + public AKUnload(MessageProto proto) { + this(); + fromMessageProto(proto); + } + +} diff --git a/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentFactory.java b/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c961d1ddefdb2ad59f038d1a93c39bf571fdcd6d --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentFactory.java @@ -0,0 +1,21 @@ +package rescuecore2.standard.messages; + +import rescuecore2.registry.AbstractMessageComponentFactory; + +/** + * A factory for standard messages. + */ +public final class StandardMessageComponentFactory + extends AbstractMessageComponentFactory<StandardMessageComponentURN> { + + /** Singleton instance. */ + public static final StandardMessageComponentFactory INSTANCE = new StandardMessageComponentFactory(); + + /** + * Singleton class: private constructor. + */ + private StandardMessageComponentFactory() { + super(StandardMessageComponentURN.class); + } + +} diff --git a/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentURN.java b/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentURN.java new file mode 100644 index 0000000000000000000000000000000000000000..1531124ecb5c18f8de2622ad52b2744884c9e1aa --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/StandardMessageComponentURN.java @@ -0,0 +1,46 @@ +package rescuecore2.standard.messages; + +import static rescuecore2.standard.Constants.STANDARD_MSG_COMPONENT_URN_PREFIX; + +import java.util.Map; + +import rescuecore2.URN;; + +public enum StandardMessageComponentURN implements URN { + Target(STANDARD_MSG_COMPONENT_URN_PREFIX | 1, "Target"), + DestinationX(STANDARD_MSG_COMPONENT_URN_PREFIX | 2, "Destination X"), + DestinationY(STANDARD_MSG_COMPONENT_URN_PREFIX | 3, "Destination Y"), + Water(STANDARD_MSG_COMPONENT_URN_PREFIX | 4, "Water"), Path(STANDARD_MSG_COMPONENT_URN_PREFIX | 5, "Path"), + Message(STANDARD_MSG_COMPONENT_URN_PREFIX | 6, "Message"), Channel(STANDARD_MSG_COMPONENT_URN_PREFIX | 7, "Channel"), + Channels(STANDARD_MSG_COMPONENT_URN_PREFIX | 8, "Channels"); + + private int urnId; + private String urnStr; + public static final Map<Integer, StandardMessageComponentURN> MAP = URN + .generateMap(StandardMessageComponentURN.class); + public static final Map<String, StandardMessageComponentURN> MAPSTR = URN + .generateMapStr(StandardMessageComponentURN.class); + + StandardMessageComponentURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return this.urnId; + } + + @Override + public String getURNStr() { + return this.urnStr; + } + + public static StandardMessageComponentURN fromInt(int urn) { + return MAP.get(urn); + } + + public static StandardMessageComponentURN fromString(String urn) { + return MAPSTR.get(urn); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/messages/StandardMessageFactory.java b/modules/standard/src/rescuecore2/standard/messages/StandardMessageFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b112f0bbac88a560d8ab6f8d1e9e9239b68eee6f --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/StandardMessageFactory.java @@ -0,0 +1,93 @@ +package rescuecore2.standard.messages; + +import java.io.IOException; +import java.io.InputStream; + +import rescuecore2.log.Logger; +import rescuecore2.messages.Message; +import rescuecore2.messages.protobuf.RCRSProto.MessageProto; +import rescuecore2.registry.AbstractMessageFactory; + +/** + * A factory for standard messages. + */ +public final class StandardMessageFactory + extends AbstractMessageFactory<StandardMessageURN> { + + /** Singleton instance. */ + public static final StandardMessageFactory INSTANCE = new StandardMessageFactory(); + + /** + * Singleton class: private constructor. + */ + private StandardMessageFactory() { + super(StandardMessageURN.class); + } + + @Override + public Message makeMessage(StandardMessageURN urn, InputStream data) + throws IOException { + switch (urn) { + case AK_REST: + return new AKRest(data); + case AK_MOVE: + return new AKMove(data); + case AK_LOAD: + return new AKLoad(data); + case AK_UNLOAD: + return new AKUnload(data); + case AK_SAY: + return new AKSay(data); + case AK_TELL: + return new AKTell(data); + case AK_EXTINGUISH: + return new AKExtinguish(data); + case AK_RESCUE: + return new AKRescue(data); + case AK_CLEAR: + return new AKClear(data); + case AK_CLEAR_AREA: + return new AKClearArea(data); + case AK_SUBSCRIBE: + return new AKSubscribe(data); + case AK_SPEAK: + return new AKSpeak(data); + default: + Logger.warn("Unrecognised message urn: " + urn); + return null; + } + } + + @Override + public Message makeMessage(StandardMessageURN urn, MessageProto proto) { + switch (urn) { + case AK_REST: + return new AKRest(proto); + case AK_MOVE: + return new AKMove(proto); + case AK_LOAD: + return new AKLoad(proto); + case AK_UNLOAD: + return new AKUnload(proto); + case AK_SAY: + return new AKSay(proto); + case AK_TELL: + return new AKTell(proto); + case AK_EXTINGUISH: + return new AKExtinguish(proto); + case AK_RESCUE: + return new AKRescue(proto); + case AK_CLEAR: + return new AKClear(proto); + case AK_CLEAR_AREA: + return new AKClearArea(proto); + case AK_SUBSCRIBE: + return new AKSubscribe(proto); + case AK_SPEAK: + return new AKSpeak(proto); + default: + Logger.warn("Unrecognised message urn: " + urn); + return null; + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/messages/StandardMessageURN.java b/modules/standard/src/rescuecore2/standard/messages/StandardMessageURN.java new file mode 100644 index 0000000000000000000000000000000000000000..0b59ab0953ff35446cdecc06010044959e47ec3a --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/messages/StandardMessageURN.java @@ -0,0 +1,72 @@ +package rescuecore2.standard.messages; + +import static rescuecore2.standard.Constants.MESSAGE_URN_PREFIX; +import static rescuecore2.standard.Constants.MESSAGE_URN_PREFIX_STR; + +import java.util.Map; + +import rescuecore2.URN; + +/** + * URNs for standard messages. + */ +public enum StandardMessageURN implements URN { + /** Rest command. */ + AK_REST(MESSAGE_URN_PREFIX | 1, MESSAGE_URN_PREFIX_STR + "rest"), + /** Move command. */ + AK_MOVE(MESSAGE_URN_PREFIX | 2, MESSAGE_URN_PREFIX_STR + "move"), + /** Load command. */ + AK_LOAD(MESSAGE_URN_PREFIX | 3, MESSAGE_URN_PREFIX_STR + "load"), + /** Unload command. */ + AK_UNLOAD(MESSAGE_URN_PREFIX | 4, MESSAGE_URN_PREFIX_STR + "unload"), + /** Say command. */ + AK_SAY(MESSAGE_URN_PREFIX | 5, MESSAGE_URN_PREFIX_STR + "say"), + /** Tell command. */ + AK_TELL(MESSAGE_URN_PREFIX | 6, MESSAGE_URN_PREFIX_STR + "tell"), + /** Extinguish command. */ + AK_EXTINGUISH(MESSAGE_URN_PREFIX | 7, MESSAGE_URN_PREFIX_STR + "extinguish"), + /** Rescue command. */ + AK_RESCUE(MESSAGE_URN_PREFIX | 8, MESSAGE_URN_PREFIX_STR + "rescue"), + /** Clear command. */ + AK_CLEAR(MESSAGE_URN_PREFIX | 9, MESSAGE_URN_PREFIX_STR + "clear"), + /** Clear-Area command. */ + AK_CLEAR_AREA(MESSAGE_URN_PREFIX | 10, MESSAGE_URN_PREFIX_STR + "clear_area"), + /** Channel subscribe command. */ + AK_SUBSCRIBE(MESSAGE_URN_PREFIX | 11, MESSAGE_URN_PREFIX_STR + "subscribe"), + /** Channel speak command. */ + AK_SPEAK(MESSAGE_URN_PREFIX | 12, MESSAGE_URN_PREFIX_STR + "speak"); + + private int urnId; + private String urnStr; + public static final Map<Integer, StandardMessageURN> MAP = URN.generateMap(StandardMessageURN.class); + public static final Map<String, StandardMessageURN> MAPSTR = URN.generateMapStr(StandardMessageURN.class); + + private StandardMessageURN(int urnId, String urnStr) { + this.urnId = urnId; + this.urnStr = urnStr; + } + + @Override + public int getURNId() { + return this.urnId; + } + + @Override + public String getURNStr() { + return this.urnStr; + } + + /** + * Convert a String to a StandardMessageURN. + * + * @param s The String to convert. + * @return A StandardMessageURN. + */ + public static StandardMessageURN fromInt(int s) { + return MAP.get(s); + } + + public static StandardMessageURN fromString(int s) { + return MAP.get(s); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/misc/AgentPath.java b/modules/standard/src/rescuecore2/standard/misc/AgentPath.java new file mode 100644 index 0000000000000000000000000000000000000000..b8cd98b72077245a2fc2a0cade7457f64816072b --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/misc/AgentPath.java @@ -0,0 +1,215 @@ +package rescuecore2.standard.misc; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import rescuecore2.misc.Pair; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.StandardWorldModel; + +/** + * A class for encapsulating the actual movement path an agent took during a + * timestep. + */ +public abstract class AgentPath { + + /** + * Compute the path an agent took. This will read the positionHistory property + * and generate a path. + * + * @param human + * The agent. + * @param world + * The world model. + * + * @return The computed Path, or null if the agent didn't move. + */ + public static AgentPath computePath(Human human, StandardWorldModel world) { + if (human == null) { + throw new IllegalArgumentException("Agent must not be null"); + } + if (!human.isPositionDefined()) { + throw new IllegalArgumentException("Agent has an undefined position"); + } + if (!human.isPositionHistoryDefined()) { + // Agent didn't move. + return null; + } + int[] history = human.getPositionHistory(); + if (history.length > 2) { + return new CoordinatePath(history); + } + // Agent didn't move far enough: only one piece of history. + return null; + } + + + /** + * Get the coordinates of a point along this path. + * + * @param d + * The distance along the path in the range [0..1]. + * + * @return The coordinates of the point along the path. + */ + public abstract Pair<Integer, Integer> getPointOnPath(double d); + + /** + * Get the length of this path. + * + * @return The length of the path. + */ + public abstract double getLength(); + + private static class CompositePath extends AgentPath { + + private List<AgentPath> parts; + + CompositePath() { + parts = new ArrayList<AgentPath>(); + } + + + void addPath(AgentPath p) { + parts.add(p); + } + + + @Override + public double getLength() { + double d = 0; + for (AgentPath next : parts) { + d += next.getLength(); + } + return d; + } + + + @Override + public Pair<Integer, Integer> getPointOnPath(double d) { + double length = getLength(); + double point = d * length; + // Find the right part + AgentPath result = null; + for (AgentPath next : parts) { + double nextLength = next.getLength(); + if (nextLength > point) { + result = next; + break; + } + point -= nextLength; + } + if (result == null) { + // Fell off the end, probably because of numerical issues + return parts.get(parts.size() - 1).getPointOnPath(1.0); + } + double p = point / result.getLength(); + return result.getPointOnPath(p); + } + + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + for (Iterator<AgentPath> it = parts.iterator(); it.hasNext();) { + result.append(it.next()); + if (it.hasNext()) { + result.append(", "); + } + } + return result.toString(); + } + } + + private abstract static class AbstractPath extends AgentPath { + + private Pair<Integer, Integer> start; + private Pair<Integer, Integer> end; + private double length; + private String description; + + protected void setStart(int sX, int sY) { + start = new Pair<Integer, Integer>(sX, sY); + } + + + protected void setEnd(int eX, int eY) { + end = new Pair<Integer, Integer>(eX, eY); + } + + + protected void setStart(Pair<Integer, Integer> s) { + start = s; + } + + + protected void setEnd(Pair<Integer, Integer> e) { + end = e; + } + + + protected void setLength(double l) { + length = l; + } + + + protected void computeLength() { + int dx = end.first() - start.first(); + int dy = end.second() - start.second(); + length = Math.hypot(dx, dy); + } + + + protected void setDescription(String d) { + description = d; + } + + + @Override + public double getLength() { + return length; + } + + + @Override + public Pair<Integer, Integer> getPointOnPath(double d) { + double dx = end.first() - start.first(); + double dy = end.second() - start.second(); + int x = start.first() + (int) (d * dx); + int y = start.second() + (int) (d * dy); + return new Pair<Integer, Integer>(x, y); + } + + + @Override + public String toString() { + return description; + } + } + + private static class CoordinatePath extends CompositePath { + + CoordinatePath(int[] history) { + int fromX = history[0]; + int fromY = history[1]; + for (int i = 2; i < history.length; i += 2) { + int toX = history[i]; + int toY = history[i + 1]; + addPath(new CoordinatePathSegment(fromX, fromY, toX, toY)); + fromX = toX; + fromY = toY; + } + } + } + + private static class CoordinatePathSegment extends AbstractPath { + + CoordinatePathSegment(int fromX, int fromY, int toX, int toY) { + setStart(fromX, fromY); + setEnd(toX, toY); + setDescription( + "From " + fromX + ", " + fromY + " to " + toX + ", " + toY); + computeLength(); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/misc/SliderComponent.java b/modules/standard/src/rescuecore2/standard/misc/SliderComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..8f307043fd7bb711568d57b8b66732fdffa6a391 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/misc/SliderComponent.java @@ -0,0 +1,139 @@ +package rescuecore2.standard.misc; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * A JComponent that has a JSlider and a JTextField. Dragging the slider will + * update the text field and vice versa. + */ +public class SliderComponent extends JPanel { + + private JSlider slider; + private JTextField text; + + /** + * Create a vertical SliderComponent with range bounds. + * + * @param min + * The minimum value of the slider. + * @param max + * The maximum value of the slider. + * @param value + * The current value of the slider. + */ + public SliderComponent(int min, int max, int value) { + this(min, max, value, SwingConstants.VERTICAL); + } + + + /** + * Create a SliderComponent with range bounds. + * + * @param min + * The minimum value of the slider. + * @param max + * The maximum value of the slider. + * @param value + * The current value of the slider. + * @param orientation + * The orientation of the slider. Must be either + * {@link javax.swing.SwingConstants#VERTICAL} or + * {@link javax.swing.SwingConstants#HORIZONTAL} + */ + public SliderComponent(int min, int max, int value, int orientation) { + this(new JSlider(orientation, min, max, value), + new JTextField(String.valueOf(value))); + } + + + /** + * Create a SliderComponent with a given slider. If the slider has vertical + * orientation then the text field will be displayed below it, otherwise it + * will + * be to the right. + * + * @param slider + * The JSlider to display. + */ + public SliderComponent(JSlider slider) { + this(slider, new JTextField(String.valueOf(slider.getValue()))); + } + + + /** + * Create a SliderComponent with a given slider and text field. If the slider + * has vertical orientation then the text field will be displayed below it, + * otherwise it will be to the right. + * + * @param slider + * The JSlider to display. + * @param text + * The JTextField to display. + */ + public SliderComponent(JSlider slider, JTextField text) { + super(new BorderLayout()); + setup(slider, text); + } + + + /** + * Get the JSlider component. This is useful if you want to manually set the + * tick marks etc. + * + * @return The JSlider component. + */ + public JSlider getSlider() { + return slider; + } + + + /** + * Get the JTextField component. + * + * @return The JTextField component. + */ + public JTextField getTextField() { + return text; + } + + + private void setup(final JSlider s, final JTextField t) { + this.slider = s; + this.text = t; + add(slider, BorderLayout.CENTER); + text.setText(String.valueOf(slider.getValue())); + if (slider.getOrientation() == SwingConstants.VERTICAL) { + add(text, BorderLayout.SOUTH); + } else { + add(text, BorderLayout.EAST); + } + text.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + try { + int i = Integer.parseInt(text.getText()); + slider.setValue(i); + } catch (NumberFormatException ex) { + // Ignore + ex.printStackTrace(); + } + } + }); + slider.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + text.setText(String.valueOf(slider.getValue())); + } + }); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/misc/URNMapPrinter.java b/modules/standard/src/rescuecore2/standard/misc/URNMapPrinter.java new file mode 100644 index 0000000000000000000000000000000000000000..d4bb7e5d16b7c649f39a1055cba6945d12a3efe8 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/misc/URNMapPrinter.java @@ -0,0 +1,191 @@ +package rescuecore2.standard.misc; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; + +import org.json.JSONObject; + +import rescuecore2.config.Config; +import rescuecore2.config.ConfigException; +import rescuecore2.messages.control.ControlMessageComponentFactory; +import rescuecore2.messages.control.ControlMessageFactory; +import rescuecore2.misc.CommandLineOptions; +import rescuecore2.misc.Pair; +import rescuecore2.registry.Factory; +import rescuecore2.standard.entities.StandardEntityFactory; +import rescuecore2.standard.entities.StandardPropertyFactory; +import rescuecore2.standard.messages.StandardMessageComponentFactory; +import rescuecore2.standard.messages.StandardMessageFactory; + +public class URNMapPrinter { + + public static void main(String[] args) throws IOException, ConfigException { + + if ((args.length > 1) && (args[1].equals("--help"))) { + System.out.println( + "URNMapPrinter --python_out=file --js_out=file --json_out=file"); + } + + Config config = new Config(); + CommandLineOptions.processArgs(args, config); + + JSONObject json = toJSON(); + ArrayList<Pair<String, ArrayList<Pair<String, Integer>>>> entry = toEntry(); + write2file(config.getValue("json_out", "rcrs_urn.json"), + json.toString()); + write2file(config.getValue("python_out", "rcrs_urn.py"), + toPython(entry)); + write2file(config.getValue("js_out", "rcrs_urn.js"), toJS(entry)); + } + + private static void write2file(String path, String content) + throws FileNotFoundException { + try (PrintStream out = new PrintStream(new FileOutputStream(path))) { + out.print(content); + out.close(); + } + } + + private static String toPython( + ArrayList<Pair<String, ArrayList<Pair<String, Integer>>>> entries) { + String out = "#AutoGenerated by \'rcrs-server/scripts/platforms/compile.sh\'\n#Please do not modify it\n\n"; + out += "from enum import IntEnum\n"; + String map = "{int(u):u for u in "; + for (Pair<String, ArrayList<Pair<String, Integer>>> entry : entries) { + String key = entry.first(); + out += "\n\nclass " + key + "(IntEnum):\n"; + for (Pair<String, Integer> e : entry.second()) { + String hex = Integer.toHexString(e.second()); + if (hex.length() < 4) + hex = "0" + hex; + out += "\t" + e.first() + " = 0x" + hex + "\n"; + } + map += "list(" + key + ") +"; + } + map = map.substring(0, map.length() - 1) + "}"; + out += "\n\nMAP=" + map; + return out; + } + + public static String toPython(JSONObject json) { + String out = "#AutoGenerated by \'rcrs-server/scripts/platforms/compile.sh\'\n#Please do not modify it\n\n"; + out += "from enum import IntEnum\n"; + String map = "{int(u):u for u in "; + for (String key : json.keySet()) { + out += "\n\nclass " + key + "(IntEnum):\n"; + JSONObject sub = ((JSONObject) json.get(key)); + for (String prettyName : sub.keySet()) { + Integer urn = (int) sub.get(prettyName); + out += "\t" + prettyName + " = 0x" + Integer.toHexString(urn) + + "\n"; + } + map += "list(" + key + ") +"; + } + map = map.substring(0, map.length() - 1) + "}"; + out += "\n\nMAP=" + map; + return out; + } + + private static String toJS( + ArrayList<Pair<String, ArrayList<Pair<String, Integer>>>> entries) { + String out = "//AutoGenerated by \'rcrs-server/scripts/platforms/compile.sh\'\n//Please do not modify it\n\n"; + + String map = "Object.assign({},"; + for (Pair<String, ArrayList<Pair<String, Integer>>> entry : entries) { + String key = entry.first(); + out += "\n\nconst " + key + "={\n"; + for (Pair<String, Integer> e : entry.second()) { + String hex = Integer.toHexString(e.second()); + if (hex.length() < 4) + hex = "0" + hex; + out += "\t" + e.first() + ":0x" + hex + ",\n"; + } + out += "}"; + map += "...Object.keys(" + key + ").map(p=>({[" + key + + "[p]]:p})),"; + } + map = map.substring(0, map.length() - 1) + ")"; + out += "\n\nMAP=" + map; + return out; + } + + public static String toJS(JSONObject json) { + String out = "//AutoGenerated by \'rcrs-server/scripts/platforms/compile.sh\'\n//Please do not modify it\n\n"; + + out += "const URN={"; + + String map = "Object.assign({},"; + for (String key : json.keySet()) { + out += "\n\n " + key + ":{\n"; + JSONObject sub = ((JSONObject) json.get(key)); + for (String prettyName : sub.keySet()) { + int urn = (int) sub.get(prettyName); + out += "\t" + prettyName + ":0x" + Integer.toHexString(urn) + + ",\n"; + } + out += "},"; + map += "...Object.keys(URN." + key + ").map(p=>({[URN." + key + + "[p]]:p})),"; + } + map = map.substring(0, map.length() - 1) + ")"; + out += "\n}"; + out += "\n\nURN.MAP=" + map; + return out; + } + + public static JSONObject toJSON() { + JSONObject json = new JSONObject(); + json.put("ControlMSG", buildJson(ControlMessageFactory.INSTANCE)); + json.put("ComponentControlMSG", + buildJson(ControlMessageComponentFactory.INSTANCE)); + json.put("Command", buildJson(StandardMessageFactory.INSTANCE)); + json.put("ComponentCommand", + buildJson(StandardMessageComponentFactory.INSTANCE)); + json.put("Entity", buildJson(StandardEntityFactory.INSTANCE)); + json.put("Property", buildJson(StandardPropertyFactory.INSTANCE)); + return json; + } + + public static ArrayList<Pair<String, ArrayList<Pair<String, Integer>>>> toEntry() { + ArrayList<Pair<String, ArrayList<Pair<String, Integer>>>> list = new ArrayList<>(); + list.add(new Pair<>("Entity", + buildEntry(StandardEntityFactory.INSTANCE))); + list.add(new Pair<>("Property", + buildEntry(StandardPropertyFactory.INSTANCE))); + list.add(new Pair<>("Command", + buildEntry(StandardMessageFactory.INSTANCE))); + list.add(new Pair<>("ComponentCommand", + buildEntry(StandardMessageComponentFactory.INSTANCE))); + list.add(new Pair<>("ControlMSG", + buildEntry(ControlMessageFactory.INSTANCE))); + list.add(new Pair<>("ComponentControlMSG", + buildEntry(ControlMessageComponentFactory.INSTANCE))); + return list; + } + + private static ArrayList<Pair<String, Integer>> buildEntry(Factory fac) { + ArrayList<Pair<String, Integer>> list = new ArrayList<>(); + int[] urns = fac.getKnownURNs(); + Arrays.sort(urns); + for (Integer urn : urns) { + String prettyName = fac.getPrettyName(urn); + list.add(new Pair<>(prettyName, urn)); + } + return list; + } + + private static JSONObject buildJson(Factory fac) { + JSONObject obj = new JSONObject(); + int[] urns = fac.getKnownURNs(); + Arrays.sort(urns); + for (Integer urn : urns) { + String prettyName = fac.getPrettyName(urn); + obj.put(prettyName, urn); + } + return obj; + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/score/BuildingDamageScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/BuildingDamageScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..fb2726ac22d383e6bc073b7fb1f5e6b0f8b84a1e --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/BuildingDamageScoreFunction.java @@ -0,0 +1,85 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.AbstractScoreFunction; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import java.util.Map; +import java.util.EnumMap; + +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.StandardEntityConstants; + +/** + Score function that measures the amount of damage done to buildings by fire. + */ +public class BuildingDamageScoreFunction extends AbstractScoreFunction { + private static final String HEATING_FACTOR = "score.standard.building-fire.heating"; + private static final String BURNING_FACTOR = "score.standard.building-fire.burning"; + private static final String INFERNO_FACTOR = "score.standard.building-fire.inferno"; + private static final String WATER_DAMAGE_FACTOR = "score.standard.building-fire.water-damage"; + private static final String MINOR_DAMAGE_FACTOR = "score.standard.building-fire.minor-damage"; + private static final String MODERATE_DAMAGE_FACTOR = "score.standard.building-fire.moderate-damage"; + private static final String SEVERE_DAMAGE_FACTOR = "score.standard.building-fire.severe-damage"; + private static final String BURNT_OUT_FACTOR = "score.standard.building-fire.burnt-out"; + private static final String ABSOLUTE_KEY = "score.standard.building-fire.absolute"; + + private Map<StandardEntityConstants.Fieryness, Double> factors; + private boolean absolute; + + /** + Construct a BuildingDamageScoreFunction. + */ + public BuildingDamageScoreFunction() { + super("Building damage"); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + factors = new EnumMap<StandardEntityConstants.Fieryness, Double>(StandardEntityConstants.Fieryness.class); + factors.put(StandardEntityConstants.Fieryness.UNBURNT, 1.0); + factors.put(StandardEntityConstants.Fieryness.HEATING, config.getFloatValue(HEATING_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.BURNING, config.getFloatValue(BURNING_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.INFERNO, config.getFloatValue(INFERNO_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.WATER_DAMAGE, config.getFloatValue(WATER_DAMAGE_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.MINOR_DAMAGE, config.getFloatValue(MINOR_DAMAGE_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.MODERATE_DAMAGE, config.getFloatValue(MODERATE_DAMAGE_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.SEVERE_DAMAGE, config.getFloatValue(SEVERE_DAMAGE_FACTOR)); + factors.put(StandardEntityConstants.Fieryness.BURNT_OUT, config.getFloatValue(BURNT_OUT_FACTOR)); + absolute = config.getBooleanValue(ABSOLUTE_KEY, false); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + double max = 0; + for (Entity next : world) { + if (next instanceof Building) { + Building b = (Building)next; + if (!b.isTotalAreaDefined()) { + continue; + } + int importance = b.isImportanceDefined() ? b.getImportance() : 1; + double area = b.getTotalArea() * importance; + StandardEntityConstants.Fieryness fire = b.getFierynessEnum(); + double factor; + if (fire == null) { + factor = 1; + } + else { + factor = factors.get(fire); + } + sum += area * factor; + max += area; + } + } + if (absolute) { + return sum; + } + else { + return sum / max; + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/CiviliansAliveScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/CiviliansAliveScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..25436df677fc43198468de8e755e6a46be80a0a8 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/CiviliansAliveScoreFunction.java @@ -0,0 +1,39 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.AbstractScoreFunction; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import rescuecore2.standard.entities.Civilian; + +/** + Score function that measures the number of living civilians. One point per civilian still alive. + */ +public class CiviliansAliveScoreFunction extends AbstractScoreFunction { + /** + Construct a CiviliansAliveScoreFunction. + */ + public CiviliansAliveScoreFunction() { + super("Civilians alive"); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + for (Entity next : world) { + if (next instanceof Civilian) { + Civilian c = (Civilian)next; + if (c.isHPDefined() && c.getHP() > 0) { + ++sum; + } + } + } + return sum; + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/DiscoveryScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/DiscoveryScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..00bff2535f1d20f2d06887a9acbc3795aeeea97f --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/DiscoveryScoreFunction.java @@ -0,0 +1,69 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.AbstractScoreFunction; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.Timestep; + +import java.util.Set; +import java.util.HashSet; + +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.PoliceForce; + +/** + Score function that measures how quickly civilians are discovered by agents. + */ +public class DiscoveryScoreFunction extends AbstractScoreFunction { + private Set<EntityID> found; + + /** + Construct a DiscoveryScoreFunction. + */ + public DiscoveryScoreFunction() { + super("Civilian discovery time"); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + found = new HashSet<EntityID>(); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + // Look for agents that observed a civilian + for (EntityID next : timestep.getAgentsWithUpdates()) { + Entity agent = world.getEntity(next); + // Only platoon agents can discover civilians + if (!isPlatoonAgent(agent)) { + continue; + } + ChangeSet perception = timestep.getAgentPerception(next); + for (EntityID observedID : perception.getChangedEntities()) { + // Is it already seen? + if (found.contains(observedID)) { + continue; + } + Entity e = world.getEntity(observedID); + if (e instanceof Civilian && !perception.getChangedProperties(observedID).isEmpty()) { + // Seen a new civilian with at least one updated property. + found.add(observedID); + sum += timestep.getTime(); + } + } + } + return sum; + } + + private boolean isPlatoonAgent(Entity e) { + return e instanceof FireBrigade + || e instanceof PoliceForce + || e instanceof AmbulanceTeam; + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/DistanceTravelledScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/DistanceTravelledScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..b02abd69701385e1660177087f2ce0d7ff870afc --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/DistanceTravelledScoreFunction.java @@ -0,0 +1,45 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.AbstractScoreFunction; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.misc.AgentPath; + +/** + A score function that measures the distance travelled by all agents. +*/ +public class DistanceTravelledScoreFunction extends AbstractScoreFunction { + /** + Construct a DistanceTravelledScoreFunction. + */ + public DistanceTravelledScoreFunction() { + super("Distance travelled"); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + StandardWorldModel model = StandardWorldModel.createStandardWorldModel(world); + // Find out how far each agent moved + double sum = 0; + for (Entity next : model) { + if (next instanceof FireBrigade + || next instanceof PoliceForce + || next instanceof AmbulanceTeam) { + Human h = (Human)next; + AgentPath path = AgentPath.computePath(h, model); + if (path != null) { + sum += path.getLength(); + } + } + } + System.out.println("Total distance travelled: " + sum); + return sum; + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/HealthScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/HealthScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..2ae3975e472bd57812e14cb5e9153705a625548e --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/HealthScoreFunction.java @@ -0,0 +1,53 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.AbstractScoreFunction; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.Timestep; + +import rescuecore2.standard.entities.Civilian; + +/** + Score function that measures the health of living civilians. + */ +public class HealthScoreFunction extends AbstractScoreFunction { + private static final String ABSOLUTE_KEY = "score.standard.health.absolute"; + private static final double MAX = 10000; + + private boolean absolute; + + /** + Construct a HealthScoreFunction. + */ + public HealthScoreFunction() { + super("Civilian health"); + } + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) { + absolute = config.getBooleanValue(ABSOLUTE_KEY, false); + setName(absolute ? "Civilian health (absolute)" : "Civilian health (proportion)"); + } + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double sum = 0; + double max = 0; + for (Entity next : world) { + if (next instanceof Civilian) { + Civilian c = (Civilian)next; + if (c.isHPDefined()) { + sum += c.getHP(); + } + max += MAX; + } + } + if (absolute) { + return sum; + } + else { + return sum / max; + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/LegacyScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/LegacyScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..e0e0b2eb2a0e8fae5a2e2f63627008055e489692 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/LegacyScoreFunction.java @@ -0,0 +1,23 @@ +package rescuecore2.standard.score; + +import rescuecore2.score.ScoreFunction; +import rescuecore2.score.UnaryOperatorScoreFunction; +import rescuecore2.score.WeightedScoreFunction; +import rescuecore2.score.MultiplicativeScoreFunction; + +/** + Implementation of the legacy score function. Score = sqrt(building area saved) * (civilians alive + civilian health) +*/ +public class LegacyScoreFunction extends MultiplicativeScoreFunction { + /** + Construct a LegacyScoreFunction. + */ + public LegacyScoreFunction() { + super("Overall"); + ScoreFunction buildings = new UnaryOperatorScoreFunction("Sqrt(building damage)", UnaryOperatorScoreFunction.Operator.SQUARE_ROOT, new BuildingDamageScoreFunction()); + WeightedScoreFunction civs = new WeightedScoreFunction("Civilian component"); + civs.addChildFunction(new CiviliansAliveScoreFunction()); + civs.addChildFunction(new HealthScoreFunction()); + addChildFunctions(civs, buildings); + } +} diff --git a/modules/standard/src/rescuecore2/standard/score/RSL21ScoreFunction.java b/modules/standard/src/rescuecore2/standard/score/RSL21ScoreFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..7bbd240a7a5c16ddff967edf3a45027c5a1910c8 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/score/RSL21ScoreFunction.java @@ -0,0 +1,43 @@ +package rescuecore2.standard.score; + +import rescuecore2.Timestep; +import rescuecore2.config.Config; +import rescuecore2.score.CompositeScoreFunction; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.WorldModel; + +public class RSL21ScoreFunction extends CompositeScoreFunction { + + private static final double MAX = 10000; + + public RSL21ScoreFunction() { + super("RSL21 Score"); + } + + + @Override + public void initialise(WorldModel<? extends Entity> world, Config config) {} + + + @Override + public double score(WorldModel<? extends Entity> world, Timestep timestep) { + double civilians = 0; + double hp = 0; + double max = 0; + for (Entity next : world) { + if (next instanceof Civilian) { + Civilian c = (Civilian) next; + if (c.isHPDefined()) { + hp += c.getHP(); + if (c.getHP() > 0) { + ++civilians; + } + } + max += MAX; + } + } + + return civilians * Math.exp(-5 * (1 - (hp / max))); + } +} \ No newline at end of file diff --git a/modules/standard/src/rescuecore2/standard/view/AnimatedHumanLayer.java b/modules/standard/src/rescuecore2/standard/view/AnimatedHumanLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..813e3037167a6acfdfcfb659ffd089de3e3bfc5c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/AnimatedHumanLayer.java @@ -0,0 +1,130 @@ +package rescuecore2.standard.view; + +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Queue; +import java.util.LinkedList; + +import rescuecore2.misc.Pair; +import rescuecore2.config.Config; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.misc.AgentPath; + +/** + A view layer that animates human movements. + */ +public class AnimatedHumanLayer extends HumanLayer { + private Set<EntityID> humanIDs; + + private Map<EntityID, Queue<Pair<Integer, Integer>>> frames; + private boolean animationDone; + + /** + Construct an animated human view layer. + */ + public AnimatedHumanLayer() { + humanIDs = new HashSet<EntityID>(); + frames = new HashMap<EntityID, Queue<Pair<Integer, Integer>>>(); + animationDone = true; + } + + @Override + public void initialise(Config config) { + super.initialise(config); + humanIDs.clear(); + synchronized (this) { + frames.clear(); + animationDone = true; + } + } + + @Override + public String getName() { + return "Humans (animated)"; + } + + /** + Increase the frame number. + @return True if a new frame is actually required. + */ + public boolean nextFrame() { + synchronized (this) { + if (animationDone) { + return false; + } + animationDone = true; + for (Queue<Pair<Integer, Integer>> next : frames.values()) { + if (next.size() > 0) { + next.remove(); + animationDone = false; + } + } + return !animationDone; + } + } + + @Override + protected Pair<Integer, Integer> getLocation(Human h) { + synchronized (this) { + Queue<Pair<Integer, Integer>> agentFrames = frames.get(h.getID()); + if (agentFrames != null && !agentFrames.isEmpty()) { + return agentFrames.peek(); + } + } + return h.getLocation(world); + } + + @Override + protected void preView() { + super.preView(); + humanIDs.clear(); + } + + @Override + protected void viewObject(Object o) { + super.viewObject(o); + if (o instanceof Human) { + humanIDs.add(((Human)o).getID()); + } + } + + /** + Compute the animation frames. + @param frameCount The number of animation frames to compute. + */ + void computeAnimation(int frameCount) { + synchronized (this) { + frames.clear(); + // Compute animation + double step = 1.0 / (frameCount - 1.0); + for (EntityID next : humanIDs) { + Queue<Pair<Integer, Integer>> result = new LinkedList<Pair<Integer, Integer>>(); + Human human = (Human)world.getEntity(next); + if (human == null) { + continue; + } + AgentPath path; + StandardEntity position = world.getEntity(human.getPosition()); + if(position instanceof AmbulanceTeam) + //to render the civilian in ambulance team movement + path=AgentPath.computePath((AmbulanceTeam)position,world); + else + path= AgentPath.computePath(human, world); + if (path == null) { + continue; + } + for (int i = 0; i < frameCount; ++i) { + Pair<Integer, Integer> nextPoint = path.getPointOnPath(i * step); + result.add(nextPoint); + } + frames.put(next, result); + } + animationDone = false; + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/AnimatedWorldModelViewer.java b/modules/standard/src/rescuecore2/standard/view/AnimatedWorldModelViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..bc79493e17e7cd737807fa150c7db0fded1583d5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/AnimatedWorldModelViewer.java @@ -0,0 +1,72 @@ +package rescuecore2.standard.view; + +import javax.swing.Timer; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +/** + A viewer for StandardWorldModels. + */ +public class AnimatedWorldModelViewer extends StandardWorldModelViewer { + private static final int FRAME_COUNT = 10; + private static final int ANIMATION_TIME = 750; + private static final int FRAME_DELAY = ANIMATION_TIME / FRAME_COUNT; + + private AnimatedHumanLayer humans; + private Timer timer; + private final Object lock = new Object(); + private boolean done; + + /** + Construct an animated world model viewer. + */ + public AnimatedWorldModelViewer() { + super(); + timer = new Timer(FRAME_DELAY, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + synchronized (lock) { + if (done) { + return; + } + done = true; + if (humans.nextFrame()) { + done = false; + repaint(); + } + } + } + }); + timer.setRepeats(true); + timer.start(); + } + + @Override + public String getViewerName() { + return "Animated world model viewer"; + } + + @Override + public void addDefaultLayers() { + addLayer(new BuildingLayer()); + addLayer(new RoadLayer()); + addLayer(new AreaNeighboursLayer()); + addLayer(new RoadBlockageLayer()); + addLayer(new AreaIconLayer()); + humans = new AnimatedHumanLayer(); + addLayer(humans); + CommandLayer commands = new CommandLayer(); + addLayer(commands); + commands.setRenderMove(false); + addLayer(new PositionHistoryLayer()); + } + + @Override + public void view(Object... objects) { + super.view(objects); + synchronized (lock) { + done = false; + humans.computeAnimation(FRAME_COUNT); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/AreaIconLayer.java b/modules/standard/src/rescuecore2/standard/view/AreaIconLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..45f6cdcb0a6a5216eb1d0749d45e8c8237f685d5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/AreaIconLayer.java @@ -0,0 +1,71 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Shape; + +import javax.swing.ImageIcon; + +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Building; +import rescuecore2.view.Icons; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A view layer that renders building icons. + */ +public class AreaIconLayer extends StandardEntityViewLayer<Area> { + private static final int ICON_SIZE = 32; + + private static final ImageIcon FIRE_STATION = Icons.get(BuildingLayer.class,"rescuecore2/standard/view/FireStation-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + private static final ImageIcon POLICE_OFFICE = Icons.get(BuildingLayer.class,"rescuecore2/standard/view/PoliceOffice-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + private static final ImageIcon AMBULANCE_CENTRE = Icons.get(BuildingLayer.class,"rescuecore2/standard/view/AmbulanceCentre-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + private static final ImageIcon REFUGE = Icons.get(BuildingLayer.class,"rescuecore2/standard/view/Refuge-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + private static final ImageIcon HYDRANT = Icons.get(BuildingLayer.class,"rescuecore2/standard/view/Hydrant-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + private static final ImageIcon GAS_STATION= Icons.get(BuildingLayer.class,"rescuecore2/standard/view/GasStation-" + ICON_SIZE + "x" + ICON_SIZE + ".png"); + + /** + Construct a building icon view layer. + */ + public AreaIconLayer() { + super(Area.class); + } + + @Override + public String getName() { + return "Area icons"; + } + + @Override + public Shape render(Area b, Graphics2D g, ScreenTransform t) { + ImageIcon icon = null; + switch (b.getStandardURN()) { + case REFUGE: + icon = REFUGE; + break; + case GAS_STATION: + icon = GAS_STATION; + break; + case FIRE_STATION: + icon = FIRE_STATION; + break; + case AMBULANCE_CENTRE: + icon = AMBULANCE_CENTRE; + break; + case POLICE_OFFICE: + icon = POLICE_OFFICE; + break; + case HYDRANT: + icon = HYDRANT; + break; + default: + break; + } + if (icon != null) { + // Draw an icon over the centre of the building + int x = t.xToScreen(b.getX()) - (icon.getIconWidth() / 2); + int y = t.yToScreen(b.getY()) - (icon.getIconHeight() / 2); + icon.paintIcon(null, g, x, y); + } + return null; + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/AreaLayer.java b/modules/standard/src/rescuecore2/standard/view/AreaLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..18bc41b44edba7986e4c10e99a255e2f18fff4a2 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/AreaLayer.java @@ -0,0 +1,69 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Polygon; + +import java.util.List; +import java.util.Iterator; + +import rescuecore2.misc.gui.ScreenTransform; + +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Edge; + +/** + A view layer that renders areas. + @param <E> The subclass of Area that this layer knows how to draw. + */ +public abstract class AreaLayer<E extends Area> extends StandardEntityViewLayer<E> { + /** + Construct an area view layer. + @param clazz The subclass of Area this can render. + */ + protected AreaLayer(Class<E> clazz) { + super(clazz); + } + + @Override + public Shape render(E area, Graphics2D g, ScreenTransform t) { + List<Edge> edges = area.getEdges(); + if (edges.isEmpty()) { + return null; + } + int count = edges.size(); + int[] xs = new int[count]; + int[] ys = new int[count]; + int i = 0; + for (Iterator<Edge> it = edges.iterator(); it.hasNext();) { + Edge e = it.next(); + xs[i] = t.xToScreen(e.getStartX()); + ys[i] = t.yToScreen(e.getStartY()); + ++i; + } + Polygon shape = new Polygon(xs, ys, count); + paintShape(area, shape, g); + for (Edge edge : edges) { + paintEdge(edge, g, t); + } + return shape; + } + + /** + Paint an individual edge. + @param e The edge to paint. + @param g The graphics to paint on. + @param t The screen transform. + */ + protected void paintEdge(Edge e, Graphics2D g, ScreenTransform t) { + } + + /** + Paint the overall shape. + @param area The area. + @param p The overall polygon. + @param g The graphics to paint on. + */ + protected void paintShape(E area, Polygon p, Graphics2D g) { + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/AreaNeighboursLayer.java b/modules/standard/src/rescuecore2/standard/view/AreaNeighboursLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..5ac3ade4057a0b09fc412aacd0f0be0adbfbc6f5 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/AreaNeighboursLayer.java @@ -0,0 +1,51 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Color; +import java.awt.Stroke; +import java.awt.BasicStroke; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.misc.gui.ScreenTransform; + +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Edge; + +/** + A view layer that renders area neighbours. + */ +public class AreaNeighboursLayer extends StandardEntityViewLayer<Area> { + + private static final Color NEIGHBOUR_COLOUR = Color.blue; + private static final Stroke NEIGHBOUR_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + + /** + Construct an area neighbours view layer. + */ + public AreaNeighboursLayer() { + super(Area.class); + setVisible(false); + } + + @Override + public String getName() { + return "Neighbours"; + } + + @Override + public Shape render(Area area, Graphics2D g, ScreenTransform t) { + g.setColor(NEIGHBOUR_COLOUR); + g.setStroke(NEIGHBOUR_STROKE); + for (Edge edge : area.getEdges()) { + EntityID neighbour = edge.getNeighbour(); + if (neighbour != null) { + Area a = (Area)world.getEntity(neighbour); + if (a != null) { + g.drawLine(t.xToScreen(area.getX()), t.yToScreen(area.getY()), t.xToScreen(a.getX()), t.yToScreen(a.getY())); + } + } + } + return null; + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/BuildingIconLayer.java b/modules/standard/src/rescuecore2/standard/view/BuildingIconLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..359046a471d9461525b087be83cf432cf893b387 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/BuildingIconLayer.java @@ -0,0 +1,61 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Shape; + +import javax.swing.ImageIcon; + +import rescuecore2.standard.entities.Building; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A view layer that renders building icons. + */ +public class BuildingIconLayer extends StandardEntityViewLayer<Building> { + private static final int ICON_SIZE = 32; + + private static final ImageIcon FIRE_STATION = new ImageIcon(BuildingLayer.class.getClassLoader().getResource("rescuecore2/standard/view/FireStation-" + ICON_SIZE + "x" + ICON_SIZE + ".png")); + private static final ImageIcon POLICE_OFFICE = new ImageIcon(BuildingLayer.class.getClassLoader().getResource("rescuecore2/standard/view/PoliceOffice-" + ICON_SIZE + "x" + ICON_SIZE + ".png")); + private static final ImageIcon AMBULANCE_CENTRE = new ImageIcon(BuildingLayer.class.getClassLoader().getResource("rescuecore2/standard/view/AmbulanceCentre-" + ICON_SIZE + "x" + ICON_SIZE + ".png")); + private static final ImageIcon REFUGE = new ImageIcon(BuildingLayer.class.getClassLoader().getResource("rescuecore2/standard/view/Refuge-" + ICON_SIZE + "x" + ICON_SIZE + ".png")); + + /** + Construct a building icon view layer. + */ + public BuildingIconLayer() { + super(Building.class); + } + + @Override + public String getName() { + return "Building icons"; + } + + @Override + public Shape render(Building b, Graphics2D g, ScreenTransform t) { + ImageIcon icon = null; + switch (b.getStandardURN()) { + case REFUGE: + icon = REFUGE; + break; + case FIRE_STATION: + icon = FIRE_STATION; + break; + case AMBULANCE_CENTRE: + icon = AMBULANCE_CENTRE; + break; + case POLICE_OFFICE: + icon = POLICE_OFFICE; + break; + default: + break; + } + if (icon != null) { + // Draw an icon over the centre of the building + int x = t.xToScreen(b.getX()) - (icon.getIconWidth() / 2); + int y = t.yToScreen(b.getY()) - (icon.getIconHeight() / 2); + icon.paintIcon(null, g, x, y); + } + return null; + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/BuildingLayer.java b/modules/standard/src/rescuecore2/standard/view/BuildingLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..72696b5ef421d2be64a4e9974ba1e6c3c28ffa49 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/BuildingLayer.java @@ -0,0 +1,109 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Polygon; +import java.awt.Stroke; +import java.awt.BasicStroke; + +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Edge; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A view layer that renders buildings. + */ +public class BuildingLayer extends AreaLayer<Building> { + private static final Color HEATING = new Color(176, 176, 56, 128); + private static final Color BURNING = new Color(204, 122, 50, 128); + private static final Color INFERNO = new Color(160, 52, 52, 128); + private static final Color WATER_DAMAGE = new Color(50, 120, 130, 128); + private static final Color MINOR_DAMAGE = new Color(100, 140, 210, 128); + private static final Color MODERATE_DAMAGE = new Color(100, 70, 190, 128); + private static final Color SEVERE_DAMAGE = new Color(80, 60, 140, 128); + private static final Color BURNT_OUT = new Color(0, 0, 0, 255); + + private static final Color OUTLINE_COLOUR = Color.GRAY.darker(); + private static final Color ENTRANCE = new Color(120, 120, 120); + + private static final Stroke WALL_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + private static final Stroke ENTRANCE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + + /** + Construct a building view layer. + */ + public BuildingLayer() { + super(Building.class); + } + + @Override + public String getName() { + return "Building shapes"; + } + + @Override + protected void paintEdge(Edge e, Graphics2D g, ScreenTransform t) { + g.setColor(OUTLINE_COLOUR); + g.setStroke(e.isPassable() ? ENTRANCE_STROKE : WALL_STROKE); + g.drawLine(t.xToScreen(e.getStartX()), + t.yToScreen(e.getStartY()), + t.xToScreen(e.getEndX()), + t.yToScreen(e.getEndY())); + } + + @Override + protected void paintShape(Building b, Polygon shape, Graphics2D g) { + drawBrokenness(b, shape, g); + drawFieryness(b, shape, g); + } + + private void drawFieryness(Building b, Polygon shape, Graphics2D g) { + if (!b.isFierynessDefined()) { + return; + } + switch (b.getFierynessEnum()) { + case UNBURNT: + return; + case HEATING: + g.setColor(HEATING); + break; + case BURNING: + g.setColor(BURNING); + break; + case INFERNO: + g.setColor(INFERNO); + break; + case WATER_DAMAGE: + g.setColor(WATER_DAMAGE); + break; + case MINOR_DAMAGE: + g.setColor(MINOR_DAMAGE); + break; + case MODERATE_DAMAGE: + g.setColor(MODERATE_DAMAGE); + break; + case SEVERE_DAMAGE: + g.setColor(SEVERE_DAMAGE); + break; + case BURNT_OUT: + g.setColor(BURNT_OUT); + break; + default: + throw new IllegalArgumentException("Don't know how to render fieryness " + b.getFierynessEnum()); + } + g.fill(shape); + } + + private void drawBrokenness(Building b, Shape shape, Graphics2D g) { + if (!b.isBrokennessDefined()) { + return; + } + int brokenness = b.getBrokenness(); + // CHECKSTYLE:OFF:MagicNumber + int colour = Math.max(0, 135 - brokenness / 2); + // CHECKSTYLE:ON:MagicNumber + g.setColor(new Color(colour, colour, colour)); + g.fill(shape); + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/CommandLayer.java b/modules/standard/src/rescuecore2/standard/view/CommandLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..06955c2206d3ba1615cca6ea597615463132881f --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/CommandLayer.java @@ -0,0 +1,501 @@ +package rescuecore2.standard.view; + +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JMenuItem; + +import rescuecore2.messages.Command; +import rescuecore2.misc.Pair; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.gui.DrawingTools; +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.messages.AKClear; +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.standard.messages.AKExtinguish; +import rescuecore2.standard.messages.AKLoad; +import rescuecore2.standard.messages.AKMove; +import rescuecore2.standard.messages.AKRescue; +import rescuecore2.standard.messages.AKUnload; +import rescuecore2.view.Icons; +import rescuecore2.view.RenderedObject; +import rescuecore2.worldmodel.EntityID; + +/** + A layer for viewing commands. + */ +public class CommandLayer extends StandardViewLayer { + private static final int SIZE = 15; + private static final Color CLEAR_COLOUR = new Color(0, 0, 255, 128); + private static final Color RESCUE_COLOUR = new Color(255, 255, 0, 128); + private static final Color LOAD_COLOUR = new Color(255, 255, 255, 128); + private static final Color UNLOAD_COLOUR = new Color(255, 255, 255, 128); + + private static final double ARROW_ANGLE = Math.toRadians(135); + private static final double ARROW_LENGTH = 5; + + private Graphics2D g; + private ScreenTransform t; + private Collection<Command> commands; + + private boolean renderMove; + private boolean renderExtinguish; + private boolean renderClear; + private boolean renderClearArea; + private boolean renderLoad; + private boolean renderUnload; + private boolean renderRescue; + + private RenderMoveAction renderMoveAction; + private RenderExtinguishAction renderExtinguishAction; + private RenderClearAction renderClearAction; + private RenderClearAreaAction renderClearAreaAction; + private RenderRescueAction renderRescueAction; + private RenderLoadAction renderLoadAction; + private RenderUnloadAction renderUnloadAction; + + /** + Construct a new CommandLayer. + */ + public CommandLayer() { + commands = new ArrayList<Command>(); + renderMove = false; + renderExtinguish = true; + renderClear = true; + renderClearArea = true; + renderLoad = true; + renderUnload = true; + renderRescue = true; + renderMoveAction = new RenderMoveAction(); + renderExtinguishAction = new RenderExtinguishAction(); + renderClearAction = new RenderClearAction(); + renderClearAreaAction = new RenderClearAreaAction(); + renderRescueAction = new RenderRescueAction(); + renderLoadAction = new RenderLoadAction(); + renderUnloadAction = new RenderUnloadAction(); + } + + /** + Set whether to render Move commands. + @param render True if move commands should be rendered, false otherwise. + */ + public void setRenderMove(boolean render) { + renderMove = render; + renderMoveAction.update(); + } + + /** + Set whether to render Extinguish commands. + @param render True if extinguish commands should be rendered, false otherwise. + */ + public void setRenderExtinguish(boolean render) { + renderExtinguish = render; + renderExtinguishAction.update(); + } + + /** + Set whether to render Clear commands. + @param render True if clear commands should be rendered, false otherwise. + */ + public void setRenderClear(boolean render) { + renderClear = render; + renderClearAction.update(); + } + + /** + Set whether to render Clear commands. + @param render True if clear commands should be rendered, false otherwise. + */ + public void setRenderClearArea(boolean render) { + renderClearArea = render; + renderClearAreaAction.update(); + } + + /** + Set whether to render Load commands. + @param render True if load commands should be rendered, false otherwise. + */ + public void setRenderLoad(boolean render) { + renderLoad = render; + renderLoadAction.update(); + } + + /** + Set whether to render Unload commands. + @param render True if unload commands should be rendered, false otherwise. + */ + public void setRenderUnload(boolean render) { + renderUnload = render; + renderUnloadAction.update(); + } + + /** + Set whether to render Rescue commands. + @param render True if rescue commands should be rendered, false otherwise. + */ + public void setRenderRescue(boolean render) { + renderRescue = render; + renderRescueAction.update(); + } + + @Override + public List<JMenuItem> getPopupMenuItems() { + List<JMenuItem> result = new ArrayList<JMenuItem>(); + result.add(new JMenuItem(renderMoveAction)); + result.add(new JMenuItem(renderClearAction)); + result.add(new JMenuItem(renderClearAreaAction)); + result.add(new JMenuItem(renderExtinguishAction)); + result.add(new JMenuItem(renderRescueAction)); + result.add(new JMenuItem(renderLoadAction)); + result.add(new JMenuItem(renderUnloadAction)); + return result; + } + + @Override + public String getName() { + return "Commands"; + } + + @Override + public Rectangle2D view(Object... objects) { + synchronized (commands) { + commands.clear(); + return super.view(objects); + } + } + + @Override + protected void viewObject(Object o) { + super.viewObject(o); + if (o instanceof Command) { + commands.add((Command)o); + } + } + + @Override + public Collection<RenderedObject> render(Graphics2D graphics, ScreenTransform transform, int width, int height) { + synchronized (commands) { + Collection<RenderedObject> result = new ArrayList<RenderedObject>(); + g = graphics; + t = transform; + for (Command next : commands) { + if (renderMove && next instanceof AKMove) { + renderMove((AKMove)next); + } + if (renderExtinguish && next instanceof AKExtinguish) { + renderExtinguish((AKExtinguish)next); + } + if (renderClear && next instanceof AKClear) { + renderClear((AKClear)next); + } + if (renderClearArea && next instanceof AKClearArea) { + renderClearArea((AKClearArea)next); + } + if (renderRescue && next instanceof AKRescue) { + renderRescue((AKRescue)next); + } + if (renderLoad && next instanceof AKLoad) { + renderLoad((AKLoad)next); + } + if (renderUnload && next instanceof AKUnload) { + renderUnload((AKUnload)next); + } + } + return result; + } + } + + private void renderMove(AKMove move) { + g.setColor(Color.BLACK); + List<EntityID> path = move.getPath(); + if (path.isEmpty()) { + return; + } + Iterator<EntityID> it = path.iterator(); + StandardEntity first = world.getEntity(it.next()); + Pair<Integer, Integer> firstLocation = first.getLocation(world); + int startX = t.xToScreen(firstLocation.first()); + int startY = t.yToScreen(firstLocation.second()); + while (it.hasNext()) { + StandardEntity next = world.getEntity(it.next()); + Pair<Integer, Integer> nextLocation = next.getLocation(world); + int nextX = t.xToScreen(nextLocation.first()); + int nextY = t.yToScreen(nextLocation.second()); + g.drawLine(startX, startY, nextX, nextY); + // Draw an arrow partway along the length + DrawingTools.drawArrowHeads(startX, startY, nextX, nextY, g); + startX = nextX; + startY = nextY; + } + } + + public static Area getClearArea(Human agent, int targetX, int targetY, + int clearLength, int clearRad) { + Vector2D agentToTarget = new Vector2D(targetX - agent.getX(), targetY + - agent.getY()); + + if (agentToTarget.getLength() > clearLength) + agentToTarget = agentToTarget.normalised().scale(clearLength); + agentToTarget = agentToTarget.normalised().scale(agentToTarget.getLength() + 510); + + Vector2D backAgent = (new Vector2D(agent.getX(), agent.getY())) + .add(agentToTarget.normalised().scale(-510)); + Line2D line = new Line2D(backAgent.getX(), backAgent.getY(), + agentToTarget.getX(), agentToTarget.getY()); + + Vector2D dir = agentToTarget.normalised().scale(clearRad); + Vector2D perpend1 = new Vector2D(-dir.getY(), dir.getX()); + Vector2D perpend2 = new Vector2D(dir.getY(), -dir.getX()); + + rescuecore2.misc.geometry.Point2D points[] = new rescuecore2.misc.geometry.Point2D[] { + line.getOrigin().plus(perpend1), + line.getEndPoint().plus(perpend1), + line.getEndPoint().plus(perpend2), + line.getOrigin().plus(perpend2) }; + int[] xPoints = new int[points.length]; + int[] yPoints = new int[points.length]; + for (int i = 0; i < points.length; i++) { + xPoints[i] = (int) points[i].getX(); + yPoints[i] = (int) points[i].getY(); + } + return new Area(new Polygon(xPoints, yPoints, points.length)); + } + + public void drawArea(Area area) { + PathIterator iter = area.getPathIterator(null); + Path2D.Float poly = new Path2D.Float(); + double[] firstPoint = null; + while (!iter.isDone()) { + double point[] = new double[2]; // x, y + int type = iter.currentSegment(point); + point[0] = t.xToScreen(point[0]); + point[1] = t.yToScreen(point[1]); + if (type == PathIterator.SEG_MOVETO) { + firstPoint = point; + poly.moveTo(point[0], point[1]); + } else if (type == PathIterator.SEG_CLOSE) { + poly.lineTo(firstPoint[0], firstPoint[1]); + g.draw(poly); + poly.reset(); + } else { + poly.lineTo(point[0], point[1]); + } + iter.next(); + } + } + + private void renderClearAreaAction(AKClearArea c) { + StandardEntity entity = world.getEntity(c.getAgentID()); + + Pair<Integer, Integer> location = entity.getLocation(world); + int x = t.xToScreen(location.first()) - SIZE / 2; + int y = t.yToScreen(location.second()) - SIZE / 2; + + PoliceForce agent = (PoliceForce) world.getEntity(c.getAgentID()); + int targetX = c.getDestinationX(); + int targetY = c.getDestinationY(); + Area area = getClearArea(agent, targetX, targetY, 10000, 1250); + g.setColor(CLEAR_COLOUR); + drawArea(area); + + Shape shape = new Ellipse2D.Double(x, y, SIZE, SIZE); + g.setColor(CLEAR_COLOUR); + g.fill(shape); + } + + private void renderExtinguish(AKExtinguish ex) { + StandardEntity fb = world.getEntity(ex.getAgentID()); + StandardEntity target = world.getEntity(ex.getTarget()); + Pair<Integer, Integer> fbLocation = fb.getLocation(world); + Pair<Integer, Integer> targetLocation = target.getLocation(world); + int fbX = t.xToScreen(fbLocation.first()); + int fbY = t.yToScreen(fbLocation.second()); + int bX = t.xToScreen(targetLocation.first()); + int bY = t.yToScreen(targetLocation.second()); + g.setColor(Color.BLUE); + g.drawLine(fbX, fbY, bX, bY); + } + + private void renderClear(AKClear clear) { + renderHumanAction(world.getEntity(clear.getAgentID()), CLEAR_COLOUR, null); + } + + private void renderClearArea(AKClearArea clear) { + renderClearAreaAction(clear); + } + + private void renderRescue(AKRescue rescue) { + renderHumanAction(world.getEntity(rescue.getAgentID()), RESCUE_COLOUR, null); + } + + private void renderLoad(AKLoad load) { + renderHumanAction(world.getEntity(load.getAgentID()), LOAD_COLOUR, "L"); + } + + private void renderUnload(AKUnload unload) { + renderHumanAction(world.getEntity(unload.getAgentID()), UNLOAD_COLOUR, "U"); + } + + private void renderHumanAction(StandardEntity entity, Color colour, String s) { + Pair<Integer, Integer> location = entity.getLocation(world); + int x = t.xToScreen(location.first()) - SIZE / 2; + int y = t.yToScreen(location.second()) - SIZE / 2; + Shape shape = new Ellipse2D.Double(x, y, SIZE, SIZE); + g.setColor(colour); + g.fill(shape); + if (s != null) { + g.setColor(Color.BLACK); + FontMetrics metrics = g.getFontMetrics(); + int width = metrics.stringWidth(s); + int height = metrics.getHeight(); + x = t.xToScreen(location.first()); + y = t.yToScreen(location.second()); + g.drawString(s, x - (width / 2), y + (height / 2)); + } + } + + private final class RenderMoveAction extends AbstractAction { + public RenderMoveAction() { + super("Show move commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderMove(!renderMove); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderMove)); + putValue(Action.SMALL_ICON, renderMove ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderClearAction extends AbstractAction { + public RenderClearAction() { + super("Show clear commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderClear(!renderClear); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderClear)); + putValue(Action.SMALL_ICON, renderClear ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderClearAreaAction extends AbstractAction { + public RenderClearAreaAction() { + super("Show clear-area commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderClear(!renderClear); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderClear)); + putValue(Action.SMALL_ICON, renderClear ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderExtinguishAction extends AbstractAction { + public RenderExtinguishAction() { + super("Show extinguish commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderExtinguish(!renderExtinguish); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderExtinguish)); + putValue(Action.SMALL_ICON, renderExtinguish ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderRescueAction extends AbstractAction { + public RenderRescueAction() { + super("Show rescue commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderRescue(!renderRescue); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderRescue)); + putValue(Action.SMALL_ICON, renderRescue ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderLoadAction extends AbstractAction { + public RenderLoadAction() { + super("Show load commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderLoad(!renderLoad); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderLoad)); + putValue(Action.SMALL_ICON, renderLoad ? Icons.TICK : Icons.CROSS); + } + } + + private final class RenderUnloadAction extends AbstractAction { + public RenderUnloadAction() { + super("Show unload commands"); + update(); + } + + @Override + public void actionPerformed(ActionEvent e) { + setRenderUnload(!renderUnload); + component.repaint(); + } + + void update() { + putValue(Action.SELECTED_KEY, Boolean.valueOf(renderUnload)); + putValue(Action.SMALL_ICON, renderUnload ? Icons.TICK : Icons.CROSS); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/HumanLayer.java b/modules/standard/src/rescuecore2/standard/view/HumanLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..241a2b3031e315115c09d657a417fe6106d3b12c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/HumanLayer.java @@ -0,0 +1,300 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.event.ActionEvent; + +import javax.swing.ImageIcon; +import javax.swing.Icon; +import javax.swing.JMenuItem; +import javax.swing.Action; +import javax.swing.AbstractAction; + +import java.util.Comparator; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.util.EnumMap; +import java.util.List; +import java.util.ArrayList; +import java.net.URL; + +import rescuecore2.misc.Pair; +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.config.Config; +import rescuecore2.view.Icons; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardWorldModel; + +/** + A view layer that renders humans. + */ +public class HumanLayer extends StandardEntityViewLayer<Human> { + private static final int SIZE = 10; + + private static final int HP_MAX = 10000; + private static final int HP_INJURED = 7500; + private static final int HP_CRITICAL = 1000; + + private static final String ICON_SIZE_KEY = "view.standard.human.icons.size"; + private static final String USE_ICONS_KEY = "view.standard.human.icons.use"; + private static final int DEFAULT_ICON_SIZE = 32; + + private static HumanSorter HUMAN_SORTER=null; + + private static final Color CIVILIAN_COLOUR = Color.GREEN; + 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 DEAD_COLOUR = Color.BLACK; + + private int iconSize; + private Map<String, Map<State, Icon>> icons; + private boolean useIcons; + private Action useIconsAction; + + /** + Construct a human view layer. + */ + public HumanLayer() { + super(Human.class); + iconSize = DEFAULT_ICON_SIZE; + } + + @Override + public void initialise(Config config) { + iconSize = config.getIntValue(ICON_SIZE_KEY, DEFAULT_ICON_SIZE); + icons = new HashMap<String, Map<State, Icon>>(); + useIcons = config.getBooleanValue(USE_ICONS_KEY, false); + icons.put(StandardEntityURN.FIRE_BRIGADE.toString(), generateIconMap("FireBrigade")); + icons.put(StandardEntityURN.AMBULANCE_TEAM.toString(), generateIconMap("AmbulanceTeam")); + icons.put(StandardEntityURN.POLICE_FORCE.toString(), generateIconMap("PoliceForce")); + icons.put(StandardEntityURN.CIVILIAN.toString() + "-Male", generateIconMap("Civilian-Male")); + icons.put(StandardEntityURN.CIVILIAN.toString() + "-Female", generateIconMap("Civilian-Female")); + useIconsAction = new UseIconsAction(); + } + + @Override + public String getName() { + return "Humans"; + } + + @Override + public Shape render(Human h, Graphics2D g, ScreenTransform t) { + Pair<Integer, Integer> location = getLocation(h); + if (location == null) { + return null; + } + int x = t.xToScreen(location.first()); + int y = t.yToScreen(location.second()); + Shape shape; + Icon icon = useIcons ? getIcon(h) : null; + if (icon == null) { + if (h.isPositionDefined() && (world.getEntity(h.getPosition()) instanceof AmbulanceTeam)) + // draw humans smaller in ambulances + shape = new Ellipse2D.Double(x - SIZE / 3, y - SIZE / 3, SIZE/3*2, SIZE/3*2); + else + shape = new Ellipse2D.Double(x - SIZE / 2, y - SIZE / 2, SIZE, SIZE); + + g.setColor(adjustColour(getColour(h), h.isHPDefined()? h.getHP():10000)); + g.fill(shape); + g.setColor(getColour(h)); + g.draw(shape); + } + else { + x -= icon.getIconWidth() / 2; + y -= icon.getIconHeight() / 2; + shape = new Rectangle2D.Double(x, y, icon.getIconWidth(), icon.getIconHeight()); + icon.paintIcon(null, g, x, y); + } + return shape; + } + @Override + public List<JMenuItem> getPopupMenuItems() { + List<JMenuItem> result = new ArrayList<JMenuItem>(); + result.add(new JMenuItem(useIconsAction)); + return result; + } + + @Override + protected void postView() { + if(world==null) + return; + if(HUMAN_SORTER==null) + HUMAN_SORTER= new HumanSorter(world); + Collections.sort(entities, HUMAN_SORTER); + + } + + /** + Get the location of a human. + @param h The human to look up. + @return The location of the human. + */ + protected Pair<Integer, Integer> getLocation(Human h) { + return h.getLocation(world); + } + + private Map<State, Icon> generateIconMap(String type) { + Map<State, Icon> result = new EnumMap<State, Icon>(State.class); + for (State state : State.values()) { + String resourceName = "rescuecore2/standard/view/" + type + "-" + state.toString() + "-" + iconSize + "x" + iconSize + ".png"; + URL resource = HumanLayer.class.getClassLoader().getResource(resourceName); + if (resource == null) { + Logger.warn("Couldn't find resource: " + resourceName); + } + else { + result.put(state, new ImageIcon(resource)); + } + } + return result; + } + + private Color getColour(Human h) { + switch (h.getStandardURN()) { + case CIVILIAN: + return CIVILIAN_COLOUR; + case FIRE_BRIGADE: + return FIRE_BRIGADE_COLOUR; + case AMBULANCE_TEAM: + return AMBULANCE_TEAM_COLOUR; + case POLICE_FORCE: + return POLICE_FORCE_COLOUR; + default: + throw new IllegalArgumentException("Don't know how to draw humans of type " + h.getStandardURN()); + } + } + + private Color adjustColour(Color c, int hp) { + if (hp == 0) { + return DEAD_COLOUR; + } + if (hp < HP_CRITICAL) { + c = c.darker(); + } + if (hp < HP_INJURED) { + c = c.darker(); + } + if (hp < HP_MAX) { + c = c.darker(); + } + return c; + } + + private Icon getIcon(Human h) { + State state = getState(h); + Map<State, Icon> iconMap = null; + switch (h.getStandardURN()) { + case CIVILIAN: + boolean male = h.getID().getValue() % 2 == 0; + if (male) { + iconMap = icons.get(StandardEntityURN.CIVILIAN.toString() + "-Male"); + } + else { + iconMap = icons.get(StandardEntityURN.CIVILIAN.toString() + "-Female"); + } + break; + default: + iconMap = icons.get(h.getStandardURN().toString()); + } + if (iconMap == null) { + return null; + } + return iconMap.get(state); + } + + private State getState(Human h) { + int hp = h.getHP(); + if (hp <= 0) { + return State.DEAD; + } + if (hp <= HP_CRITICAL) { + return State.CRITICAL; + } + if (hp <= HP_INJURED) { + return State.INJURED; + } + return State.HEALTHY; + } + + private enum State { + HEALTHY { + @Override + public String toString() { + return "Healthy"; + } + }, + INJURED { + @Override + public String toString() { + return "Injured"; + } + }, + CRITICAL { + @Override + public String toString() { + return "Critical"; + } + }, + DEAD { + @Override + public String toString() { + return "Dead"; + } + }; + } + + private static final class HumanSorter implements Comparator<Human>, java.io.Serializable { + private final StandardWorldModel world; + + public HumanSorter(StandardWorldModel world) { + this.world = world; + } + + @Override + public int compare(Human h1, Human h2) { + if(h1.isPositionDefined()&&h2.isPositionDefined()){ + if(world.getEntity(h1.getPosition())instanceof AmbulanceTeam && !(world.getEntity(h2.getPosition())instanceof AmbulanceTeam)) + return 1; + if(!(world.getEntity(h1.getPosition())instanceof AmbulanceTeam) && (world.getEntity(h2.getPosition())instanceof AmbulanceTeam)) + return -1; + } + if (h1 instanceof Civilian && !(h2 instanceof Civilian)) { + return -1; + } + + if (h2 instanceof Civilian && !(h1 instanceof Civilian)) { + return 1; + } + + if(h1.isHPDefined()&&h2.isHPDefined()) + return h2.getHP()-h1.getHP(); + return h1.getID().getValue() - h2.getID().getValue(); + } + } + + private final class UseIconsAction extends AbstractAction { + public UseIconsAction() { + super("Use icons"); + putValue(Action.SELECTED_KEY, Boolean.valueOf(useIcons)); + putValue(Action.SMALL_ICON, useIcons ? Icons.TICK : Icons.CROSS); + } + + @Override + public void actionPerformed(ActionEvent e) { + useIcons = !useIcons; + putValue(Action.SELECTED_KEY, Boolean.valueOf(useIcons)); + putValue(Action.SMALL_ICON, useIcons ? Icons.TICK : Icons.CROSS); + component.repaint(); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/PositionHistoryLayer.java b/modules/standard/src/rescuecore2/standard/view/PositionHistoryLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..bd1262a2da46385d65082d9f6f6e91d8d8254ab0 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/PositionHistoryLayer.java @@ -0,0 +1,57 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Shape; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.config.Config; + +import rescuecore2.standard.entities.Human; + +/** + A view layer that renders position history. + */ +public class PositionHistoryLayer extends StandardEntityViewLayer<Human> { + private static final Color PATH_COLOUR = Color.RED; + + /** + Construct a position history layer. + */ + public PositionHistoryLayer() { + super(Human.class); + } + + @Override + public void initialise(Config config) { + } + + @Override + public String getName() { + return "Position history"; + } + + @Override + public Shape render(Human h, Graphics2D g, ScreenTransform t) { + if (!h.isPositionHistoryDefined()) { + return null; + } + int[] history = h.getPositionHistory(); + // CHECKSTYLE:OFF:MagicNumber + if (history.length < 4) { + return null; + } + // CHECKSTYLE:ON:MagicNumber + g.setColor(PATH_COLOUR); + int x = t.xToScreen(history[0]); + int y = t.yToScreen(history[1]); + for (int i = 2; i < history.length; i += 2) { + int x2 = t.xToScreen(history[i]); + int y2 = t.yToScreen(history[i + 1]); + g.drawLine(x, y, x2, y2); + x = x2; + y = y2; + } + return null; + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/RoadBlockageLayer.java b/modules/standard/src/rescuecore2/standard/view/RoadBlockageLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..a274b4e5707c68e2f8599fbd9e091266ebb18a66 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/RoadBlockageLayer.java @@ -0,0 +1,49 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Shape; +import java.awt.Polygon; + +import rescuecore2.standard.entities.Blockade; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A view layer that renders road blockages. + */ +public class RoadBlockageLayer extends StandardEntityViewLayer<Blockade> { + private static final int BLOCK_SIZE = 3; + private static final int BLOCK_STROKE_WIDTH = 2; + + private static final Color COLOUR = Color.black; + + /** + Construct a road blockage rendering layer. + */ + public RoadBlockageLayer() { + super(Blockade.class); + } + + @Override + public String getName() { + return "Road blockages"; + } + + @Override + public Shape render(Blockade b, Graphics2D g, ScreenTransform t) { + if(!b.isApexesDefined()) + return null; + int[] apexes = b.getApexes(); + int count = apexes.length / 2; + int[] xs = new int[count]; + int[] ys = new int[count]; + for (int i = 0; i < count; ++i) { + xs[i] = t.xToScreen(apexes[i * 2]); + ys[i] = t.yToScreen(apexes[(i * 2) + 1]); + } + Polygon shape = new Polygon(xs, ys, count); + g.setColor(COLOUR); + g.fill(shape); + return shape; + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/RoadLayer.java b/modules/standard/src/rescuecore2/standard/view/RoadLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..bde13770417280d465fd6cb0eace5f8430cf9958 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/RoadLayer.java @@ -0,0 +1,50 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Stroke; +import java.awt.BasicStroke; +import java.awt.Polygon; + +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.Edge; +import rescuecore2.misc.gui.ScreenTransform; + +/** + A view layer that renders roads. + */ +public class RoadLayer extends AreaLayer<Road> { + private static final Color ROAD_EDGE_COLOUR = Color.GRAY.darker(); + private static final Color ROAD_SHAPE_COLOUR = new Color(185, 185, 185); + + private static final Stroke WALL_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + private static final Stroke ENTRANCE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + + /** + Construct a road rendering layer. + */ + public RoadLayer() { + super(Road.class); + } + + @Override + public String getName() { + return "Roads"; + } + + @Override + protected void paintShape(Road r, Polygon shape, Graphics2D g) { + g.setColor(ROAD_SHAPE_COLOUR); + g.fill(shape); + } + + @Override + protected void paintEdge(Edge e, Graphics2D g, ScreenTransform t) { + g.setColor(ROAD_EDGE_COLOUR); + g.setStroke(e.isPassable() ? ENTRANCE_STROKE : WALL_STROKE); + g.drawLine(t.xToScreen(e.getStartX()), + t.yToScreen(e.getStartY()), + t.xToScreen(e.getEndX()), + t.yToScreen(e.getEndY())); + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/StandardEntityInspector.java b/modules/standard/src/rescuecore2/standard/view/StandardEntityInspector.java new file mode 100644 index 0000000000000000000000000000000000000000..6a76fbad6d5ac6534c9836009c6ea9655107bf89 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/StandardEntityInspector.java @@ -0,0 +1,126 @@ +package rescuecore2.standard.view; + +import static rescuecore2.misc.collections.ArrayTools.convertArrayObjectToString; + +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; + +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.Comparator; + +import rescuecore2.registry.Registry; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.Property; + +/** + A component for inspecting Entities. +*/ +public class StandardEntityInspector extends JTable { + private static final Comparator<Property> PROPERTY_NAME_COMPARATOR = new Comparator<Property>() { + @Override + public int compare(Property p1, Property p2) { + return Integer.compare(p1.getURN(),p2.getURN()); + } + }; + + private EntityTableModel model; + + /** + Create a new EntityInspector. + */ + public StandardEntityInspector() { + model = new EntityTableModel(); + setModel(model); + } + + /** + Inspect an entity. + @param e The entity to inspect. + */ + public void inspect(Entity e) { + model.setEntity(e); + } + + private static class EntityTableModel extends AbstractTableModel { + private Entity e; + private List<Property> props; + + public EntityTableModel() { + e = null; + props = new ArrayList<Property>(); + } + + public void setEntity(Entity entity) { + e = entity; + props.clear(); + if (e != null) { + props.addAll(e.getProperties()); + Collections.sort(props, PROPERTY_NAME_COMPARATOR); + } + fireTableStructureChanged(); + fireTableDataChanged(); + } + + @Override + public int getRowCount() { + return props.size() + 2; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(int row, int col) { + switch (col) { + case 0: + if (row == 0) { + return "ID"; + } + else if (row == 1) { + return "Type"; + } + else { + return Registry.getCurrentRegistry().toPrettyName(props.get(row - 2).getURN()); + } + case 1: + if (row == 0) { + return e == null ? "-" : e.getID(); + } + else if (row == 1) { + return e == null?"-": Registry.getCurrentRegistry().toPrettyName(e.getURN()); + } + else { + Property prop = props.get(row - 2); + if (prop.isDefined()) { + Object value = prop.getValue(); + if (value.getClass().isArray()) { + return convertArrayObjectToString(value); + } + return value; + } + else { + return "Undefined"; + } + } + default: + throw new IllegalArgumentException("Invalid column: " + col); + } + } + + @Override + public String getColumnName(int col) { + switch (col) { + case 0: + return "Property"; + case 1: + return "Value"; + default: + throw new IllegalArgumentException("Invalid column: " + col); + } + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/StandardEntityViewLayer.java b/modules/standard/src/rescuecore2/standard/view/StandardEntityViewLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..f2fe7dff8c8bcab288b644266f2d27b7904c1e32 --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/StandardEntityViewLayer.java @@ -0,0 +1,95 @@ +package rescuecore2.standard.view; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.view.RenderedObject; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +import rescuecore2.standard.entities.StandardEntity; + +/** + An abstract base class for StandardWorldModel view layers that render standard entities. + @param <T> The subclass of StandardEntity that this layer knows how to render. + */ +public abstract class StandardEntityViewLayer<T extends StandardEntity> extends StandardViewLayer { + /** + The entities this layer should render. + */ + protected List<T> entities; + + private Class<T> clazz; + + /** + Construct a new StandardViewLayer. + @param clazz The class of entity that this layer can render. + */ + protected StandardEntityViewLayer(Class<T> clazz) { + this.clazz = clazz; + entities = new ArrayList<T>(); + } + + @Override + public Rectangle2D view(Object... objects) { + synchronized (entities) { + entities.clear(); + preView(); + Rectangle2D result = super.view(objects); + postView(); + return result; + } + } + + @Override + protected void viewObject(Object o) { + super.viewObject(o); + if (clazz.isAssignableFrom(o.getClass())) { + entities.add(clazz.cast(o)); + } + if (o instanceof WorldModel) { + WorldModel<? extends Entity> wm = (WorldModel<? extends Entity>)o; + for (Entity next : wm) { + viewObject(next); + } + } + } + + @Override + public Collection<RenderedObject> render(Graphics2D g, ScreenTransform transform, int width, int height) { + synchronized (entities) { + Collection<RenderedObject> result = new ArrayList<RenderedObject>(); + for (T next : entities) { + result.add(new RenderedObject(next, render(next, g, transform))); + } + return result; + } + } + + /** + Render an entity and return the shape. This shape is used for resolving mouse-clicks so should represent a hit-box for the entity. + @param entity The entity to render. + @param graphics The graphics to render on. + @param transform A helpful coordinate transformer. + @return A Shape that represents the hit-box of the rendered entity. + */ + public abstract Shape render(T entity, Graphics2D graphics, ScreenTransform transform); + + /** + Perform any pre-processing required before {@link #view} has been called. + */ + protected void preView() { + } + + /** + Perform any post-processing required after {@link #view} has been called. + */ + protected void postView() { + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/StandardViewLayer.java b/modules/standard/src/rescuecore2/standard/view/StandardViewLayer.java new file mode 100644 index 0000000000000000000000000000000000000000..769f6fe0f9096c39e62b8d6d9ae5eadccc7dce8e --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/StandardViewLayer.java @@ -0,0 +1,53 @@ +package rescuecore2.standard.view; + +import java.awt.geom.Rectangle2D; + +import rescuecore2.config.Config; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.view.AbstractViewLayer; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.Entity; + +import static rescuecore2.standard.view.StandardWorldModelViewer.STANDARD_VIEWER_PREFIX; + +/** + An abstract base class for StandardWorldModel view layers. + */ +public abstract class StandardViewLayer extends AbstractViewLayer { + /** Default visibility setting for a layer. */ + public static final String VISIBILITY_SUFFIX = "visible"; + + /** + The StandardWorldModel to view. + */ + protected StandardWorldModel world; + + /** + Construct a new StandardViewLayer. + */ + protected StandardViewLayer() { + } + + @Override + public void initialise(Config config) { + String visibleKey = STANDARD_VIEWER_PREFIX + "." + this.getClass().getSimpleName() + "." + VISIBILITY_SUFFIX; + boolean isVisible = config.getBooleanValue(visibleKey, isVisible()); + setVisible(isVisible); + } + + @Override + public Rectangle2D view(Object... objects) { + processView(objects); + if (world == null) { + return null; + } + return world.getBounds(); + } + + @Override + protected void viewObject(Object o) { + if (o instanceof WorldModel) { + world = StandardWorldModel.createStandardWorldModel((WorldModel<? extends Entity>)o); + } + } +} diff --git a/modules/standard/src/rescuecore2/standard/view/StandardWorldModelViewer.java b/modules/standard/src/rescuecore2/standard/view/StandardWorldModelViewer.java new file mode 100644 index 0000000000000000000000000000000000000000..76511dbec29eee993ff4a1d4402822f72477115c --- /dev/null +++ b/modules/standard/src/rescuecore2/standard/view/StandardWorldModelViewer.java @@ -0,0 +1,37 @@ +package rescuecore2.standard.view; + +import rescuecore2.view.LayerViewComponent; + +/** + A viewer for StandardWorldModels. + */ +public class StandardWorldModelViewer extends LayerViewComponent { + /** Prefix for setting related to the StandardWorldModelViewer. */ + public static final String STANDARD_VIEWER_PREFIX = "viewer.standard"; + + /** + Construct a standard world model viewer. + */ + public StandardWorldModelViewer() { + addDefaultLayers(); + } + + @Override + public String getViewerName() { + return "Standard world model viewer"; + } + + /** + Add the default layer set, i.e. nodes, roads, buildings, humans and commands. + */ + public void addDefaultLayers() { + addLayer(new BuildingLayer()); + addLayer(new RoadLayer()); + addLayer(new AreaNeighboursLayer()); + addLayer(new RoadBlockageLayer()); + addLayer(new AreaIconLayer()); + addLayer(new HumanLayer()); + addLayer(new CommandLayer()); + addLayer(new PositionHistoryLayer()); + } +} diff --git a/modules/traffic3/resources/data/resources/default-menubar.xml b/modules/traffic3/resources/data/resources/default-menubar.xml new file mode 100644 index 0000000000000000000000000000000000000000..c0e08ce73c3a90b8df2dde8494f944cbae27e185 --- /dev/null +++ b/modules/traffic3/resources/data/resources/default-menubar.xml @@ -0,0 +1,47 @@ +<default-menubar> + <menu-item type="list" name="File"> + <menu-item type="action" name="Import" class="traffic3.manager.gui.action.ImportAction"/> + <menu-item type="action" name="Import from old version" class="traffic3.manager.gui.action.ImportAction2"/> + <menu-item type="action" name="Export" class="traffic3.manager.gui.action.ExportAction"/> + <menu-item type="separator"/> + <menu-item type="action" class="traffic3.manager.gui.action.ExitAction"/> + </menu-item> + <menu-item type="list" name="View"> + <menu-item type="action" class="traffic3.manager.gui.action.FitViewAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.ShowAllAsTextAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.ShowTargetsAsTextAction"/> + <menu-item type="separator"/> + <menu-item type="list" name="show"> + <menu-item type="check" value="antiariasing" init="false"/> + <menu-item type="check" value="showArea" init="true"/> + <menu-item type="check" value="showAreaEdge" init="true"/> + <menu-item type="check" value="showAreaNode" init="false"/> + <menu-item type="check" value="showAreaConnector" init="false"/> + <menu-item type="check" value="showAreaNodeID" init="false"/> + </menu-item> + </menu-item> + <menu-item type="list" name="Edit"> + <menu-item type="list" name="delete"> + <menu-item type="action" class="traffic3.manager.gui.action.ClearAllAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.ClearAllAgentsAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.DeleteSelectionAction"/> + </menu-item> + <menu-item type="list" name="select"> + <menu-item type="action" class="traffic3.manager.gui.action.SelectAllAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.SelectAllAgentsAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.SelectByIdAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.SelectAgentGroupAction"/> + </menu-item> + <menu-item type="list" name="validate"> + <menu-item type="action" class="traffic3.manager.gui.action.ValidateMapAction"/> + </menu-item> + </menu-item> + <menu-item type="list" name="Devel"> + <menu-item type="action" class="traffic3.manager.gui.action.RecAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.RecAction2"/> + <menu-item type="action" class="traffic3.manager.gui.action.ShowLogAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.ShowVersionAction"/> + <menu-item type="separator"/> + <menu-item type="check" value="simulating" init="false"/> + </menu-item> +</default-menubar> diff --git a/modules/traffic3/resources/data/resources/default-popup.xml b/modules/traffic3/resources/data/resources/default-popup.xml new file mode 100644 index 0000000000000000000000000000000000000000..8e8b21add5147ff737ea1e2f06eba11c460fcf5a --- /dev/null +++ b/modules/traffic3/resources/data/resources/default-popup.xml @@ -0,0 +1,12 @@ +<default-toolbar> + <menu-item type="action" class="traffic3.manager.gui.action.ShowTargetsAsTextAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.CreateAreaAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.SetAsConnectorAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.RecAction2"/> + <menu-item type="action" class="traffic3.manager.gui.action.PutAgentsAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.PutAgentsAction2"/> + <menu-item type="action" class="traffic3.manager.gui.action.SetDestinationAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.PutBlockadeAction"/> + <menu-item type="action" class="traffic3.manager.gui.action.PutBlockadeAction2"/> + <menu-item type="action" class="traffic3.manager.gui.action.SetAreaTypeAction"/> +</default-toolbar> diff --git a/modules/traffic3/resources/data/resources/handle.png b/modules/traffic3/resources/data/resources/handle.png new file mode 100644 index 0000000000000000000000000000000000000000..3c5586a8c0c1347fb658e7b36dbded165b404e50 Binary files /dev/null and b/modules/traffic3/resources/data/resources/handle.png differ diff --git a/modules/traffic3/resources/data/resources/video.html b/modules/traffic3/resources/data/resources/video.html new file mode 100644 index 0000000000000000000000000000000000000000..6d0cdd19adab9a3be54a3f5cce8bd936d53dfc62 --- /dev/null +++ b/modules/traffic3/resources/data/resources/video.html @@ -0,0 +1,96 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <style type="text/css"> + h1 {text-align:center;} + h2 {text-align:left;} + div.author {text-align:center;} + div.name {text-align:right;} + div.affiliation {text-align:right;font-size:60%;} + div.fig {text-align:center; margin:30px auto 30px auto;} + math {white-space: nowrap; margin:20px;} + div.caption{font: caption;font-weight: bold;font-size: 70%;margin-top:10px;text-align:center; } + .test {margin:10px auto 10px auto; } + .source {font-size: 70%;text-align:left;font-family: monospace;background: white;width:80%;padding-left:1em;margin-left:auto;margin-right:auto;border-style:dotted;border-width:1px; } + .none {border:none;background: none;margin:0;padding:0; } + table {border-style:solid;border-spacing:0;border-width:1px 0 0 1px;text-align:center;background:white;margin-left:auto;margin-right:auto;} + th{background:#444444;color:white;padding:3px 10px;border-style:solid;border-width: 0 1px 1px 0;border-color:black;} + td{border-style:solid;padding:0 10px;border-width:0 1px 1px 0;text-align:left;} + div.slide{margin-bottom:100px;margin-left:auto;margin-right:auto;} + .note{margin:10px;border:dotted 1px;background:white;font-size:0px;visibility:hidden;} + div.slide_cover{margin-bottom:100px;} + ul.i0 {list-style-image:url(./../img/item/darkball_blue24.png); } + ul.i1 {list-style-image:url(./../img/item/darkball_red24.png); } + ul.i2 {list-style-image:url(./../img/item/darkball_yellow24.png); } + ul.i3 {list-style-image:url(./../img/item/darkball_pink24.png); } + ul.i4 {list-style-image:url(./../img/item/darkball_black24.png); } + ul.i5 {list-style-image:url(./../img/item/darkball_green24.png); } + a:link {text-decoration:none } + a:active {text-decoration:none } + a:visited {text-decoration:none } + html{width:100%; height:100%; margin:0; } + body{width:100%; height:100%; margin:0 auto 400px auto; background:#cccccc; } + .control-button {text-align:center;font-family:arial;font-size:13px;font-weight:bold;background:#aaaaaa;margin:1px;padding:0px 2px;border:solid 1px gray;cursor:pointer;} + </style> + + <title>Video</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <script type="text/javascript" src="./videoPlayer.js"></script> + <script type="text/javascript" src="./videoInformation.js"></script> + <script type="text/javascript"> + /*<!--*/ + + //--> + </script> + </head> + <body + onload="setFrame(0);" + style="margin:auto;" + onmousemove="x=event.pageX;y=event.pageY;if(down_flag) { draggedHandleTo(x,y); };lastx=x;lasty=y;return true;"> + + <div style="margin:60px;"> + <div class="test" style="margin:0px;"> + <table id="video-player" class="none" style="border:solid 1px gray;"> + <tr> + <td class="none"> + <img id="target" src="" alt="Video"/> + </td> + </tr> + <tr> + <td class="none"> + <table class="none"> + <tr> + <td class="none"> + <div class="control-button" onclick="prev();"><</div> + </td> + <td class="none"> + <div id="play-button" style="width:35px;" class="control-button" onclick="switchPlayStop();">play</div> + </td> + <td class="none"> + <div class="control-button" onclick="next();">></div> + </td> + <td class="none"> + <div class="control-button" onclick="switchPlaySpeed();">s</div> + </td> + <td class="none" style="width:100%;"> + <div id="progress-back" onmouseup="down_flag=false;" onmouseexit="down_flag=false;" style="height:4px;border:solid 1px gray;margin:5px;"> + <div id="progress" style="margin:-1;border:solid 1px gray;height:100%;width:0;background:red;"> + <div id="progress-handle" style="cursor:pointer;border:solid 0px;;height:10px;width:10px;margin:-7px 0px auto auto;" onmousedown="down_flag=true; down_x=event.pageX; down_counter=counter;down_y=event.pageY;"> + <img src="./handle.png" width="18px" height="18px" alt=""/> + </div> + </div> + </div> + </td> + <td class="none"> + <div id="count_number" style="background:white;border:solid 1px gray;margin:1px;width:30px;padding:0px;font-family:arial;font-weight:bold;Arial;font-size:13px;text-align:right;"> + [ ]</div> + </td> + </tr> + </table> + </td> + </tr> + </table> + </div> + </div> + </body> + </html> diff --git a/modules/traffic3/resources/data/resources/videoInformation.js b/modules/traffic3/resources/data/resources/videoInformation.js new file mode 100644 index 0000000000000000000000000000000000000000..716dade23911145e4b7facee1848d0ef73ee553f --- /dev/null +++ b/modules/traffic3/resources/data/resources/videoInformation.js @@ -0,0 +1,4 @@ +var before = "p"; +var after = ".png"; +var cmax = 100; +var wait = 10; \ No newline at end of file diff --git a/modules/traffic3/resources/data/resources/videoPlayer.js b/modules/traffic3/resources/data/resources/videoPlayer.js new file mode 100644 index 0000000000000000000000000000000000000000..14fc4b2e087a00987a59b710e632feadacf334a0 --- /dev/null +++ b/modules/traffic3/resources/data/resources/videoPlayer.js @@ -0,0 +1,101 @@ +var is_playing = false; +var counter = 0; +var wait = 0; + +var before = "p"; +var after = ".png"; +var cmax = 10; +var speed = "play"; + +function next() { + if (counter >= cmax) { + stop(); + counter=0; + } + else{ + counter = (counter + 1); + } + update(); + if(is_playing) { + if("play" == speed) + setTimeout("next()", wait); + else + setTimeout("next()", 0); + } +}; + +function switchPlaySpeed() { + if("play" == speed) + speed = "fast"; + else + speed = "play"; +} + +function prev() { + if (counter <= 0) { + counter=0; + } + else{ + counter = counter - 1; + } + update(); +}; + +function update() { + target = document.getElementById("target"); + target.setAttribute("src", before+counter+after); + document.getElementById("count_number").innerHTML = counter; + var point = parseInt(counter*100/cmax); + document.getElementById("progress").style.width=point+"%"; +}; + +function play() { + document.getElementById("play-button").innerHTML="stop"; + is_playing=true; + next(); +}; +function stop() { + document.getElementById("play-button").innerHTML="play"; + is_playing=false; +}; + +function switchPlayStop() { + if(is_playing) stop(); + else play(); +}; + +function draggedHandleTo(x, y) { + var base = document.getElementById("progress-handle"); + var dx = x-down_x; + /*alert(base.offsetLeft); + */ var dy = y-down_y; + /*alert(down_x); + */ var pb = document.getElementById("progress-back"); + var width = parseInt(pb.offsetWidth); + counter = parseInt(dx*cmax/width+down_counter); + if(counter<0) counter=0; + else if(counter>cmax) counter=cmax; + update(); +}; + +document.onmousedown = function(event) { + return false; +}; + +document.onmousemove = function(event) { + return false; +}; + +document.onmouseup = function(event) { + down_flag=false; + return false; +}; + +var down_flag=false; +var lastx=0; +var lasty=0; +var down_x,down_y,down_counter; +function setFrame(frame) { + counter=frame; + update(); +}; diff --git a/modules/traffic3/src/traffic3/manager/TrafficManager.java b/modules/traffic3/src/traffic3/manager/TrafficManager.java new file mode 100644 index 0000000000000000000000000000000000000000..44b9c489a3274028b330e55e89032a9f78f9c2a2 --- /dev/null +++ b/modules/traffic3/src/traffic3/manager/TrafficManager.java @@ -0,0 +1,302 @@ +package traffic3.manager; + +import com.infomatiq.jsi.Rectangle; +import com.infomatiq.jsi.SpatialIndex; +import com.infomatiq.jsi.rtree.RTree; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import rescuecore2.misc.collections.LazyMap; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardWorldModel; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import gnu.trove.TIntProcedure; +import traffic3.objects.TrafficAgent; +import traffic3.objects.TrafficArea; +import traffic3.objects.TrafficBlockade; + +/** + * The traffic manager maintains information about traffic simulator objects. + */ +public class TrafficManager { + + private Map<Integer, TrafficArea> areaByID; + private Map<Integer, TrafficBlockade> blockadeByID; + private Map<Area, TrafficArea> areas; + private Map<Blockade, TrafficBlockade> blocks; + private Map<Human, TrafficAgent> agents; + private Map<TrafficArea, Collection<TrafficArea>> areaNeighbours; + + private SpatialIndex index; + + /** + * Construct a new TrafficManager. + */ + public TrafficManager() { + areas = new ConcurrentHashMap<Area, TrafficArea>(); + areaByID = new ConcurrentHashMap<Integer, TrafficArea>(); + blocks = new ConcurrentHashMap<Blockade, TrafficBlockade>(); + blockadeByID = new ConcurrentHashMap<Integer, TrafficBlockade>(); + agents = new ConcurrentHashMap<Human, TrafficAgent>(); + areaNeighbours = new LazyMap<TrafficArea, Collection<TrafficArea>>() { + + @Override + public Collection<TrafficArea> createValue() { + return new HashSet<TrafficArea>(); + } + }; + index = new RTree(); + index.init(new Properties()); + } + + + /** + * Find the area that contains a point. + * + * @param x + * The X coordinate. + * @param y + * The Y coordinate. + * + * @return The TrafficArea that contains the given point, or null if no such + * area is found. + */ + public TrafficArea findArea(double x, double y) { + final List<TrafficArea> found = new ArrayList<TrafficArea>(); + index.intersects(new Rectangle((float) x, (float) y, (float) x, (float) y), + new TIntProcedure() { + + @Override + public boolean execute(int id) { + found.add(areaByID.get(id)); + return true; + } + }); + for (TrafficArea next : found) { + if (next.contains(x, y)) { + return next; + } + } + return null; + } + + + /** + * Get the neighbouring areas to a TrafficArea. + * + * @param area + * The area to look up. + * + * @return All TrafficAreas that share an edge with the given area. + */ + public Collection<TrafficArea> getNeighbours(TrafficArea area) { + return areaNeighbours.get(area); + } + + + /** + * Get all agents in the same area or a neighbouring area as an agent. + * + * @param agent + * The agent to look up. + * + * @return All agents (except the input agent) that are in the same or a + * neighbouring area. + */ + public Collection<TrafficAgent> getNearbyAgents(TrafficAgent agent) { + Set<TrafficAgent> result = new HashSet<TrafficAgent>(); + result.addAll(agent.getArea().getAgents()); + for (TrafficArea next : getNeighbours(agent.getArea())) { + result.addAll(next.getAgents()); + } + result.remove(agent); + return result; + } + + + /** + * Remove all objects from this manager. + */ + public void clear() { + areas.clear(); + blocks.clear(); + agents.clear(); + areaNeighbours.clear(); + areaByID.clear(); + blockadeByID.clear(); + index = new RTree(); + index.init(new Properties()); + } + + + /** + * Register a new TrafficArea. + * + * @param area + * The TrafficArea to register. + */ + public void register(TrafficArea area) { + areas.put(area.getArea(), area); + int id = area.getArea().getID().getValue(); + areaByID.put(id, area); + index.add(area.getBounds(), id); + } + + + /** + * Register a new TrafficAgent. + * + * @param agent + * The TrafficAgent to register. + */ + public void register(TrafficAgent agent) { + agents.put(agent.getHuman(), agent); + } + + + /** + * Register a new TrafficBlockade. + * + * @param block + * The TrafficBlockade to register. + */ + public void register(TrafficBlockade block) { + blocks.put(block.getBlockade(), block); + blockadeByID.put(block.getBlockade().getID().getValue(), block); + } + + + /** + * Remove a blockade. + * + * @param block + * The TrafficBlockade to remove. + */ + public void remove(TrafficBlockade block) { + remove(block.getBlockade()); + } + + + /** + * Remove a blockade. + * + * @param block + * The Blockade to remove. + */ + public void remove(Blockade block) { + blocks.remove(block); + blockadeByID.remove(block.getID().getValue()); + } + + + /** + * Get all TrafficAgents. + * + * @return All TrafficAgents. + */ + public Collection<TrafficAgent> getAgents() { + return Collections.unmodifiableCollection(agents.values()); + } + + + /** + * Get all TrafficAreas. + * + * @return All TrafficAreas. + */ + public Collection<TrafficArea> getAreas() { + return Collections.unmodifiableCollection(areas.values()); + } + + + /** + * Get all TrafficBlockades. + * + * @return All TrafficBlockades. + */ + public Collection<TrafficBlockade> getBlockades() { + return Collections.unmodifiableCollection(blocks.values()); + } + + + /** + * Compute pre-cached information about the world. TrafficArea and + * TrafficAgent objects must have already been registered with + * {@link #register(TrafficArea)} and {@link #register(TrafficAgent)}. + * + * @param world + * The world model. + */ + public void cacheInformation(StandardWorldModel world) { + areaNeighbours.clear(); + for (StandardEntity next : world) { + if (next instanceof Area) { + computeNeighbours((Area) next, world); + } + } + } + + + /** + * Get the TrafficArea that wraps a given Area. + * + * @param a + * The area to look up. + * + * @return The TrafficArea that wraps the given area or null if no such + * TrafficArea exists. + */ + public TrafficArea getTrafficArea(Area a) { + return areas.get(a); + } + + + /** + * Get the TrafficBlockade that wraps a given Blockade. + * + * @param b + * The blockade to look up. + * + * @return The TrafficBlockade that wraps the given blockade or null if no + * such TrafficBlockade exists. + */ + public TrafficBlockade getTrafficBlockade(Blockade b) { + return blocks.get(b); + } + + + /** + * Get the TrafficAgent that wraps a given human. + * + * @param h + * The human to look up. + * + * @return The TrafficAgent that wraps the given human or null if no such + * TrafficAgent exists. + */ + public TrafficAgent getTrafficAgent(Human h) { + return agents.get(h); + } + + + private void computeNeighbours(Area a, StandardWorldModel world) { + Collection<TrafficArea> neighbours = areaNeighbours.get(getTrafficArea(a)); + neighbours.clear(); + for (EntityID id : a.getNeighbours()) { + Entity e = world.getEntity(id); + if (e instanceof Area) { + neighbours.add(getTrafficArea((Area) e)); + } + } + } +} \ No newline at end of file diff --git a/modules/traffic3/src/traffic3/objects/Force.java b/modules/traffic3/src/traffic3/objects/Force.java new file mode 100644 index 0000000000000000000000000000000000000000..0d94082c00c24678deac1fce7dc0e4be35a31ae5 --- /dev/null +++ b/modules/traffic3/src/traffic3/objects/Force.java @@ -0,0 +1,71 @@ +package traffic3.objects; + +/** + Container for force information. + */ +public class Force { + private double forceX; + private double forceY; + + /** + Construct a zero force. + */ + public Force() { + this(0, 0); + } + + /** + Construct a Force with given components. + @param x The X component of this force. + @param y The Y component of this force. + */ + public Force(double x, double y) { + forceX = x; + forceY = y; + } + + /** + Get the X component of this force. + @return The X component of the force. + */ + public double getX() { + return forceX; + } + + /** + Get the Y component of this force. + @return The Y component of the force. + */ + public double getY() { + return forceY; + } + + /** + Add to this force. + @param x The amount to add to the X component. + @param y The amount to add to the Y component. + */ + public void add(double x, double y) { + forceX += x; + forceY += y; + } + + /** + Add some other forces to this force. + @param forces The forces to add. + */ + public void add(Force... forces) { + for (Force next : forces) { + forceX += next.forceX; + forceY += next.forceY; + } + } + + /** + Zero this force. + */ + public void clear() { + forceX = 0; + forceY = 0; + } +} diff --git a/modules/traffic3/src/traffic3/objects/TrafficAgent.java b/modules/traffic3/src/traffic3/objects/TrafficAgent.java new file mode 100644 index 0000000000000000000000000000000000000000..8d79916667991b9e5b1180775dfc2bf9fee3bc8e --- /dev/null +++ b/modules/traffic3/src/traffic3/objects/TrafficAgent.java @@ -0,0 +1,1164 @@ +package traffic3.objects; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import rescuecore2.log.Logger; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.standard.entities.Building; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.Road; +import traffic3.manager.TrafficManager; +import traffic3.simulator.PathElement; +import traffic3.simulator.TrafficConstants; + +/** + * A TrafficAgent is a mobile object in the world. + */ +public class TrafficAgent { + + /** + * This class is used to compute and cache wall related information. + * + * @author goebelbe + * + */ + private static class WallInfo { + private Line2D wall; + private TrafficArea area; + private double distance; + private Point2D closest; + private Point2D origin; + private Line2D line; + private Vector2D vector; + + /** + * Create a new WallInfo object from a Line2D in a TrafficArea. + * + * @param wall + * The wall to cache. + * @param area + * The area this wall belongs to. + */ + public WallInfo(Line2D wall, TrafficArea area) { + this.wall = wall; + this.area = area; + this.distance = -1; + this.closest = null; + this.origin = null; + } + + /** + * Get the shortest distance from the agent's position. The distance may + * not be accurate if the wall can't affect the agent in this microstep. + * + * @return The distance to the agent. + */ + public double getDistance() { + return this.distance; + } + + /** + * Recompute the distance to the agent and the closest point on the + * line. + * + * @param from + * The position of the agent. + */ + public void computeClostestPoint(Point2D from) { + if (from.equals(origin) && distance >= 0 && closest != null) { + return; + } + origin = from; + closest = GeometryTools2D.getClosestPointOnSegment(wall, origin); + line = new Line2D(origin, closest); + vector = line.getDirection(); + distance = vector.getLength(); + } + + /** + * Get the clostest point to the agent on the wall. + * + * @return The closest point. + */ + public Point2D getClosestPoint() { + return closest; + } + + /** + * Decrease the distance from the wall by an amount. + * + * @param d + * The amount by which to decrease the distance. + */ + public void decreaseDistance(double d) { + distance -= d; + } + + /** + * Get the wall this WallInfo represents. + * + * @return The wall. + */ + public Line2D getWall() { + return wall; + } + + /** + * Get the line from the agent to the closest point on the wall. + * + * @return Line2D to wall. + */ + public Line2D getLine() { + return line; + } + + /** + * Get the vector from the agent to the closest point on the wall. + * + * @return Vector2D to wall. + */ + public Vector2D getVector() { + return vector; + } + + /** + * Get the are the wall lies in. + * + * @return The area of this wall. + */ + public TrafficArea getArea() { + return area; + } + } + + private static final int D = 2; + + private static final int DEFAULT_POSITION_HISTORY_FREQUENCY = 60; + + private static final double NEARBY_THRESHOLD_SQUARED = 1000000; + + // Force towards destination + private final double[] destinationForce = new double[D]; + + // Force away from agents + private final double[] agentsForce = new double[D]; + + // Force away from walls + private final double[] wallsForce = new double[D]; + + // Location + private final double[] location = new double[D]; + + // Velocity + private final double[] velocity = new double[D]; + + // Force + private final double[] force = new double[D]; + + // List of blocking lines near the agent. + private List<WallInfo> blockingLines; + + private double radius; + private double velocityLimit; + + // The point this agent wants to reach. + private Point2D finalDestination; + + // The path this agent wants to take. + private Queue<PathElement> path; + + // The current (possibly intermediate) destination. + private PathElement currentPathElement; + private Point2D currentDestination; + + // The area the agent is currently in. + private TrafficArea currentArea; + + private List<Point2D> positionHistory; + private double totalDistance; + private boolean savePositionHistory; + private int positionHistoryFrequency; + private int historyCount; + + private Human human; + private TrafficManager manager; + + private boolean mobile; + private boolean colocated; + private boolean verbose; + + private TrafficArea startPosition; + + /** + * Construct a TrafficAgent. + * + * @param human + * The Human wrapped by this object. + * @param manager + * The traffic manager. + * @param radius + * The radius of this agent in mm. + * @param velocityLimit + * The velicity limit. + */ + public TrafficAgent(Human human, TrafficManager manager, double radius, double velocityLimit) { + this.human = human; + this.manager = manager; + this.radius = radius; + this.velocityLimit = velocityLimit; + path = new LinkedList<PathElement>(); + positionHistory = new ArrayList<Point2D>(); + savePositionHistory = true; + historyCount = 0; + positionHistoryFrequency = DEFAULT_POSITION_HISTORY_FREQUENCY; + mobile = true; + blockingLines = new ArrayList<WallInfo>(); + } + + /** + * Get the Human wrapped by this object. + * + * @return The wrapped Human. + */ + public Human getHuman() { + return human; + } + + /** + * Get the maximum velocity of this agent. + * + * @return The maximum velocity. + */ + public double getMaxVelocity() { + return velocityLimit; + } + + /** + * Set the maximum velocity of this agent. + * + * @param vLimit + * The new maximum velocity. + */ + public void setMaxVelocity(double vLimit) { + velocityLimit = vLimit; + } + + /** + * Get the TrafficArea the agent is currently in. + * + * @return The current TrafficArea. + */ + public TrafficArea getArea() { + return currentArea; + } + + /** + * Get the position history. + * + * @return The position history. + */ + public List<Point2D> getPositionHistory() { + return Collections.unmodifiableList(positionHistory); + } + + /** + * Get the distance travelled so far. + * + * @return The distance travelled. + */ + public double getTravelDistance() { + return totalDistance; + } + + /** + * Clear the position history and distance travelled. + */ + public void clearPositionHistory() { + positionHistory.clear(); + historyCount = 0; + totalDistance = 0; + } + + /** + * Set the frequency of position history records. One record will be created + * every nth microstep. + * + * @param n + * The new frequency. + */ + public void setPositionHistoryFrequency(int n) { + positionHistoryFrequency = n; + } + + /** + * Enable or disable position history recording. + * + * @param b + * True to enable position history recording, false otherwise. + */ + public void setPositionHistoryEnabled(boolean b) { + savePositionHistory = b; + } + + /** + * Get the X coordinate of this agent. + * + * @return The X coordinate. + */ + public double getX() { + return location[0]; + } + + /** + * Get the Y coordinate of this agent. + * + * @return The Y coordinate. + */ + public double getY() { + return location[1]; + } + + /** + * Get the total X force on this agent. + * + * @return The total X force in N. + */ + public double getFX() { + return force[0]; + } + + /** + * Get the total Y force on this agent. + * + * @return The total Y force in N. + */ + public double getFY() { + return force[1]; + } + + /** + * Get the X velocity of this agent. + * + * @return The X velocity in mm/s. + */ + public double getVX() { + return velocity[0]; + } + + /** + * Get the Y velocity of this agent. + * + * @return The Y velocity in mm/s. + */ + public double getVY() { + return velocity[1]; + } + + /** + * Set the radius of this agent. + * + * @param r + * The new radius in mm. + */ + public void setRadius(double r) { + this.radius = r; + } + + /** + * Get the radius of this agent. + * + * @return The radius in mm. + */ + public double getRadius() { + return this.radius; + } + + /** + * Set the path this agent wants to take. + * + * @param steps + * The new path. + */ + public void setPath(List<PathElement> steps) { + if (steps == null || steps.isEmpty()) { + clearPath(); + return; + } + path.clear(); + path.addAll(steps); + finalDestination = steps.get(steps.size() - 1).getGoal(); + currentDestination = null; + currentPathElement = null; + // Logger.debug(this + " destination set: " + path); + // Logger.debug(this + " final destination set: " + finalDestination); + } + + /** + * Clear the path. + */ + public void clearPath() { + finalDestination = null; + currentDestination = null; + currentPathElement = null; + path.clear(); + } + + /** + * Get the final destination. + * + * @return The final destination + */ + public Point2D getFinalDestination() { + return finalDestination; + } + + /** + * Get the current (possibly intermediate) destination. + * + * @return The current destination. + */ + public Point2D getCurrentDestination() { + return currentDestination; + } + + /** + * Get the current (possibly intermediate) path element. + * + * @return The current path element. + */ + public PathElement getCurrentElement() { + return currentPathElement; + } + + /** + * Get the current path. + * + * @return The path. + */ + public List<PathElement> getPath() { + return Collections.unmodifiableList((List<PathElement>) path); + } + + /** + * Set the location of this agent. This method will also update the position + * history (if enabled). + * + * @param x + * location x + * @param y + * location y + */ + public void setLocation(double x, double y) { + if (currentArea == null || !currentArea.contains(x, y)) { + if (currentArea != null) { + currentArea.removeAgent(this); + } + TrafficArea newArea = manager.findArea(x, y); + + if (newArea == null) { + Logger.warn(getHuman() + "moved outside area: " + this); + return; + } + + currentArea = newArea; + findBlockingLines(); + currentArea.addAgent(this); + } + // Check current destination + if (currentPathElement != null) { + // If we just crossed the target edge then clear the current path + // element + if (currentDestination == currentPathElement.getGoal() && currentDestination != finalDestination) { + // Did we cross the edge? + if (currentPathElement.getEdgeLine() != null && crossedLine(location[0], location[1], x, y, currentPathElement.getEdgeLine())) { + currentPathElement = null; + } + // Are we close enough to the goal point? + else { + double dx = x - currentDestination.getX(); + double dy = y - currentDestination.getY(); + double distanceSquared = dx * dx + dy * dy; + if (distanceSquared < NEARBY_THRESHOLD_SQUARED) { + currentPathElement = null; + } + } + } + } + // Save position history + if (savePositionHistory) { + if (historyCount % positionHistoryFrequency == 0) { + positionHistory.add(new Point2D(x, y)); + } + historyCount++; + + // Update distance travelled + double dx = x - location[0]; + double dy = y - location[1]; + totalDistance += Math.hypot(dx, dy); + } + location[0] = x; + location[1] = y; + } + + private boolean haveThisAreaInPath(TrafficArea newArea) { + for (PathElement path : getPath()) { + if (path.getAreaID().equals(newArea.getArea().getID())) + return true; + } + return false; + } + + /** + * Perform any pre-timestep activities required. + */ + public void beginTimestep() { + findBlockingLines(); + if (insideBlockade()) { + Logger.debug(this + " inside blockade"); + setMobile(false); + } + startPosition = currentArea; + } + + /** + * Execute a microstep. + * + * @param dt + * The amount of time to simulate in ms. + */ + public void step(double dt) { + if (mobile) { + updateWalls(dt); + updateGoals(); + computeForces(dt); + updatePosition(dt); + } + } + + /** + * Perform any post-timestep activities required. + */ + public void endTimestep() { + handleOutOfActionCivilianMoves(); + + } + + private void handleOutOfActionCivilianMoves() { + if (!(getHuman() instanceof Civilian)) + return; + if (currentArea.getArea().equals(startPosition.getArea())) + return; + if (!(currentArea.getArea() instanceof Building)) + return; + if (getPath().isEmpty()) + return; + if (haveThisAreaInPath(currentArea)) + return; + + Logger.warn(getHuman() + " moved to unplaned building(" + currentArea + ") " + this); + TrafficArea newDest = getBestRoadNeighbor(currentArea, new HashSet<TrafficArea>()); + if (newDest == null) { + Logger.warn(currentArea + " dosen't connected to any Road!"); + return; + } + setLocation(newDest.getArea().getX(), newDest.getArea().getY()); + + } + + private TrafficArea getBestRoadNeighbor(TrafficArea area, HashSet<TrafficArea> checked) { + checked.add(area); + if (area.getArea() instanceof Road) + return area; + for (TrafficArea neighbor : manager.getNeighbours(area)) { + if (neighbor.getArea() instanceof Road) + return neighbor; + } + for (TrafficArea neighbor : manager.getNeighbours(area)) { + if (checked.contains(neighbor)) + continue; + TrafficArea result = getBestRoadNeighbor(neighbor, checked); + if (result != null) + return result; + } + return null; + } + + /** + * Set whether this agent is mobile or not. + * + * @param m + * True if this agent is mobile, false otherwise. + */ + public void setMobile(boolean m) { + mobile = m; + } + + /** + * Find out if this agent is mobile. + * + * @return True if this agent is mobile. + */ + public boolean isMobile() { + return mobile; + } + + /** + * Turn verbose logging on or off. + * + * @param b + * True for piles of debugging output, false for smaller piles. + */ + public void setVerbose(boolean b) { + verbose = b; + Logger.debug(this + " is now " + (verbose ? "" : "not ") + "verbose"); + } + + private void updateGoals() { + if (currentPathElement == null) { + if (path.isEmpty()) { + currentDestination = finalDestination; + currentPathElement = null; + } else { + currentPathElement = path.remove(); + if (verbose) { + Logger.debug(this + " updated path: " + path); + } + } + } + // Head for the best point in the current path element + if (currentPathElement != null) { + // Assume we're heading for the target edge. + currentDestination = currentPathElement.getGoal(); + Point2D current = new Point2D(location[0], location[1]); + Vector2D vectorToEdge = currentDestination.minus(current).normalised(); + if (verbose) { + Logger.debug(this + " finding goal point"); + Logger.debug(this + " current path element: " + currentPathElement); + Logger.debug(this + " current position: " + current); + Logger.debug(this + " edge goal: " + currentDestination); + } + for (Point2D next : currentPathElement.getWaypoints()) { + if (verbose) { + Logger.debug(this + " next possible goal: " + next); + } + if (next != currentPathElement.getGoal()) { + Vector2D vectorToNext = next.minus(current).normalised(); + double dot = vectorToNext.dot(vectorToEdge); + if (dot < 0 || dot > 1) { + if (verbose) { + Logger.debug("Dot product of " + vectorToNext + " and " + vectorToEdge + " is " + dot); + Logger.debug(this + " next point is " + (dot < 0 ? "backwards" : "too distant") + "; ignoring"); + } + continue; + } + } + + if (hasLos(current, next, currentArea)) { + currentDestination = next; + if (verbose) { + Logger.debug(this + " has line-of-sight to " + next); + } + break; + } + } + } + } + + private void computeForces(double dt) { + colocated = false; + computeAgentsForce(agentsForce); + if (!colocated) { + computeDestinationForce(destinationForce); + computeWallsForce(wallsForce, dt); + } + + force[0] = destinationForce[0] + agentsForce[0] + wallsForce[0]; + force[1] = destinationForce[1] + agentsForce[1] + wallsForce[1]; + + if (Double.isNaN(force[0]) || Double.isNaN(force[1])) { + Logger.warn("Force is NaN!"); + force[0] = 0; + force[1] = 0; + } + } + + private void updatePosition(double dt) { + double newVX = velocity[0] + dt * force[0]; + double newVY = velocity[1] + dt * force[1]; + double v = Math.hypot(newVX, newVY); + if (v > this.velocityLimit) { + // System.err.println("velocity exceeded velocityLimit"); + v /= this.velocityLimit; + newVX /= v; + newVY /= v; + } + + double x = location[0] + dt * newVX; + double y = location[1] + dt * newVY; + + if (verbose) { + Logger.debug("Updating position for " + this); + Logger.debug("Current position : " + location[0] + ", " + location[1]); + Logger.debug("Current velocity : " + velocity[0] + ", " + velocity[1]); + Logger.debug("Destination forces : " + destinationForce[0] + ", " + destinationForce[1]); + Logger.debug("Agent forces : " + agentsForce[0] + ", " + agentsForce[1]); + Logger.debug("Wall forces : " + wallsForce[0] + ", " + wallsForce[1]); + Logger.debug("Total forces : " + force[0] + ", " + force[1]); + Logger.debug("New position : " + x + ", " + y); + Logger.debug("New velocity : " + newVX + ", " + newVY); + } + if (crossedWall(location[0], location[1], x, y)) { + velocity[0] = 0; + velocity[1] = 0; + return; + } + velocity[0] = newVX; + velocity[1] = newVY; + if (newVX != 0 || newVY != 0) { + double dist = v * dt; + for (WallInfo wall : blockingLines) { + wall.decreaseDistance(dist); + } + setLocation(x, y); + } + } + + private boolean hasLos(WallInfo target, List<WallInfo> blocking) { + Line2D line = target.getLine(); + + for (WallInfo wall : blocking) { + if (wall == target) { + break; + } + + Line2D next = wall.getWall(); + if (target.getClosestPoint().equals(next.getOrigin()) || target.getClosestPoint().equals(next.getEndPoint())) { + continue; + } + + // Here we test if the wall can intersect the line to the target + // in front of the target. This is the case if the following holds: + // |v_target| < |v_wall|/cos(alpha) + // using dot(v_t, v_w) = cos(alpha) * |v_t| * |v_w| + // we get dot(v_t, v_w) < |v_w|^2 + // + // This is strictly true only if the line to the wall and the wall + // are orthogonal, but in our case the angle between those can never + // be + // acute (because they intersect at the closest point), so we never + // prune real intersections here. + double dotp = line.getDirection().dot(wall.getVector()); + if (dotp < wall.getDistance() * wall.getDistance()) { + continue; + } + + if (GeometryTools2D.getSegmentIntersectionPoint(line, next) != null) { + return false; + } + } + return true; + } + + private boolean hasLos(Point2D source, Point2D target, TrafficArea area) { + Line2D line = new Line2D(source, target); + double dist = line.getDirection().getLength(); + + for (WallInfo wall : blockingLines) { + if (wall.getDistance() > dist || wall.getArea() != area) { + break; + } + + Line2D next = wall.getWall(); + if (GeometryTools2D.getSegmentIntersectionPoint(line, next) != null) { + return false; + } + } + return true; + } + + private boolean insideBlockade() { + if (currentArea == null) { + return false; + } + for (TrafficBlockade block : currentArea.getBlockades()) { + if (block.contains(location[0], location[1])) { + return true; + } + } + return false; + } + + private boolean crossedLine(double oldX, double oldY, double newX, double newY, Line2D line) { + Line2D moved = new Line2D(oldX, oldY, newX - oldX, newY - oldY); + return (GeometryTools2D.getSegmentIntersectionPoint(moved, line) != null); + /* + * Vector2D normal = line.getDirection().getNormal().normalised(); + * double dot1 = new Vector2D(oldX - line.getOrigin().getX(), oldY - + * line.getOrigin().getY()).normalised().dot(normal); double dot2 = new + * Vector2D(newX - line.getOrigin().getX(), newY - + * line.getOrigin().getY()).normalised().dot(normal); return (((dot1 < 0 + * && dot2 > 0) || (dot1 > 0 && dot2 < 0)) && GeometryTools2D + * .getSegmentIntersectionPoint(moved, line) != null); + */ + } + + private boolean crossedWall(double oldX, double oldY, double newX, double newY) { + Line2D moved = new Line2D(oldX, oldY, newX - oldX, newY - oldY); + double dist = moved.getDirection().getLength(); + for (WallInfo wall : blockingLines) { + if (wall.getDistance() >= dist) { + break; + } + Line2D test = wall.getWall(); + if (GeometryTools2D.getSegmentIntersectionPoint(moved, test) != null) { + // if (crossedLine(oldX, oldY, newX, newY, test)) { + /* + * Logger.warn(this + " crossed wall"); + * Logger.warn("Old location: " + oldX + ", " + oldY); + * Logger.warn("New location: " + newX + ", " + newY); + * Logger.warn("Movement line: " + moved); + * Logger.warn("Wall : " + test); + * Logger.warn("Crossed at " + + * GeometryTools2D.getSegmentIntersectionPoint(moved, test)); + */ + return true; + } + } + return false; + } + + private void findBlockingLines() { + blockingLines.clear(); + if (currentArea != null) { + for (Line2D line : currentArea.getAllBlockingLines()) { + blockingLines.add(new WallInfo(line, currentArea)); + } + for (TrafficArea neighbour : manager.getNeighbours(currentArea)) { + for (Line2D line : neighbour.getAllBlockingLines()) { + blockingLines.add(new WallInfo(line, neighbour)); + } + } + } + } + + private void updateWalls(double dt) { + Point2D position = new Point2D(location[0], location[1]); + double crossingCutoff = dt * this.velocityLimit; + double forceCutoff = TrafficConstants.getWallDistanceCutoff(); + double cutoff = Math.max(forceCutoff, crossingCutoff); + // double dist; + + for (WallInfo wall : blockingLines) { + if (wall.getDistance() > cutoff) { + continue; + } + wall.computeClostestPoint(position); + } + + // Hand coded, in-sito insertion sort is much faster than + // Collection.sort() for lists of this size. + for (int i = 1; i < blockingLines.size(); i++) { + WallInfo info = blockingLines.get(i); + for (int j = i; j >= 0; j--) { + if (j == 0) { + blockingLines.remove(i); + blockingLines.add(0, info); + } else if (blockingLines.get(j - 1).getDistance() < info.getDistance()) { + if (j == i) { + break; + } + blockingLines.remove(i); + blockingLines.add(j, info); + break; + } + } + } + } + + private void computeDestinationForce(double[] result) { + double destx = 0; + double desty = 0; + if (currentDestination != null) { + double dx = currentDestination.getX() - location[0]; + double dy = currentDestination.getY() - location[1]; + double dist = Math.hypot(dx, dy); + if (dist == 0) { + dx = 0; + dy = 0; + } else { + dx /= dist; + dy /= dist; + } + final double ddd = 0.001; + if (currentDestination == finalDestination) { + dx = Math.min(velocityLimit, ddd * dist) * dx; + dy = Math.min(velocityLimit, ddd * dist) * dy; + } else { + dx = this.velocityLimit * dx; + dy = this.velocityLimit * dy; + } + + final double sss2 = 0.0002; + destx = sss2 * (dx - velocity[0]); + desty = sss2 * (dy - velocity[1]); + } else { + final double sss = 0.0001; + destx = sss * (-velocity[0]); + desty = sss * (-velocity[1]); + } + result[0] = destx; + result[1] = desty; + if (Double.isNaN(destx)) { + Logger.error("Destination force x is NaN"); + result[0] = 0; + } + if (Double.isNaN(desty)) { + Logger.error("Destination force y is NaN"); + result[1] = 0; + } + if (verbose) { + Logger.debug("Destination force: " + result[0] + ", " + result[1]); + } + } + + private void computeAgentsForce(double[] result) { + result[0] = 0; + result[1] = 0; + if (currentArea == null) { + return; + } + + double xSum = 0; + double ySum = 0; + + double cutoff = TrafficConstants.getAgentDistanceCutoff(); + double a = TrafficConstants.getAgentForceCoefficientA(); + double b = TrafficConstants.getAgentForceCoefficientB(); + double k = TrafficConstants.getAgentForceCoefficientK(); + double forceLimit = TrafficConstants.getAgentForceLimit(); + + Collection<TrafficAgent> nearby = manager.getNearbyAgents(this); + for (TrafficAgent agent : nearby) { + if (!agent.isMobile()) { + continue; + } + double dx = agent.getX() - location[0]; + double dy = agent.getY() - location[1]; + + if (Math.abs(dx) > cutoff) { + continue; + } + if (Math.abs(dy) > cutoff) { + continue; + } + + double totalRadius = radius + agent.getRadius(); + double distanceSquared = dx * dx + dy * dy; + + if (distanceSquared == 0) { + xSum = TrafficConstants.getColocatedAgentNudge(); + ySum = TrafficConstants.getColocatedAgentNudge(); + colocated = true; + Logger.debug(this + " is co-located with " + agent); + break; + } + double distance = Math.sqrt(distanceSquared); + double dxN = dx / distance; + double dyN = dy / distance; + double negativeSeparation = totalRadius - distance; + double tmp = -a * Math.exp(negativeSeparation * b); + if (Double.isInfinite(tmp)) { + Logger.warn("calculateAgentsForce(): A result of exp is infinite: exp(" + (negativeSeparation * b) + ")"); + } else { + xSum += tmp * dxN; + ySum += tmp * dyN; + } + if (negativeSeparation > 0) { + // Agents overlap + xSum += -k * negativeSeparation * dxN; + ySum += -k * negativeSeparation * dyN; + } + } + + double forceSum = Math.hypot(xSum, ySum); + if (forceSum > forceLimit) { + forceSum /= forceLimit; + xSum /= forceSum; + ySum /= forceSum; + } + if (Double.isNaN(xSum)) { + Logger.warn("computeAgentsForce: Sum of X force is NaN"); + xSum = 0; + } + if (Double.isNaN(ySum)) { + Logger.warn("computeAgentsForce: Sum of Y force is NaN"); + ySum = 0; + } + result[0] = xSum; + result[1] = ySum; + } + + private void computeWallsForce(double[] result, double dt) { + double xSum = 0; + double ySum = 0; + if (currentArea != null) { + double r = getRadius(); + double dist; + double cutoff = TrafficConstants.getWallDistanceCutoff(); + // double a = TrafficConstants.getWallForceCoefficientA(); + double b = TrafficConstants.getWallForceCoefficientB(); + Point2D position = new Point2D(location[0], location[1]); + if (verbose) { + Logger.debug("Computing wall forces for " + this); + Logger.debug("Position: " + position); + } + + for (WallInfo wall : blockingLines) { + if (wall.getDistance() > cutoff) { + break; + } + Line2D line = wall.getWall(); + dist = wall.getDistance(); + Point2D closest = wall.getClosestPoint(); + + if (verbose) { + Logger.debug("Next wall: " + line); + } + // Point2D closest = + // GeometryTools2D.getClosestPointOnSegment(line, position); + if (verbose) { + Logger.debug("Closest point: " + closest); + } + // dist = GeometryTools2D.getDistance(closest, position); + // if (dist > cutoff) { + // if (verbose) { + // Logger.debug("Distance to wall: " + dist + + // " greater than cutoff " + cutoff); + // } + // continue; + // } + if (!hasLos(wall, blockingLines)) { + // No line-of-sight to closest point + if (verbose) { + Logger.debug("No line of sight"); + } + continue; + } + + boolean endPoint = false; + if (closest == line.getOrigin() || closest == line.getEndPoint()) { + endPoint = true; + } + // Two forces apply: + // If the agent is moving towards this wall then apply a force + // to bring the agent to a stop. This force applies when the + // distance is less than the agent radius. + // Also apply a force that decreases exponentially with distance + // no matter what the agent is doing. + double currentVX = velocity[0]; + double currentVY = velocity[1]; + double currentFX = destinationForce[0] + agentsForce[0]; + double currentFY = destinationForce[1] + agentsForce[1]; + double expectedVX = currentVX + dt * currentFX; + double expectedVY = currentVY + dt * currentFY; + Vector2D expectedVelocity = new Vector2D(expectedVX, expectedVY); + Vector2D wallForceVector = wall.getVector().scale(-1.0 / dist); + double radii = dist / r; + // Compute the stopping force + // Magnitude is the multiple of wallForceVector required to + // bring the agent to a stop. + double magnitude = -expectedVelocity.dot(wallForceVector); + if (magnitude < 0 || radii >= 1) { + magnitude = 0; + // Agent is moving away or far enough away - no stopping + // force required. + } else if (radii < 1) { + double d = Math.exp(-(radii - 1) * b); + if (d < 1) { + d = 0; + } + magnitude *= d; + if (endPoint) { + // Endpoints are counted twice so halve the magnitude + magnitude /= 2; + } + } + Vector2D stopForce = wallForceVector.scale(magnitude / dt); + // Compute the repulsion force + // Decreases exponentially with distance in terms of agent + // radii. + // double factor = a * Math.min(1, Math.exp(-(radii - 1) * b)); + // Vector2D repulsionForce = wallForceVector.scale(factor / dt); + xSum += stopForce.getX(); + ySum += stopForce.getY(); + // xSum += repulsionForce.getX(); + // ySum += repulsionForce.getY(); + if (verbose) { + Logger.debug("Distance to wall : " + dist); + Logger.debug("Distance to wall : " + radii + " radii"); + Logger.debug("Current velocity : " + currentVX + ", " + currentVY); + Logger.debug("Current force : " + currentFX + ", " + currentFY); + Logger.debug("Expected velocity: " + expectedVelocity); + Logger.debug("Wall force : " + wallForceVector); + Logger.debug("Magnitude : " + magnitude); + Logger.debug("Stop force : " + stopForce); + // Logger.debug("Factor : " + factor + " (e^" + + // (-(dist / r) * b) + ")"); + // Logger.debug("Repulsion force : " + repulsionForce); + } + } + } + if (Double.isNaN(xSum) || Double.isNaN(ySum)) { + xSum = 0; + ySum = 0; + } + if (verbose) { + Logger.debug("Total wall force: " + xSum + ", " + ySum); + } + + result[0] = xSum; + result[1] = ySum; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer("TrafficAgent["); + sb.append("id:").append(human.getID()).append(";"); + sb.append("x:").append((int) getX()).append(";"); + sb.append("y:").append((int) getY()).append(";"); + sb.append("]"); + return sb.toString(); + } + + /** + * Get a long version of the toString method. + * + * @return A long description of this agent. + */ + public String toLongString() { + StringBuffer sb = new StringBuffer("TrafficAgent["); + sb.append("id: ").append(human.getID()).append(";"); + sb.append(" x: ").append(location[0]).append(";"); + sb.append(" y: ").append(location[1]).append(";"); + sb.append(" current area: ").append(currentArea).append(";"); + sb.append(" current destination: ").append(currentDestination).append(";"); + sb.append(" final destination: ").append(finalDestination).append(";"); + sb.append("]"); + return sb.toString(); + } + + @Override + public int hashCode() { + return human.getID().hashCode(); + } +} diff --git a/modules/traffic3/src/traffic3/objects/TrafficArea.java b/modules/traffic3/src/traffic3/objects/TrafficArea.java new file mode 100644 index 0000000000000000000000000000000000000000..357e509903e006e2271b9a9ca77a0d5cefb14f47 --- /dev/null +++ b/modules/traffic3/src/traffic3/objects/TrafficArea.java @@ -0,0 +1,489 @@ +package traffic3.objects; + +import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Collection; +import java.util.Collections; +import java.awt.Color; +import java.awt.Point; +import java.awt.geom.Rectangle2D; + +import org.apache.log4j.NDC; + +//import rescuecore2.log.Logger; + +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.gui.ShapeDebugFrame; + +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Edge; +import traffic3.simulator.TrafficSimulator; + +import com.infomatiq.jsi.Rectangle; + +/** + * This class wraps an Area object with some extra information. + */ +public class TrafficArea { + // private List<TrafficAreaListener> areaListenerList = new + // ArrayList<TrafficAreaListener>(); + private Collection<TrafficAgent> agents; + private Collection<TrafficBlockade> blocks; + + private List<Line2D> blockingLines; + private List<Line2D> blockadeLines; + private List<Line2D> allBlockingLines; + private List<Line2D> areaLines; + + private Area area; + private Rectangle bounds; + private Vector2D baseVector; + private ArrayList<Line2D> openLines; + private int[][] graph; + + /** + * Construct a TrafficArea. + * + * @param area + * The Area to wrap. + */ + public TrafficArea(final Area area) { + this.area = area; + agents = new HashSet<TrafficAgent>(); + blocks = new HashSet<TrafficBlockade>(); + blockingLines = null; + blockadeLines = null; + allBlockingLines = null; + areaLines = null; + Rectangle2D r = area.getShape().getBounds2D(); + bounds = new Rectangle((float) r.getMinX(), (float) r.getMinY(), (float) r.getMaxX(), (float) r.getMaxY()); + /* + * area.addEntityListener(new EntityListener() { + * + * @Override public void propertyChanged(Entity e, Property p, Object + * oldValue, Object newValue) { if (p == area.getBlockadesProperty()) { + * blockadeLines = null; allBlockingLines = null; } if (p == + * area.getEdgesProperty()) { blockingLines = null; allBlockingLines = + * null; } } }); + */ + } + + /** + * Get the wrapped area. + * + * @return The wrapped area. + */ + public Area getArea() { + return area; + } + + /** + * Get the bounding rectangle. + * + * @return The bounding rectangle. + */ + public Rectangle getBounds() { + return bounds; + } + + /** + * Get all lines around this area that block movement. + * + * @return All area lines that block movement. + */ + public List<Line2D> getBlockingLines() { + if (blockingLines == null) { + blockingLines = new ArrayList<Line2D>(); + for (Edge edge : area.getEdges()) { + if (!edge.isPassable()) { + blockingLines.add(edge.getLine()); + } + } + } + return Collections.unmodifiableList(blockingLines); + } + + public List<Line2D> getAreaLines() { + if (areaLines == null) { + areaLines = new ArrayList<Line2D>(); + for (Edge edge : area.getEdges()) { + areaLines.add(edge.getLine()); + } + } + return Collections.unmodifiableList(areaLines); + } + + /** + * Get the lines that describe blockades in this area. + * + * @return All blockade lines. + */ + public List<Line2D> getBlockadeLines() { + if (blockadeLines == null) { + blockadeLines = new ArrayList<Line2D>(); + for (TrafficBlockade block : blocks) { + blockadeLines.addAll(block.getLines()); + } + } + return Collections.unmodifiableList(blockadeLines); + } + + /** + * Get all lines that block movement. This includes impassable edges of the + * area and all blockade lines. + * + * @return All movement-blocking lines. + */ + public List<Line2D> getAllBlockingLines() { + if (allBlockingLines == null) { + allBlockingLines = new ArrayList<Line2D>(); + allBlockingLines.addAll(getBlockingLines()); + allBlockingLines.addAll(getBlockadeLines()); + } + return Collections.unmodifiableList(allBlockingLines); + } + + /** + * Find out whether this area contains a point (x, y). + * + * @param x + * The X coordinate to test. + * @param y + * The Y coordinate to test. + * @return True if and only if this area contains the specified point. + */ + public boolean contains(double x, double y) { + return area.getShape().contains(x, y); + } + + /** + * Add an agent to this area. + * + * @param agent + * The agent to add. + */ + public void addAgent(TrafficAgent agent) { + agents.add(agent); + } + + /** + * Remove an agent from this area. + * + * @param agent + * The agent to remove. + */ + public void removeAgent(TrafficAgent agent) { + agents.remove(agent); + } + + /** + * Get all agents in this area. + * + * @return All agents inside this area. + */ + public Collection<TrafficAgent> getAgents() { + return Collections.unmodifiableCollection(agents); + } + + /** + * Add a TrafficBlockade. + * + * @param block + * The blockade to add. + */ + public void addBlockade(TrafficBlockade block) { + blocks.add(block); + clearBlockadeCache(); + } + + /** + * Remove a TrafficBlockade. + * + * @param block + * The blockade to remove. + */ + public void removeBlockade(TrafficBlockade block) { + blocks.remove(block); + clearBlockadeCache(); + } + + /** + * Clear any cached blockade information. + */ + public void clearBlockadeCache() { + blockadeLines = null; + allBlockingLines = null; + openLines = null; + graph=null; + } + + /** + * Get all TrafficBlockades inside this area. + * + * @return All TrafficBlockades in this area. + */ + public Collection<TrafficBlockade> getBlockades() { + return Collections.unmodifiableCollection(blocks); + } + + @Override + public String toString() { + return "TrafficArea (" + area + ")"; + } + public int getNearestLineIndex(Point2D point){ + List<Line2D> oLines = getOpenLines(); + double minDst=Integer.MAX_VALUE; + int minIndex=-1; + /*FOR:*/for (int i = 0; i < oLines.size(); i++) { +// Line2D line = new Line2D(point,getMidPoint(oLines.get(i).getOrigin(), oLines.get(i).getEndPoint())); +// for (Line2D is :getAllBlockingLines()) { +// if (GeometryTools2D.getSegmentIntersectionPoint(line, is) != null) { +// continue FOR; +// } +// } +// for (int k = 0; k < oLines.size(); k++) { +// if(k==i) +// continue; +// if (GeometryTools2D.getSegmentIntersectionPoint(line, oLines.get(k)) == null) { +// continue FOR; +// } +// } + Point2D nearestPoint = GeometryTools2D.getClosestPointOnSegment(oLines.get(i), point); + double dst = GeometryTools2D.getDistance(point, nearestPoint); + if(dst<minDst){ + minDst=dst; + minIndex=i; + } +// return i; + } + return minIndex; + } + + public int[][] getGraph() { + if (graph == null) { + List<Line2D> oLines = getOpenLines(); + graph = new int[oLines.size()][oLines.size()]; + for (int i = 0; i < graph.length; i++) { + FOR: for (int j = 0; j < graph.length; j++) { + Line2D line = new Line2D(getMidPoint(oLines.get(i).getOrigin(), oLines.get(i).getEndPoint()), getMidPoint(oLines.get(j).getOrigin(), oLines.get(j) + .getEndPoint())); + for (Line2D is : getAllBlockingLines()) { + if (GeometryTools2D.getSegmentIntersectionPoint(line, is) != null) { + graph[i][j] = 100000; + continue FOR; + } + } + for (int k = 0; k < oLines.size(); k++) { + if(k==i||k==j) + continue; + if (GeometryTools2D.getSegmentIntersectionPoint(line, oLines.get(k)) != null) { + graph[i][j] = Integer.MAX_VALUE; + continue FOR; + } + } + graph[i][j] = 1; + } + } + } + return graph; + } + + private Point2D getMidPoint(Point2D p1, Point2D p2) { + return new Point2D((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2); + } + + public List<Line2D> getOpenLines() { + if (openLines == null) { + openLines = new ArrayList<Line2D>(); + HashSet<Point2D> checkedPoint = new HashSet<Point2D>(); + for (Line2D line : getBlockadeLines()) { + if (!checkedPoint.contains(line.getOrigin())) + createLine(line.getOrigin(), openLines); + if (!checkedPoint.contains(line.getEndPoint())) + createLine(line.getEndPoint(), openLines); + checkedPoint.add(line.getOrigin()); + checkedPoint.add(line.getEndPoint()); + } +// createPassableEdgesLine(openLines); +// TrafficSimulator.debug.show("Full Lines", new ShapeDebugFrame.AWTShapeInfo(getArea().getShape(), getArea() + "", Color.blue, false), +// new ShapeDebugFrame.Line2DShapeInfo(openLines, "openLines", Color.green, false, true) +// +// ); + } + return Collections.unmodifiableList(openLines); + } + + private void createPassableEdgesLine(List<Line2D> openLines) { + for (Edge edge : getArea().getEdges()) { + if (edge.isPassable()) { + List<Line2D> edgeLines = new ArrayList<Line2D>(); + edgeLines.add(edge.getLine()); + // Line2D edgeLine =new Line2D(edge.getStart(),edge.getEnd()); + // ArrayList<Line2D> subtractLines=new ArrayList<Line2D>(); + // subtractLines.add(edgeLine); + for (Line2D line : getBlockadeLines()) { + // double distance = getDistance(edgeLine,line); + // if(distance<1){ + List<Line2D> old = edgeLines; + edgeLines = minus(edgeLines, line); + + // } +// TrafficSimulator.debug.show("d", new ShapeDebugFrame.Line2DShapeInfo(edgeLines, "result", Color.green, true, true), new ShapeDebugFrame.Line2DShapeInfo(old, +// "edgeLines", Color.black, false, false), new ShapeDebugFrame.Line2DShapeInfo(line, "blockadeLine", Color.white, false, true)); + } + openLines.addAll(edgeLines); + } + } + } + + private static List<Line2D> minus(List<Line2D> edgeLines, Line2D line) { + List<Line2D> result = new ArrayList<Line2D>(); + for (Line2D edgeLine : edgeLines) { + // System.out.println("Edge===="); + // System.out.println(edgeLine); + // System.out.println(line); + Line2D clone = new Line2D(edgeLine.getOrigin(), edgeLine.getEndPoint()); + boolean lineContaintEdgeOrigin = GeometryTools2D.contains(line, edgeLine.getOrigin()); + boolean lineContaintEdgeEnd = GeometryTools2D.contains(line, edgeLine.getEndPoint()); + boolean edgeContaintLineOrigin = GeometryTools2D.contains(edgeLine, line.getOrigin()); + boolean edgeContaintLineEnd = GeometryTools2D.contains(edgeLine, line.getEndPoint()); + if (lineContaintEdgeOrigin & lineContaintEdgeEnd) + continue; + + if (edgeContaintLineOrigin & edgeContaintLineEnd) { + Line2D firstLine; + Line2D secondLine; + double distanceoo = GeometryTools2D.getDistance(edgeLine.getOrigin(), line.getOrigin()); + double distanceoe = GeometryTools2D.getDistance(edgeLine.getOrigin(), line.getEndPoint()); + double distanceeo = GeometryTools2D.getDistance(edgeLine.getEndPoint(), line.getOrigin()); + double distanceee = GeometryTools2D.getDistance(edgeLine.getEndPoint(), line.getEndPoint()); + if (distanceoo < distanceoe) { + firstLine = new Line2D(edgeLine.getOrigin(), line.getOrigin()); + secondLine = new Line2D(line.getEndPoint(), edgeLine.getEndPoint()); + } else { + firstLine = new Line2D(edgeLine.getOrigin(), line.getEndPoint()); + secondLine = new Line2D(line.getOrigin(), edgeLine.getEndPoint()); + } + // System.out.println(distanceoo); + // System.out.println(distanceoe); + // System.out.println(distanceeo); + // System.out.println(distanceee); + if (!GeometryTools2D.nearlyZero(distanceoo) && !GeometryTools2D.nearlyZero(distanceoe)) + result.add(firstLine); + if (!GeometryTools2D.nearlyZero(distanceeo) && !GeometryTools2D.nearlyZero(distanceee)) + result.add(secondLine); + continue; + } + if (lineContaintEdgeOrigin) { + if (edgeContaintLineOrigin) + clone.setOrigin(line.getOrigin()); + else if (edgeContaintLineEnd) + clone.setOrigin(line.getEndPoint()); + else + System.err.println("why?"); + } + if (lineContaintEdgeEnd) { + if (edgeContaintLineOrigin) + clone.setEnd(line.getOrigin()); + else if (edgeContaintLineEnd) + clone.setEnd(line.getEndPoint()); + else + System.err.println("why?2"); + } + + result.add(clone); + } + return result; + } + + private double getDistance(Line2D e1, Point2D p) { + Point2D p1 = GeometryTools2D.getClosestPointOnSegment(e1, p); + return GeometryTools2D.getDistance(p1, p); + } + + private double getDistance(Line2D e1, Line2D e2) { + double d1 = getDistance(e1, e2.getOrigin()); + double d2 = getDistance(e1, e2.getEndPoint()); + double d4 = getDistance(e2, e1.getOrigin()); + double d5 = getDistance(e2, e1.getEndPoint()); + d1 = Math.min(d1, d2); + d2 = Math.min(d5, d4); + return Math.min(d1, d2); + } + + private Vector2D getBaseVector() { + if (baseVector == null) { + baseVector = new Vector2D(10, 10); + } + return baseVector; + } + + private List<Line2D> getAllBlockingAndAreaLine() { + List<Line2D> lines = new ArrayList<Line2D>(getBlockadeLines()); + lines.addAll(getAreaLines()); + return lines; + } + + private void createLine(Point2D origin, List<Line2D> openLines) { + + Line2D newLineToUp = new Line2D(origin, getBaseVector().scale(1000)); + Line2D newLineToDown = new Line2D(origin, getBaseVector().scale(-1000)); +// TrafficSimulator.debug.show("Init", new ShapeDebugFrame.AWTShapeInfo(getArea().getShape(), getArea() + "", Color.blue, false), new ShapeDebugFrame.Line2DShapeInfo( +// newLineToUp, "lineToUp", Color.red, false, true), new ShapeDebugFrame.Line2DShapeInfo(newLineToDown, "lineTodown", Color.green, false, true)); + + for (Line2D line : getAllBlockingAndAreaLine()) { + if (line.getOrigin().equals(origin) || line.getEndPoint().equals(origin)) + continue; + double distance1 = GeometryTools2D.getDistance(newLineToUp.getOrigin(), newLineToUp.getEndPoint()); + // newLineToUp.getDirection().scale(distance1); + Point2D point1 = GeometryTools2D.getSegmentIntersectionPoint(newLineToUp, line); + if (point1 != null && !GeometryTools2D.nearlyZero(distance1)) + newLineToUp.setEnd(point1); + double distance2 = GeometryTools2D.getDistance(newLineToDown.getOrigin(), newLineToDown.getEndPoint()); + // newLineToDown.senewLineToDown.getDirection().scale(distance2); + Point2D point2 = GeometryTools2D.getSegmentIntersectionPoint(newLineToDown, line); + if (point2 != null && !GeometryTools2D.nearlyZero(distance2)) + newLineToDown.setEnd(point2); +// TrafficSimulator.debug.show( +// "Checking", +// // new ShapeDebugFrame.AWTShapeInfo(getArea().getShape(), +// // getArea()+"", Color.blue, false), +// new ShapeDebugFrame.Line2DShapeInfo(line, "checkline", Color.MAGENTA, false, true), new ShapeDebugFrame.Line2DShapeInfo(newLineToUp, "lineToUp " + distance1, +// Color.red, false, true), new ShapeDebugFrame.Line2DShapeInfo(newLineToDown, "lineTodown " + distance2, Color.green, false, true), +// new ShapeDebugFrame.Point2DShapeInfo(point1, "Point1" + point1, Color.red, true), new ShapeDebugFrame.Point2DShapeInfo(point2, "Point2" + point2, Color.green, +// true)); + } + boolean isValidLineToUp = isValidLine(newLineToUp); + boolean isValidLineToDown = isValidLine(newLineToDown); +// TrafficSimulator.debug.show("Final", +// // new ShapeDebugFrame.AWTShapeInfo(getArea().getShape(), getArea()+"", +// // Color.blue, false), +// new ShapeDebugFrame.Line2DShapeInfo(newLineToUp, "lineToUp:" + isValidLineToUp, Color.red, isValidLineToUp, true), new ShapeDebugFrame.Line2DShapeInfo( +// newLineToDown, "lineTodown:" + isValidLineToDown, Color.green, isValidLineToDown, true)); + if (isValidLineToUp) + openLines.add(newLineToUp); + if (isValidLineToDown) + openLines.add(newLineToDown); + } + + private boolean isValidLine(Line2D line) { + Point midPoint = new Point((int) (line.getOrigin().getX() + line.getEndPoint().getX()) / 2, (int) (line.getOrigin().getY() + line.getEndPoint().getY()) / 2); + if (!getArea().getShape().contains(midPoint)) + return false; + for (TrafficBlockade blockade : getBlockades()) { + if (blockade.getBlockade().getShape().contains(midPoint)) + return false; + } + return true; + + } + + @Override + public int hashCode() { + return area.getID().hashCode(); + } +} diff --git a/modules/traffic3/src/traffic3/objects/TrafficBlockade.java b/modules/traffic3/src/traffic3/objects/TrafficBlockade.java new file mode 100644 index 0000000000000000000000000000000000000000..3a02f1990091f313cb54dea021cf0d5093055808 --- /dev/null +++ b/modules/traffic3/src/traffic3/objects/TrafficBlockade.java @@ -0,0 +1,94 @@ +package traffic3.objects; + +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + +//import rescuecore2.log.Logger; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.Property; +import rescuecore2.worldmodel.EntityListener; + +import rescuecore2.standard.entities.Blockade; + +/** + This class wraps a Blockade object with some extra information. +*/ +public class TrafficBlockade { + private Blockade blockade; + private TrafficArea area; + private List<Line2D> lines = new ArrayList<Line2D>(); + + /** + Construct a TrafficBlockade object. + @param blockade The wrapped blockade. + @param area The area containing this blockade. + */ + public TrafficBlockade(final Blockade blockade, TrafficArea area) { + this.blockade = blockade; + this.area = area; + lines = null; + blockade.addEntityListener(new EntityListener() { + @Override + public void propertyChanged(Entity e, Property p, Object oldValue, Object newValue) { + if (p == blockade.getApexesProperty()) { + lines = null; + } + } + }); + } + + /** + Get the lines that make up the outline of this blockade. + @return A list of lines. + */ + public List<Line2D> getLines() { + if (lines == null) { + lines = new ArrayList<Line2D>(); + int[] apexes = blockade.getApexes(); + // CHECKSTYLE:OFF:MagicNumber + for (int i = 0; i < apexes.length - 3; i += 2) { + Point2D first = new Point2D(apexes[i], apexes[i + 1]); + Point2D second = new Point2D(apexes[i + 2], apexes[i + 3]); + lines.add(new Line2D(first, second)); + } + // CHECKSTYLE:ON:MagicNumber + // Close the shape + lines.add(new Line2D(new Point2D(apexes[apexes.length - 2], apexes[apexes.length - 1]), new Point2D(apexes[0], apexes[1]))); + } + return Collections.unmodifiableList(lines); + } + + /** + Get the wrapped Blockade. + @return The Blockade. + */ + public Blockade getBlockade() { + return blockade; + } + + /** + Get the containing TrafficArea. + @return The TrafficArea. + */ + public TrafficArea getArea() { + return area; + } + + /** + Find out whether this blockade contains a point (x, y). + @param x The X coordinate to test. + @param y The Y coordinate to test. + @return True if and only if this blockade contains the specified point. + */ + public boolean contains(double x, double y) { + return blockade.getShape().contains(x, y); + } + + @Override + public int hashCode() { + return blockade.getID().hashCode(); + } +} diff --git a/modules/traffic3/src/traffic3/simulator/Dijkstra.java b/modules/traffic3/src/traffic3/simulator/Dijkstra.java new file mode 100644 index 0000000000000000000000000000000000000000..85437760a78dbe6c8f56b902eda6443ffc976fd4 --- /dev/null +++ b/modules/traffic3/src/traffic3/simulator/Dijkstra.java @@ -0,0 +1,115 @@ +package traffic3.simulator; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * Run gets a Graph Object and Runs The Dijkstra From single and Multiple sources .. with methods getPathArray() & getCost You can Get the Results of The Dijkstra !!!!!!!!!IMPORTANT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! you Can use the Dijkstra object multiple Times without initializing it ... But the First time or when the Graph is changed(number of nodes are changed) you shoould call setGraphSize() + */ +public class Dijkstra { + + private int parent[]; + private long minCost[]; + private int mark[]; + private int numberOfVertex, marker = 1; + + public Dijkstra(int n) { // number of Graph Nodes + setGraphSize(n); + } + + public Dijkstra() { + + } + + /** + * Use This Method only if the Graph Size has Changed ... or its the First Time you need to Use this ... + */ + public void setGraphSize(int n) { + parent = new int[n + 1]; + mark = new int[n + 1]; + minCost = new long[n + 1]; + this.numberOfVertex = n; + } + + /** + * Run multi Src Dijkstra + */ + public void Run(int[][]graph1, int src) throws Exception { + marker++; + for (int i = 0; i < numberOfVertex; ++i) { + minCost[i] = Long.MAX_VALUE/2; + } + for(int i=0;i<parent.length;i++){ + parent[i] = -1; + } + PriorityQueue<Integer> PQ = new PriorityQueue<Integer>(100, new Cmp()); + parent[src] = -1; + minCost[src] = 0; + PQ.add(src); + while (PQ.size() != 0) { + int node = (PQ.poll()); + if (mark[node] == marker) + continue; + else + mark[node] = marker; + for (int i = 0; i < graph1.length; i++) { + if(graph1[node][i]>100000) + continue; + if(node==i) + continue; + + int childIndex = i; + if (mark[childIndex] == marker) + continue; + int w = graph1[node][i]; + if (w <= 0 || minCost[node] + w<0) + throw new Exception("Negetive Cost"); + + if (minCost[childIndex] > minCost[node] + w) { + minCost[childIndex] = minCost[node] + w; + parent[childIndex] = node; + PQ.add(childIndex); + } + } + } + } + + /** + * Get Path From Des to Src ... both Src and Des are included in path + */ + public ArrayList<Integer> getpathArray(int desVertex) { + ArrayList<Integer> ar = new ArrayList<Integer>(); + if (parent[desVertex] ==desVertex) { + System.err.println("How it executed!===>loop in getpath array"); + return ar; + } + if (parent[desVertex] != -1) { + ar = getpathArray(parent[desVertex]); + } + ar.add(desVertex); + return ar; + } + + /** + * Get Cost From Des to Src ... + */ + public long getWeight(int desVertex) { + if(minCost[desVertex]<0) + new Error("Cost is negetive....").printStackTrace(); + return minCost[desVertex]; + } + + private class Cmp implements Comparator<Integer> { + @Override + public int compare(Integer a, Integer b) { + if (minCost[a] > minCost[b]) + return 1; + else if (minCost[a] == minCost[b]) + return 0; + else + return -1; + } + } +} diff --git a/modules/traffic3/src/traffic3/simulator/PathElement.java b/modules/traffic3/src/traffic3/simulator/PathElement.java new file mode 100644 index 0000000000000000000000000000000000000000..b0322afed4ced99f5ca1f84388f2b59bae891beb --- /dev/null +++ b/modules/traffic3/src/traffic3/simulator/PathElement.java @@ -0,0 +1,76 @@ +package traffic3.simulator; + +import rescuecore2.worldmodel.EntityID; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Line2D; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +/** + A container for information about a step along a path. +*/ +public class PathElement { + private EntityID areaID; + private Line2D edgeLine; + private Point2D goalPoint; + private List<Point2D> allPoints; + + /** + Construct a PathElement. + @param areaID The ID of the area this element refers to. + @param edgeLine The line of the edge we're heading for (if any). + @param goalPoint The goal of the path element. + @param wayPoints Zero or more intermediate points that can be used if there is no line-of-sight to the goal. + */ + public PathElement(EntityID areaID, Line2D edgeLine, Point2D goalPoint, Point2D... wayPoints) { + this.areaID = areaID; + this.edgeLine = edgeLine; + this.goalPoint = goalPoint; + allPoints = new ArrayList<Point2D>(Arrays.asList(wayPoints)); + allPoints.add(goalPoint); + Collections.reverse(allPoints); + } + + @Override + public String toString() { + return "Move to area " + areaID + ": " + allPoints; + } + + /** + Get the goal point. + @return The goal point. + */ + public Point2D getGoal() { + return goalPoint; + } + + /** + Get the target edge line, if any. + @return The target edge line or null. + */ + public Line2D getEdgeLine() { + return edgeLine; + } + + /** + Get the list of waypoints in preferred order. + @return The waypoints. + */ + public List<Point2D> getWaypoints() { + return Collections.unmodifiableList(allPoints); + } + + /** + Remove a waypoint. + @param p The waypoint to remove. + */ + public void removeWaypoint(Point2D p) { + allPoints.remove(p); + } + public EntityID getAreaID() { + return areaID; + } +} \ No newline at end of file diff --git a/modules/traffic3/src/traffic3/simulator/TrafficConstants.java b/modules/traffic3/src/traffic3/simulator/TrafficConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..e064035ea7f6be6b170521dccd2986dc2faf9686 --- /dev/null +++ b/modules/traffic3/src/traffic3/simulator/TrafficConstants.java @@ -0,0 +1,127 @@ +package traffic3.simulator; + +import org.uncommons.maths.random.ContinuousUniformGenerator; +import org.uncommons.maths.number.NumberGenerator; + +import rescuecore2.config.Config; + +/** + A bunch of useful constants for the traffic simulator. +*/ +public final class TrafficConstants { + // Agent force constants + private static double NUDGE_MAGNITUDE = 0.001; + private static double AGENT_FORCE_COEFFICIENT_A = 0.0001; + private static double AGENT_FORCE_COEFFICIENT_B = 0.001; + private static double AGENT_FORCE_COEFFICIENT_K = 0.00001; + private static double AGENT_DISTANCE_CUTOFF = 10000; + private static double AGENT_FORCE_LIMIT = 0.0001; + private static NumberGenerator<Double> nudge; + + // Wall force constants + private static double WALL_DISTANCE_CUTOFF = 2000; + private static double WALL_FORCE_COEFFICIENT_A = 0.01; + private static double WALL_FORCE_COEFFICIENT_B = 0.7; + // private static final double WALL_FORCE_COEFFICIENT_K = 0.00001; + + private TrafficConstants() {} + + /** + Initialise constants from a configuration file. + @param config The Config to read. + */ + static void init(Config config) { + AGENT_FORCE_COEFFICIENT_A=config.getFloatValue("traffic3.agent.force.coefficient.A",AGENT_FORCE_COEFFICIENT_A); + AGENT_FORCE_COEFFICIENT_B=config.getFloatValue("traffic3.agent.force.coefficient.B",AGENT_FORCE_COEFFICIENT_B); + AGENT_FORCE_COEFFICIENT_K=config.getFloatValue("traffic3.agent.force.coefficient.K",AGENT_FORCE_COEFFICIENT_K); + AGENT_DISTANCE_CUTOFF=config.getFloatValue("traffic3.agent.distance.cutoff",AGENT_DISTANCE_CUTOFF); + AGENT_FORCE_LIMIT=config.getFloatValue("traffic3.agent.force.limit",AGENT_FORCE_LIMIT); + WALL_DISTANCE_CUTOFF=config.getFloatValue("traffic3.wall.distance.cutoff",WALL_DISTANCE_CUTOFF); + WALL_FORCE_COEFFICIENT_A=config.getFloatValue("traffic3.wall.force.coefficient.A",WALL_FORCE_COEFFICIENT_A); + WALL_FORCE_COEFFICIENT_B=config.getFloatValue("traffic3.wall.force.coefficient.B",WALL_FORCE_COEFFICIENT_B); + NUDGE_MAGNITUDE=config.getFloatValue("traffic3.nudge-magnitude",NUDGE_MAGNITUDE); + nudge = new ContinuousUniformGenerator(-NUDGE_MAGNITUDE, NUDGE_MAGNITUDE, config.getRandom()); + } + + /** + Get a (randomised) nudge for agents that are co-located. + @return A random nudge force. + */ + public static double getColocatedAgentNudge() { + return nudge.nextValue(); + } + + /** + Get the maximum distance at which agents affect each other. + @return The agent force distance cutoff. + */ + public static double getAgentDistanceCutoff() { + return AGENT_DISTANCE_CUTOFF; + } + + /** + Get the agent force function coefficient "A". + @return The agent force function coefficient "A". + */ + public static double getAgentForceCoefficientA() { + return AGENT_FORCE_COEFFICIENT_A; + } + + /** + Get the agent force function coefficient "B". + @return The agent force function coefficient "B". + */ + public static double getAgentForceCoefficientB() { + return AGENT_FORCE_COEFFICIENT_B; + } + + /** + Get the agent force function coefficient "K". + @return The agent force function coefficient "K". + */ + public static double getAgentForceCoefficientK() { + return AGENT_FORCE_COEFFICIENT_K; + } + + /** + Get the maximum total agent force. + @return The maximum total agent force. + */ + public static double getAgentForceLimit() { + return AGENT_FORCE_LIMIT; + } + + /** + Get the maximum distance at which walls affect agents. + @return The wall force distance cutoff. + */ + public static double getWallDistanceCutoff() { + return WALL_DISTANCE_CUTOFF; + } + + /** + Get the wall force function coefficient "A". + @return The wall force function coefficient "A". + */ + public static double getWallForceCoefficientA() { + return WALL_FORCE_COEFFICIENT_A; + } + + /** + Get the wall force function coefficient "B". + @return The wall force function coefficient "B". + */ + public static double getWallForceCoefficientB() { + return WALL_FORCE_COEFFICIENT_B; + } + + /** + Get the wall force function coefficient "K". + @return The wall force function coefficient "K". + */ + /* + public static double getWallForceCoefficientK() { + return WALL_FORCE_COEFFICIENT_K; + } + */ +} \ No newline at end of file diff --git a/modules/traffic3/src/traffic3/simulator/TrafficSimulator.java b/modules/traffic3/src/traffic3/simulator/TrafficSimulator.java new file mode 100644 index 0000000000000000000000000000000000000000..591f3680f66e30cdd099f397175a70132828f65a --- /dev/null +++ b/modules/traffic3/src/traffic3/simulator/TrafficSimulator.java @@ -0,0 +1,804 @@ +package traffic3.simulator; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import javax.swing.JComponent; + +import org.uncommons.maths.number.NumberGenerator; +import org.uncommons.maths.random.GaussianGenerator; + +import rescuecore2.GUIComponent; +import rescuecore2.log.Logger; +import rescuecore2.messages.Command; +import rescuecore2.messages.control.KSCommands; +import rescuecore2.messages.control.KSUpdate; +import rescuecore2.misc.geometry.GeometryTools2D; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.misc.geometry.Vector2D; +import rescuecore2.misc.gui.ShapeDebugFrame; +import rescuecore2.misc.gui.ShapeDebugFrame.Line2DShapeInfo; +import rescuecore2.standard.components.StandardSimulator; +import rescuecore2.standard.entities.AmbulanceTeam; +import rescuecore2.standard.entities.Area; +import rescuecore2.standard.entities.Blockade; +import rescuecore2.standard.entities.Civilian; +import rescuecore2.standard.entities.Edge; +import rescuecore2.standard.entities.FireBrigade; +import rescuecore2.standard.entities.Human; +import rescuecore2.standard.entities.PoliceForce; +import rescuecore2.standard.entities.Refuge; +import rescuecore2.standard.entities.Road; +import rescuecore2.standard.entities.StandardEntity; +import rescuecore2.standard.entities.StandardEntityURN; +import rescuecore2.standard.entities.StandardPropertyURN; +import rescuecore2.standard.messages.AKClear; +import rescuecore2.standard.messages.AKClearArea; +import rescuecore2.standard.messages.AKExtinguish; +import rescuecore2.standard.messages.AKLoad; +import rescuecore2.standard.messages.AKMove; +import rescuecore2.standard.messages.AKRescue; +import rescuecore2.standard.messages.AKUnload; +import rescuecore2.worldmodel.ChangeSet; +import rescuecore2.worldmodel.Entity; +import rescuecore2.worldmodel.EntityID; +import rescuecore2.worldmodel.WorldModel; +import rescuecore2.worldmodel.WorldModelListener; +import rescuecore2.worldmodel.properties.EntityRefListProperty; +import rescuecore2.worldmodel.properties.EntityRefProperty; +import rescuecore2.worldmodel.properties.IntProperty; + +import traffic3.manager.TrafficManager; +import traffic3.objects.TrafficAgent; +import traffic3.objects.TrafficArea; +import traffic3.objects.TrafficBlockade; + +/** + * 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; + 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 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 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 AKClearArea) { + handleClear((AKClearArea) 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()); + 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) { + clearCache(u); + super.handleUpdate(u); + } + + private void clearCache(KSUpdate u) { + for (EntityID id : u.getChangeSet().getChangedEntities()) { + StandardEntity entity = model.getEntity(id); + switch (StandardEntityURN.fromInt(u.getChangeSet().getEntityURN(id))) { + case BLOCKADE: + IntProperty blockadeCost = (IntProperty) u.getChangeSet().getChangedProperty(id, + StandardPropertyURN.REPAIR_COST.getURNId()); + EntityRefProperty position = ((EntityRefProperty) u.getChangeSet().getChangedProperty(id, + StandardPropertyURN.POSITION.getURNId())); + if (entity == null || blockadeCost == null + || ((Blockade) entity).getRepairCost() != blockadeCost.getValue()) { + if (position != null) + clearAreaCache(position.getValue()); + else if (entity != null) { + clearAreaCache(((Blockade) entity).getPosition()); + + } + } + break; + case ROAD: + case HYDRANT: + if (entity == null) + continue; + EntityRefListProperty blockades = (EntityRefListProperty) u.getChangeSet().getChangedProperty(id, + StandardPropertyURN.BLOCKADES.getURNId()); + if ((!((Road) entity).isBlockadesDefined()) + || !blockades.getValue().containsAll(((Road) entity).getBlockades()) + || !((Road) entity).getBlockades().containsAll(blockades.getValue())) + clearAreaCache(id); + break; + case BUILDING: + case AMBULANCE_CENTRE: + case FIRE_STATION: + case GAS_STATION: + case POLICE_OFFICE: + case REFUGE: + case AMBULANCE_TEAM: + case POLICE_FORCE: + case CIVILIAN: + case FIRE_BRIGADE: + case WORLD: + default: + break; + } + } + } + + private void clearAreaCache(EntityID entityArea) { + manager.getTrafficArea((Area) model.getEntity(entityArea)).clearBlockadeCache(); + } + + 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>(); + Edge lastEdge = null; + /** + * 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 nextArea = (Area) e; + + steps.addAll(getPathElements2(human, currentArea, lastEdge, nextArea, edge)); + + current = next; + currentArea = nextArea; + lastEdge = edge; + } + 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); + } + + private Collection<? extends PathElement> getPathElements(Human human, Area lastArea, Edge lastEdge, Area nextArea, + Edge nextEdge) { + if (human.getID().getValue() == 204623396) { + System.out.println( + "lastArea=" + lastArea + " lastEdge=" + lastEdge + " nextArea=" + nextArea + " nextEdge=" + nextEdge); + } + ArrayList<PathElement> steps = new ArrayList<PathElement>(); + Point2D edgePoint = getBestPoint(nextEdge, nextArea); + Point2D centrePoint = new Point2D(lastArea.getX(), lastArea.getY()); + if (lastEdge == null) { + Point2D entracePoint = getEntranceOfArea(nextEdge, lastArea); + if (entracePoint != null) { + steps.add(new PathElement(lastArea.getID(), null, entracePoint, centrePoint)); + steps.add(new PathElement(lastArea.getID(), nextEdge.getLine(), edgePoint, entracePoint)); + } else + steps.add(new PathElement(lastArea.getID(), nextEdge.getLine(), edgePoint)); + + } else { + Point2D startEntracePoint = getEntranceOfArea(lastEdge, lastArea); + if (startEntracePoint != null) + steps.add(new PathElement(lastArea.getID(), null, startEntracePoint)); + Point2D entracePoint = getEntranceOfArea(nextEdge, lastArea); + if (entracePoint != null) { + steps.add(new PathElement(lastArea.getID(), nextEdge.getLine(), entracePoint, centrePoint)); + steps.add(new PathElement(lastArea.getID(), nextEdge.getLine(), edgePoint, entracePoint)); + } else { + steps.add(new PathElement(lastArea.getID(), nextEdge.getLine(), edgePoint, centrePoint)); + } + } + + return steps; + } + + private boolean haveImpassibleEdge(Area dest) { + for (Edge edge : dest.getEdges()) { + if (!edge.isPassable()) + return true; + } + return false; + } + + private Point2D getEntranceOfArea(Edge inComingEdge, Area dest) { + + Point2D edgeMid = getBestPoint(inComingEdge, dest); + + Line2D wallLine = inComingEdge.getLine(); + + int distance = 500; + while (distance > 0) { + Vector2D offset = wallLine.getDirection().getNormal().normalised().scale(distance); + Point2D destXY = edgeMid.plus(offset); + if (dest.getShape().contains(destXY.getX(), destXY.getY())) { + return destXY; + } + offset = wallLine.getDirection().getNormal().normalised().scale(-distance); + destXY = edgeMid.plus(offset); + if (dest.getShape().contains(destXY.getX(), destXY.getY())) { + return destXY; + } + distance -= 100; + } + return null; + + } + + private Collection<? extends PathElement> getPathElements2(Human human, Area lastArea, Edge lastEdge, Area nextArea, + Edge nextEdge) { + Collection<? extends PathElement> originalPaths = getPathElements(human, lastArea, lastEdge, nextArea, nextEdge); + if (isOriginalPathOk(originalPaths)) + return originalPaths; + Point2D start; + if (lastEdge == null) + start = new Point2D(human.getX(), human.getY()); + else + start = getBestPoint(lastEdge, lastArea); + Point2D startpoint; + if (lastEdge == null) + startpoint = start; + else + startpoint = getMidPoint(lastEdge.getStart(), lastEdge.getEnd()); + Point2D edgePoint = getBestPoint(nextEdge, nextArea); + Point2D centrePoint = new Point2D(lastArea.getX(), lastArea.getY()); + + List<ShapeDebugFrame.ShapeInfo> resultGraph = new ArrayList<ShapeDebugFrame.ShapeInfo>(); + + resultGraph.add( + new Line2DShapeInfo(new Line2D(startpoint, centrePoint), "path start to center", Color.black, false, true)); + resultGraph.add(new Line2DShapeInfo(new Line2D(centrePoint, getMidPoint(nextEdge.getStart(), nextEdge.getEnd())), + "path center to end", Color.white, false, true)); + + TrafficArea trafficArea = manager.getTrafficArea(lastArea); + int[][] graph = manager.getTrafficArea(lastArea).getGraph(); + List<Line2D> oLines = manager.getTrafficArea(lastArea).getOpenLines(); + List<Line2D> graphline = new ArrayList<>(); + resultGraph.add(new Line2DShapeInfo(oLines, "openLines", Color.green, false, false)); + for (int i = 0; i < graph.length; i++) { + for (int j = 0; j < graph.length; j++) { + if (graph[i][j] > 10000) + continue; + Line2D line = new Line2D(getMidPoint(oLines.get(i).getOrigin(), oLines.get(i).getEndPoint()), + getMidPoint(oLines.get(j).getOrigin(), oLines.get(j).getEndPoint())); + graphline.add(line); + } + } + + int src = trafficArea.getNearestLineIndex(start); + int end = trafficArea.getNearestLineIndex(edgePoint); + + if (src != end && src != -1 && end != -1) { + Dijkstra dijkstra = new Dijkstra(graph.length); + try { + dijkstra.Run(graph, src); + } catch (Exception e) { + e.printStackTrace(); + } + try { + if (dijkstra.getWeight(end) < 1000) { + ArrayList<Integer> path = dijkstra.getpathArray(end); + if (path.size() > 2) { + List<Point2D> points = new ArrayList<Point2D>(); + for (Integer integer : path) { + Point2D point = getMidPoint(oLines.get(integer).getOrigin(), oLines.get(integer).getEndPoint()); + points.add(point); + } + + ArrayList<PathElement> result = new ArrayList<PathElement>(); + result.add(new PathElement(nextArea.getID(), nextEdge.getLine(), start)); + + for (Point2D point : points) + result.add(new PathElement(nextArea.getID(), nextEdge.getLine(), point)); + + result.add(new PathElement(nextArea.getID(), nextEdge.getLine(), edgePoint)); + + return result; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + return originalPaths; + } + + private boolean isOriginalPathOk(Collection<? extends PathElement> originalPaths) { + if (originalPaths.isEmpty()) { + Logger.warn("originalPaths is null"); + return true; + } + + TrafficArea lastArea = null; + ArrayList<PathElement> SameAreaElements = new ArrayList<>(); + for (PathElement pathElement : originalPaths) { + + TrafficArea area = manager.getTrafficArea((Area) model.getEntity(pathElement.getAreaID())); + for (TrafficBlockade block : area.getBlockades()) { + if (block.getBlockade().getShape().contains(pathElement.getGoal().getX(), pathElement.getGoal().getY())) + return false; + } + double minDistance = getMinDistance(area.getAllBlockingLines(), pathElement.getGoal()); + + if (minDistance < TrafficSimulator.RESCUE_AGENT_RADIUS / 2) + return false; + if (lastArea == null || lastArea == area) { + SameAreaElements.add(pathElement); + } else { + if (!checkElements(lastArea, SameAreaElements)) + return false; + SameAreaElements.clear(); + } + lastArea = area; + } + if (!checkElements(lastArea, SameAreaElements)) + return false; + return true; + } + + private boolean checkElements(TrafficArea lastArea, List<PathElement> sameAreaElements) { + if (sameAreaElements.size() <= 1) + return true; + + for (int i = 1; i < sameAreaElements.size(); i++) { + Line2D line2D = new Line2D(sameAreaElements.get(i - 1).getGoal(), sameAreaElements.get(i).getGoal()); + for (Line2D block : lastArea.getAllBlockingLines()) { + if (GeometryTools2D.getSegmentIntersectionPoint(line2D, block) != null) + return false; + } + } + return true; + } + + static Point2D getMidPoint(Point2D p1, Point2D p2) { + return new Point2D((p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2); + } + + private Point2D getTransivit(Point2D base, Point2D p1) { + return new Point2D((base.getX() - (p1.getX() - base.getX())), (base.getY() - (p1.getY() - base.getY()))); + + } + + private Point2D getBestPoint(Edge edge, Area dest) { + return getMidPoint(edge.getStart(), edge.getEnd()); + } + + private double getMinDistance(List<Line2D> blockingLines, Point2D point) { + double min = Integer.MAX_VALUE; + for (Line2D block : blockingLines) { + Point2D tempPoint = GeometryTools2D.getClosestPointOnSegment(block, point); + double tempDistance = GeometryTools2D.getDistance(point, tempPoint); + if (tempDistance < min) + min = tempDistance; + } + return min; + + } + + // 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 handleClear(AKClearArea clear, ChangeSet changes) { + 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 rescuing 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 rescuing"); + } + } + + 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(); + } +} \ No newline at end of file diff --git a/modules/traffic3/src/traffic3/simulator/TrafficSimulatorGUI.java b/modules/traffic3/src/traffic3/simulator/TrafficSimulatorGUI.java new file mode 100644 index 0000000000000000000000000000000000000000..c795cf52374a9e3b2c62f4e9ddf3b14da6c38577 --- /dev/null +++ b/modules/traffic3/src/traffic3/simulator/TrafficSimulatorGUI.java @@ -0,0 +1,450 @@ +package traffic3.simulator; + +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Color; +import java.awt.Insets; +import java.awt.Shape; +import java.awt.Point; +import java.awt.Stroke; +import java.awt.BasicStroke; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.Timer; +import javax.swing.SwingUtilities; +import javax.swing.Box; +import javax.swing.BorderFactory; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; + +import traffic3.objects.TrafficArea; +import traffic3.objects.TrafficAgent; +import traffic3.objects.TrafficBlockade; +import traffic3.manager.TrafficManager; + +import rescuecore2.misc.gui.ScreenTransform; +import rescuecore2.misc.gui.PanZoomListener; +import rescuecore2.misc.gui.ShapeDebugFrame.Line2DShapeInfo; +import rescuecore2.misc.geometry.Line2D; +import rescuecore2.misc.geometry.Point2D; +import rescuecore2.log.Logger; + +import rescuecore2.standard.entities.Edge; + +/** + * A GUI for watching the traffic simulator. + */ +public class TrafficSimulatorGUI extends JPanel { + private static final Color SELECTED_AREA_COLOUR = new Color(0, 0, 255, 128); + private static final Color AREA_OUTLINE_COLOUR = new Color(0, 0, 0); + private static final Color BLOCKADE_OUTLINE_COLOUR = new Color(128, 0, 0); + // private static final Color BLOCKADE_FILL_COLOUR = new Color(255, 0, 0, + // 128); + + private static final Stroke PASSABLE_EDGE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke IMPASSABLE_EDGE_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke SELECTED_AREA_OUTLINE_STROKE = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + private static final Stroke BLOCKADE_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); + + private static final int PATH_NODE_SIZE = 5; + private static final int PATH_SPECIAL_NODE_SIZE = 9; + private static final int TICK_TIME_MS = 10; + + private static final double FORCE_GUI_FACTOR = 1000; + + private TrafficManager manager; + + private volatile boolean waitOnRefresh; + private final Object lock = new Object(); + private CountDownLatch latch; + + private WorldView view; + private JButton cont; + private JCheckBox wait; + private JCheckBox animate; + private Timer timer; + private Box verboseBox; + + /** + * Construct a TrafficSimulatorGUI. + * + * @param manager + * The traffic manager. + */ + public TrafficSimulatorGUI(TrafficManager manager) { + super(new BorderLayout()); + this.manager = manager; + waitOnRefresh = false; + + view = new WorldView(); + cont = new JButton("Continue"); + wait = new JCheckBox("Wait on refresh", waitOnRefresh); + animate = new JCheckBox("Animate", false); + verboseBox = Box.createVerticalBox(); + verboseBox.setBorder(BorderFactory.createTitledBorder("Verbose agents")); + cont.setEnabled(false); + cont.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + synchronized (lock) { + if (latch != null) { + latch.countDown(); + } + } + cont.setEnabled(false); + } + }); + wait.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + waitOnRefresh = wait.isSelected(); + } + }); + animate.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (animate.isSelected()) { + timer.start(); + } else { + timer.stop(); + } + cont.setEnabled(false); + } + }); + + Box buttons = Box.createHorizontalBox(); + buttons.add(wait); + buttons.add(cont); + buttons.add(animate); + + add(view, BorderLayout.CENTER); + add(buttons, BorderLayout.SOUTH); + add(new JScrollPane(verboseBox, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.EAST); + + timer = new Timer(TICK_TIME_MS, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + synchronized (lock) { + if (latch != null) { + latch.countDown(); + } + } + } + }); + } + + /** + * Initialise the GUI. + */ + public void initialise() { + view.initialise(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + verboseBox.removeAll(); + for (TrafficAgent next : manager.getAgents()) { + final TrafficAgent ta = next; + final JCheckBox check = new JCheckBox("Agent " + ta.getHuman(), false); + check.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ta.setVerbose(check.isSelected()); + } + }); + verboseBox.add(check); + } + verboseBox.revalidate(); + } + }); + } + + /** + * Refresh the view and wait for user input if required. + * + * @see #setWaitOnRefresh(boolean). + */ + public void refresh() { + repaint(); + if (waitOnRefresh) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (!timer.isRunning()) { + cont.setEnabled(true); + } + } + }); + synchronized (lock) { + latch = new CountDownLatch(1); + } + try { + latch.await(); + } catch (InterruptedException e) { + Logger.error("Error waiting for continue", e); + } + } + } + + /** + * Set whether to wait for the user before returning from a call to + * {@link #refresh()}. + * + * @param b + * Whether to wait on future calls to refresh. + */ + public void setWaitOnRefresh(boolean b) { + waitOnRefresh = b; + } + + private class WorldView extends JComponent { + private ScreenTransform transform; + private TrafficArea selectedArea; + private TrafficAgent selectedAgent; + private Map<Shape, TrafficArea> areas; + private Map<Shape, TrafficAgent> agents; + + public WorldView() { + } + + public void initialise() { + Rectangle2D bounds = null; + for (TrafficArea area : manager.getAreas()) { + Rectangle2D r = area.getArea().getShape().getBounds2D(); + if (bounds == null) { + bounds = new Rectangle2D.Double(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } else { + Rectangle2D.union(bounds, r, bounds); + } + } + transform = new ScreenTransform(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY()); + new PanZoomListener(this).setScreenTransform(transform); + selectedArea = null; + areas = new HashMap<Shape, TrafficArea>(); + agents = new HashMap<Shape, TrafficAgent>(); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + Point p = e.getPoint(); + selectedArea = null; + selectedAgent = null; + for (Map.Entry<Shape, TrafficArea> next : areas.entrySet()) { + if (next.getKey().contains(p)) { + selectedArea = next.getValue(); + } + } + for (Map.Entry<Shape, TrafficAgent> next : agents.entrySet()) { + if (next.getKey().contains(p)) { + selectedAgent = next.getValue(); + } + } + repaint(); + } + }); + } + + @Override + public void paintComponent(Graphics g) { + Logger.pushLogContext("traffic3"); + try { + int width = getWidth(); + int height = getHeight(); + Insets insets = getInsets(); + width -= insets.left + insets.right; + height -= insets.top + insets.bottom; + transform.rescale(width, height); + Graphics2D copy = (Graphics2D) g.create(insets.left, insets.top, width, height); + drawObjects(copy); + } finally { + Logger.popLogContext(); + } + } + + private void drawObjects(Graphics2D g) { + drawAreas((Graphics2D) g.create()); + drawAgents((Graphics2D) g.create()); + drawBlockades((Graphics2D) g.create()); + } + + private void drawAreas(Graphics2D g) { + areas.clear(); + for (TrafficArea area : manager.getAreas()) { + Path2D shape = new Path2D.Double(); + List<Edge> edges = area.getArea().getEdges(); + Edge e = edges.get(0); + shape.moveTo(transform.xToScreen(e.getStartX()), transform.yToScreen(e.getStartY())); + for (Edge edge : edges) { + shape.lineTo(transform.xToScreen(edge.getEndX()), transform.yToScreen(edge.getEndY())); + } + if (area == selectedArea) { + g.setColor(SELECTED_AREA_COLOUR); + g.fill(shape); + g.setColor(AREA_OUTLINE_COLOUR); + paintEdges(edges, g); + int[][] graph = area.getGraph(); + List<Line2D> oLines = area.getOpenLines(); + g.setColor(Color.green); + paintLines(oLines, g); + g.setColor(Color.yellow); + for (int i = 0; i < graph.length; i++) { + for (int j = 0; j < graph.length; j++) { + if (graph[i][j] > 10000) + continue; + Line2D line = new Line2D(TrafficSimulator.getMidPoint(oLines.get(i).getOrigin(), oLines.get(i).getEndPoint()), + TrafficSimulator.getMidPoint(oLines.get(j).getOrigin(), oLines.get(j).getEndPoint())); + paintLine(line, g); + } + } + } else { + g.setColor(AREA_OUTLINE_COLOUR); + paintEdges(edges, g); + } + areas.put(shape, area); + } + } + + private void paintLines(List<Line2D> lines, Graphics2D g) { + for (Line2D line : lines) { + paintLine(line, g); + } + } + + private void paintLine(Line2D line, Graphics2D g) { + g.drawLine(transform.xToScreen(line.getOrigin().getX()), transform.yToScreen(line.getOrigin().getY()), transform.xToScreen(line.getEndPoint().getX()), + transform.yToScreen(line.getEndPoint().getY())); + } + + private void paintEdges(List<Edge> edges, Graphics2D g) { + for (Edge edge : edges) { + if (edge.isPassable()) { + g.setStroke(PASSABLE_EDGE_STROKE); + } else { + g.setStroke(IMPASSABLE_EDGE_STROKE); + } + Line2D line = edge.getLine(); + paintLine(line, g); + } + } + + private void drawBlockades(Graphics2D g) { + g.setStroke(BLOCKADE_STROKE); + g.setColor(BLOCKADE_OUTLINE_COLOUR); + for (TrafficBlockade b : manager.getBlockades()) { + for (Line2D line : b.getLines()) { + int x1 = transform.xToScreen(line.getOrigin().getX()); + int y1 = transform.yToScreen(line.getOrigin().getY()); + int x2 = transform.xToScreen(line.getEndPoint().getX()); + int y2 = transform.yToScreen(line.getEndPoint().getY()); + g.drawLine(x1, y1, x2, y2); + } + } + } + + private void drawAgents(Graphics2D g) { + for (TrafficAgent agent : manager.getAgents()) { + double agentX = agent.getX(); + double agentY = agent.getY(); + double ellipseX1 = agentX - agent.getRadius(); + double ellipseY1 = agentY - agent.getRadius(); + double ellipseX2 = agentX + agent.getRadius(); + double ellipseY2 = agentY + agent.getRadius(); + double velocityX = agentX + (agent.getVX() * 1000); + double velocityY = agentY + (agent.getVY() * 1000); + double forceX = agentX + (agent.getFX() * FORCE_GUI_FACTOR); + double forceY = agentY + (agent.getFY() * FORCE_GUI_FACTOR); + + int x = transform.xToScreen(agentX); + int y = transform.yToScreen(agentY); + int x1 = transform.xToScreen(ellipseX1); + int y1 = transform.yToScreen(ellipseY1); + int x2 = transform.xToScreen(ellipseX2); + int y2 = transform.yToScreen(ellipseY2); + int vx = transform.xToScreen(velocityX); + int vy = transform.yToScreen(velocityY); + int fx = transform.xToScreen(forceX); + int fy = transform.yToScreen(forceY); + int ellipseWidth = x2 - x1; + int ellipseHeight = y1 - y2; + + /* + * Logger.debug("Agent " + agent); Logger.debug("Position: " + + * agentX + ", " + agentY + " -> " + x + ", " + y); + * Logger.debug("Ellipse bounds: " + ellipseX1 + ", " + + * ellipseY1 + " -> " + ellipseX2 + ", " + ellipseY2); + * Logger.debug(" " + x1 + ", " + y1 + " -> " + x2 + ", " + + * y2); Logger.debug(" Width: " + ellipseWidth + ", height: " + + * ellipseHeight); Logger.debug("Velocity: " + velocityX + ", " + * + velocityY + " -> " + vx + ", " + vy); + * Logger.debug("Force: " + forceX + ", " + forceY + " -> " + fx + * + ", " + fy); + */ + + g.setColor(agent == selectedAgent ? Color.orange : Color.red); + Shape shape = new Ellipse2D.Double(x1, y2, ellipseWidth, ellipseHeight); + g.fill(shape); + agents.put(shape, agent); + + // Draw the path of the selected agent + if (agent == selectedAgent) { + List<PathElement> path = new ArrayList<PathElement>(selectedAgent.getPath()); + if (selectedAgent.getCurrentElement() != null) { + path.add(0, selectedAgent.getCurrentElement()); + } + if (path != null) { + Point2D goal = selectedAgent.getFinalDestination(); + Point2D current = selectedAgent.getCurrentDestination(); + g.setColor(Color.gray); + int lastX = x; + int lastY = y; + for (PathElement next : path) { + List<Point2D> waypoints = new ArrayList<Point2D>(next.getWaypoints()); + Collections.reverse(waypoints); + for (Point2D p : waypoints) { + int nodeX = transform.xToScreen(p.getX()); + int nodeY = transform.yToScreen(p.getY()); + g.fillOval(nodeX - (PATH_NODE_SIZE / 2), nodeY - (PATH_NODE_SIZE / 2), PATH_NODE_SIZE, PATH_NODE_SIZE); + g.drawLine(lastX, lastY, nodeX, nodeY); + lastX = nodeX; + lastY = nodeY; + } + } + if (current != null) { + g.setColor(Color.YELLOW); + int nodeX = transform.xToScreen(current.getX()); + int nodeY = transform.yToScreen(current.getY()); + g.fillOval(nodeX - (PATH_SPECIAL_NODE_SIZE / 2), nodeY - (PATH_SPECIAL_NODE_SIZE / 2), PATH_SPECIAL_NODE_SIZE, PATH_SPECIAL_NODE_SIZE); + g.drawLine(x, y, nodeX, nodeY); + } + if (goal != null) { + g.setColor(Color.WHITE); + int nodeX = transform.xToScreen(goal.getX()); + int nodeY = transform.yToScreen(goal.getY()); + g.fillOval(nodeX - (PATH_SPECIAL_NODE_SIZE / 2), nodeY - (PATH_SPECIAL_NODE_SIZE / 2), PATH_SPECIAL_NODE_SIZE, PATH_SPECIAL_NODE_SIZE); + } + } + } + + // Draw force and velocity lines + g.setColor(Color.blue); + g.drawLine(x, y, vx, vy); + g.setColor(Color.green); + g.drawLine(x, y, fx, fy); + } + } + } +}