From 3f407bfe8988991147b31170f408236c49bb60b9 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 28 Nov 2017 22:58:16 +0100 Subject: [PATCH 01/27] Improvement of the tactic. The utility calculation function is still very much left to do. --- .../drone/tactic/TheoreticalTactic.java | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java index 1dad5eab..be9fda00 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java @@ -9,15 +9,13 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Log4j @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { public static final long ttlLeader = 2; //seconds + private static final double MOVE_GENERATION_DELTA = 1.0; private DroneType droneType; private String idLeader; private HashMap>> teammembers = new HashMap<>(); @@ -254,17 +252,65 @@ private void broadcastHeartbeat() { private void sendInstructions() { for (String teammember : teammembers.keySet()) { - D3Vector targetMoveLocation = targetMoveLocations.get(teammember); - if (targetMoveLocation == null) { - targetMoveLocation = calculateRandomPositionInField(); - targetMoveLocations.put(teammember, targetMoveLocation); + Map, Integer> utilityMap = new HashMap<>(); + + //Generate utility calculations to move in any of the 26 directions + generateUtilityCalculations(utilityMap, teammember, InstructionMessage.InstructionType.MOVE); + //Generate utility for movement towards a different drone and shooting towards a drone + for (Map.Entry enemy : mapOfTheWorld.entrySet()) { + utilityMap.put( + new Tuple<>(InstructionMessage.InstructionType.MOVE, enemy.getValue()), + calculateUtility( + InstructionMessage.InstructionType.MOVE, + enemy.getValue(), + teammembers.get(teammember).getRight() + ) + ); + + if (teammembers.get(teammember).getRight().contains("gun")) { + utilityMap.put( + new Tuple<>(InstructionMessage.InstructionType.SHOOT, enemy.getValue()), + calculateUtility( + InstructionMessage.InstructionType.SHOOT, + enemy.getValue(), + teammembers.get(teammember).getRight() + ) + ); + } } - log.debug("sendInstructions type: MOVE, to " + teammember + "location: " + targetMoveLocation.toString()); - radio.send(new InstructionMessage(this, InstructionMessage.InstructionType.MOVE, teammember, - targetMoveLocation).getMessage()); + Tuple highesUtilityParams = utilityMap.entrySet().stream() + .sorted(Comparator.comparingInt(Map.Entry::getValue)).findFirst().get().getKey(); + log.debug("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + + highesUtilityParams.getRight().toString()); + radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, + highesUtilityParams.getRight()).getMessage()); } } + private void generateUtilityCalculations(Map, Integer> utilityMap, String teammember, InstructionMessage.InstructionType instructionType) { + D3Vector currentLocation = mapOfTheWorld.get(teammember); + for (double ix = -MOVE_GENERATION_DELTA; ix <= MOVE_GENERATION_DELTA; ix += MOVE_GENERATION_DELTA) { + for (double iy = -MOVE_GENERATION_DELTA; iy <= MOVE_GENERATION_DELTA; iy += MOVE_GENERATION_DELTA) { + for (double iz = -MOVE_GENERATION_DELTA; iz <= MOVE_GENERATION_DELTA; iz += MOVE_GENERATION_DELTA) { + if (ix != 0 && iy != 0 && iz != 0) { + D3Vector targetLocation = currentLocation.add(new D3Vector(ix, iy, iz)); + utilityMap.put( + new Tuple<>(instructionType, targetLocation), + calculateUtility( + instructionType, + targetLocation, + teammembers.get(teammember).getRight() + ) + ); + } else { + log.info("Skipped the current location"); //TODO remove + } + } + } + } + + } + private D3Vector calculateRandomPositionInField() { return new D3Vector( (Math.random() * (Settings.ARENA_WIDTH - 200) + 100), @@ -273,8 +319,12 @@ private D3Vector calculateRandomPositionInField() { ); } - private int calculateUtility(InstructionMessage.InstructionType type, D3Vector target) { + private int calculateUtility(InstructionMessage.InstructionType type, D3Vector target, List availableComponents) { int utility = 0; + if (type.equals(InstructionMessage.InstructionType.SHOOT) && !availableComponents.contains("gun")) { + return -1; //We cannot shoot, so all utility should be negative + } + //Drones that are close have a high likelyhood to kill you, so shoot if possible for (Map.Entry entry : mapOfTheWorld.entrySet()) { if (!teammembers.containsKey(entry.getKey())) { utility += entry.getValue().distance_between(target); From 65e6e5ee4c5d55d6c0d70c04d9be6b096327463d Mon Sep 17 00:00:00 2001 From: Tim108 Date: Fri, 1 Dec 2017 13:47:38 +0100 Subject: [PATCH 02/27] progress --- .../drone/components/engine/Engine.java | 28 +++++----- .../drone/tactic/BasicTactic.java | 52 ++++++++++--------- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index bb82a42f..73980b36 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -1,6 +1,7 @@ package org.inaetics.dronessimulator.drone.components.engine; import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.Settings; @@ -35,7 +36,8 @@ public class Engine { private volatile GPS m_gps; private Set callbacks = new HashSet<>(); - + + @Getter private D3Vector lastAcceleration; /** @@ -97,7 +99,7 @@ public D3Vector stagnate_acceleration(D3Vector input) { // Change acceleration if velocity is close to the maximum velocity if (m_gps.getVelocity().length() >= (Settings.MAX_DRONE_VELOCITY * 0.9)) { double maxAcceleration = Settings.MAX_DRONE_VELOCITY - m_gps.getVelocity().length(); - if (Math.abs(output.length()) > Math.abs(maxAcceleration)) { + if (output.length() > Math.abs(maxAcceleration)) { output = output.scale(maxAcceleration / output.length() == 0 ? 1 : output.length()); } } @@ -110,6 +112,8 @@ public D3Vector stagnate_acceleration(D3Vector input) { * @param input_acceleration The new acceleration for the drone using this component */ public void changeAcceleration(D3Vector input_acceleration) { + log.debug("CHANGED ACCELERATION -> " + input_acceleration); + D3Vector acceleration = input_acceleration; acceleration = this.limit_acceleration(acceleration); @@ -122,19 +126,18 @@ public void changeAcceleration(D3Vector input_acceleration) { } Boolean change = true; - if (lastAcceleration != null) { - double diffX = Math.abs(lastAcceleration.getX() - acceleration.getX()); - double diffY = Math.abs(lastAcceleration.getY() - acceleration.getY()); - double diffZ = Math.abs(lastAcceleration.getZ() - acceleration.getZ()); - double diffTot = diffX + diffY + diffZ; - if (diffTot < 3) { - change = false; - } - } +// if (lastAcceleration != null) { +// double diffX = Math.abs(lastAcceleration.getX() - acceleration.getX()); +// double diffY = Math.abs(lastAcceleration.getY() - acceleration.getY()); +// double diffZ = Math.abs(lastAcceleration.getZ() - acceleration.getZ()); +// double diffTot = diffX + diffY + diffZ; +// if (diffTot < 3) { +// change = false; +// } +// } if (change) { lastAcceleration = acceleration; - log.debug("Message saved! -> " + lastAcceleration + " | " + acceleration); MovementMessage msg = new MovementMessage(); msg.setAcceleration(acceleration); msg.setIdentifier(m_drone.getIdentifier()); @@ -164,7 +167,6 @@ public void moveTo(D3Vector location) { } } - public final void registerCallback(EngineCallback callback) { callbacks.add(callback); } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java index d23fc4f9..c568e8c7 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java @@ -15,16 +15,11 @@ @Log4j public class BasicTactic extends Tactic { private static final int DRONE_TIMEOUT = 2; // seconds - - D3Vector moveTarget = new D3Vector(ThreadLocalRandom.current().nextInt(100, 300), ThreadLocalRandom.current().nextInt(100, 300), ThreadLocalRandom.current().nextInt(100, 300)); + private final TimeoutTimer tacticTimer = new TimeoutTimer(1000); //ms + D3Vector moveTarget = null; D3Vector attackTarget = null; - - private D3Vector lastPosition; - private D3Vector lastAttackTarget; - Map radarDrones = new HashMap<>(); Map gunDrones = new HashMap<>(); - boolean isRadar = false; String bossDrone = ""; List myGunDrones = new ArrayList<>(); @@ -32,7 +27,8 @@ public class BasicTactic extends Tactic { BasicTacticHeartbeat heartbeat; Thread commThread; Thread heartbeatThread; - private final TimeoutTimer tacticTimer = new TimeoutTimer(1000); //ms + private D3Vector lastPosition; + private D3Vector lastAttackTarget; @Override protected void initializeTactics() { @@ -107,29 +103,35 @@ private void organize() { } private void calculateMovement() { + D3Vector position = gps.getPosition(); log.debug("distance to target = " + position.distance_between(moveTarget)); double distance = position.distance_between(moveTarget); double velocity = gps.getVelocity().length(); - if (position.distance_between(moveTarget) < 1) { - if (gps.getVelocity().length() != 0) { - engine.changeAcceleration(gps.getVelocity().scale(-1)); - } - } else { - log.debug("Velocity is: " + gps.getVelocity().length()); - if (velocity == 0 || position.distance_between(moveTarget) > ((velocity * velocity) / (2 * Settings.MAX_DRONE_ACCELERATION))) { - log.debug("accelerating.."); - engine.changeAcceleration(moveTarget.sub(position.add(gps.getVelocity()))); - } else { - log.debug("decelerating.."); - double acceleration = -(velocity * velocity) / (2 * distance); - D3Vector newAcceleration = (moveTarget.sub(position)).normalize().scale(acceleration); - log.debug(String.format("CALCULOG d=%f, v=%f, a=%f", distance, velocity, acceleration)); - - engine.changeAcceleration(newAcceleration); - } + // stationary, on target + if (distance < 1 && velocity == 0) { + log.debug("TEST21 - " + position.toString() + " - doing nothing.. "); + engine.changeAcceleration(new D3Vector()); + } + + // stationary/not stationary, not on target, accelerating + else if (distance > ((velocity * velocity) / (2 * Settings.MAX_DRONE_ACCELERATION))) { + D3Vector newAcceleration = moveTarget.sub(position); + log.debug("TEST21 - " + position.toString() + " - accelerating.. " + newAcceleration); + engine.changeAcceleration(newAcceleration); + } + + // not stationary, not on target, decelerating + else if (distance != 0) { + + double acceleration = -(velocity * velocity) / (2 * distance); + D3Vector newAcceleration = (gps.getVelocity()).normalize().scale(acceleration); + log.debug(String.format("CALCULOG d=%f, v=%f, a=%f", distance, velocity, acceleration)); + + log.debug("TEST21 - " + position.toString() + " - decelerating.. " + newAcceleration); + engine.changeAcceleration(newAcceleration); } } From b65d02a6b32ee011ce0c65bc02903403b5684c1e Mon Sep 17 00:00:00 2001 From: Tim108 Date: Fri, 1 Dec 2017 14:07:03 +0100 Subject: [PATCH 03/27] fix for random flip when standing still on target. --- .../inaetics/dronessimulator/drone/tactic/BasicTactic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java index c568e8c7..9f15124b 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java @@ -111,14 +111,14 @@ private void calculateMovement() { double velocity = gps.getVelocity().length(); // stationary, on target - if (distance < 1 && velocity == 0) { + if (distance < 1 && velocity < 1) { log.debug("TEST21 - " + position.toString() + " - doing nothing.. "); engine.changeAcceleration(new D3Vector()); } // stationary/not stationary, not on target, accelerating else if (distance > ((velocity * velocity) / (2 * Settings.MAX_DRONE_ACCELERATION))) { - D3Vector newAcceleration = moveTarget.sub(position); + D3Vector newAcceleration = moveTarget.sub(position).scale(0.5); log.debug("TEST21 - " + position.toString() + " - accelerating.. " + newAcceleration); engine.changeAcceleration(newAcceleration); } From 9ddaac8464030f7b48b01cdcc339e832d4c7c14d Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 1 Dec 2017 17:17:20 +0100 Subject: [PATCH 04/27] Merged master and improved the utility calculation --- .../drone/tactic/Activator.java | 2 +- .../dronessimulator/drone/tactic/Tactic.java | 6 +- .../drone/tactic/TheoreticalTactic.java | 84 ++++++++---- .../drone/tactic/messages/DataMessage.java | 24 ++++ .../tactic/messages/HeartbeatMessage.java | 27 ++++ .../tactic/messages/InstructionMessage.java | 25 ++++ .../tactic/messages/MyTacticMessage.java | 82 ++++++++++++ .../tactic/messages/RadarImageMessage.java | 21 +++ .../drone/tactic/TacticTester.java | 123 ++++++++++++++++++ 9 files changed, 368 insertions(+), 26 deletions(-) create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java create mode 100644 implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java index 21b12bce..0af6e769 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java @@ -25,7 +25,7 @@ public class Activator extends DependencyActivatorBase { private static final Logger logger = Logger.getLogger(Activator.class); @Override public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception { - Tactic tactic = new MoveToLocationTactic(); + Tactic tactic = new TheoreticalTactic(); Component component = createComponent() .setInterface(Tactic.class.getName(), null) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 8676ecdd..9229cbbc 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -237,7 +237,11 @@ public final void handleMessage(Message message) { */ private void handleKillMessage(KillMessage killMessage) { if (killMessage.getIdentifier().equals(m_drone.getIdentifier())) { - log.info("Found kill message! Quitting for now..."); + log.info("Found kill message! Quitting for now... Last known movements: \n" + + "\tposition: " + gps.getPosition().toString() + "\n" + + "\tvelocity: " + gps.getVelocity().toString() + "\n" + + "\tacceleration: " + gps.getAcceleration().toString() + "\n" + ); this.stopSimulation(); // if (!log.isDebugEnabled()){ // System.exit(10); diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java index be9fda00..573aac22 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java @@ -10,16 +10,20 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; @Log4j @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { public static final long ttlLeader = 2; //seconds private static final double MOVE_GENERATION_DELTA = 1.0; + private static final double SHOOTING_WEIGHT = 1; + private static final double MOVING_WEIGHT = 1; + private static final double MAX_ARENA_DISTANCE = new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length(); private DroneType droneType; private String idLeader; - private HashMap>> teammembers = new HashMap<>(); - private Map mapOfTheWorld = new HashMap<>(); + private Map>> teammembers = new ConcurrentHashMap<>(); + private Map mapOfTheWorld = new ConcurrentHashMap<>(); private ManagedThread handleBroadcastMessagesThread; private HashMap targetMoveLocations = new HashMap<>(); private D3Vector myTargetMoveLocation; @@ -134,8 +138,8 @@ private void manageIncomingCommunication() { mapOfTheWorld.put(newMessage.get("id"), D3Vector.fromString(newMessage.get("position"))); } else if (MyTacticMessage.checkType(newMessage, InstructionMessage.class)) { if (newMessage.get("receiver").equals(getIdentifier())) { - executeInstruction(InstructionMessage.InstructionType.valueOf(newMessage.get(InstructionMessage. - InstructionType.class.getSimpleName())), D3Vector.fromString(newMessage.get("target"))); + executeInstruction(InstructionMessage.InstructionType.valueOf(newMessage.get(InstructionMessage.InstructionType.class.getSimpleName())), + D3Vector.fromString(newMessage.get("target"))); } } else if (MyTacticMessage.checkType(newMessage, MyTacticMessage.MESSAGETYPES.SearchLeaderMessage) && droneType.equals(DroneType.RADAR) && idLeader == null) { @@ -278,10 +282,11 @@ private void sendInstructions() { ); } } - Tuple highesUtilityParams = utilityMap.entrySet().stream() - .sorted(Comparator.comparingInt(Map.Entry::getValue)).findFirst().get().getKey(); - log.debug("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + - highesUtilityParams.getRight().toString()); + Map.Entry, Integer> highestUtility = utilityMap.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).findFirst().get(); + Tuple highesUtilityParams = highestUtility.getKey(); + log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + + highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtility.getValue() + " out of " + Arrays.toString(utilityMap.values() + .toArray())); radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); } @@ -292,19 +297,15 @@ private void generateUtilityCalculations(Map(instructionType, targetLocation), - calculateUtility( - instructionType, - targetLocation, - teammembers.get(teammember).getRight() - ) - ); - } else { - log.info("Skipped the current location"); //TODO remove - } + D3Vector targetLocation = currentLocation.add(new D3Vector(ix, iy, iz)); + utilityMap.put( + new Tuple<>(instructionType, targetLocation), + calculateUtility( + instructionType, + targetLocation, + teammembers.get(teammember).getRight() + ) + ); } } } @@ -319,20 +320,55 @@ private D3Vector calculateRandomPositionInField() { ); } - private int calculateUtility(InstructionMessage.InstructionType type, D3Vector target, List availableComponents) { + public int calculateUtility(InstructionMessage.InstructionType type, D3Vector target, List availableComponents) { int utility = 0; if (type.equals(InstructionMessage.InstructionType.SHOOT) && !availableComponents.contains("gun")) { return -1; //We cannot shoot, so all utility should be negative } - //Drones that are close have a high likelyhood to kill you, so shoot if possible + if (type.equals(InstructionMessage.InstructionType.MOVE) && !insideRange(D3Vector.ZERO, + new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT), target)) { + return -1; //We never want to move to a location that is out of the bounds of the game + } + //Drones that are close have a high likelyhood to kill you, so shoot if possible and move in the opposite + // direction for (Map.Entry entry : mapOfTheWorld.entrySet()) { if (!teammembers.containsKey(entry.getKey())) { - utility += entry.getValue().distance_between(target); + Map.Entry enemy = entry; + if (type.equals(InstructionMessage.InstructionType.SHOOT)) { + //Shooting at the closest enemy gives the highest utility + if (target.equals(enemy.getValue())) //If the target to shoot is at the same position as the enemy + utility += (MAX_ARENA_DISTANCE - target.distance_between(gps.getPosition())) * SHOOTING_WEIGHT; + } else { + double distanceToEnemy = enemy.getValue().distance_between(target); + if (availableComponents.contains("gun")) { + //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if + // we move towards the enemy. + utility += (MAX_ARENA_DISTANCE - distanceToEnemy) * MOVING_WEIGHT; + } else { + //We cannot shoot it, so evade it. + utility += (distanceToEnemy * MOVING_WEIGHT); + } + } } } return utility; //TODO } + private boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vector testedLocation) { + return + //Check the x location + testedLocation.getX() > startRange.getX() && + testedLocation.getX() < endRange.getX() && + //Check the y location + testedLocation.getY() > startRange.getY() && + testedLocation.getY() < endRange.getY() && + //Check the z location + testedLocation.getZ() > startRange.getZ() && + testedLocation.getZ() < endRange.getZ(); + + + } + private enum DroneType { GUN, RADAR } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java new file mode 100644 index 00000000..3a25d436 --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java @@ -0,0 +1,24 @@ +package org.inaetics.dronessimulator.drone.tactic.messages; + +import lombok.AccessLevel; +import lombok.Getter; +import org.inaetics.dronessimulator.drone.tactic.Tactic; + +import java.util.HashMap; +import java.util.Map; + +public class DataMessage extends MyTacticMessage { + private final String messageType; + @Getter(AccessLevel.PUBLIC) + private Map data = new HashMap<>(); + + public DataMessage(Tactic tactic, String messageType) { + super(tactic); + this.messageType = messageType; + } + + @Override + protected String getType() { + return messageType; + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java new file mode 100644 index 00000000..500e3a78 --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java @@ -0,0 +1,27 @@ +package org.inaetics.dronessimulator.drone.tactic.messages; + +import lombok.AccessLevel; +import lombok.Getter; +import org.inaetics.dronessimulator.drone.components.gps.GPS; +import org.inaetics.dronessimulator.drone.tactic.TheoreticalTactic; + +import java.util.HashMap; +import java.util.Map; + +public class HeartbeatMessage extends MyTacticMessage { + @Getter(AccessLevel.PROTECTED) + private Map data = new HashMap<>(); + + public HeartbeatMessage(TheoreticalTactic tactic, GPS gps) { + super(tactic); + data.put("position", gps.getPosition().toString()); + data.put("direction", gps.getDirection().toString()); + data.put("velocity", gps.getVelocity().toString()); + data.put("acceleration", gps.getAcceleration().toString()); + data.put("components", String.join(",", tactic.getAvailableComponents().toArray(new String[]{}))); + } + + public void setIsLeader(boolean isLeader) { + data.put("isLeader", String.valueOf(isLeader)); + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java new file mode 100644 index 00000000..7d60ad09 --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java @@ -0,0 +1,25 @@ +package org.inaetics.dronessimulator.drone.tactic.messages; + +import lombok.AccessLevel; +import lombok.Getter; +import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.Tactic; + +import java.util.HashMap; +import java.util.Map; + +public class InstructionMessage extends MyTacticMessage { + @Getter(AccessLevel.PROTECTED) + private Map data = new HashMap<>(); + + public InstructionMessage(Tactic tactic, InstructionType type, String instructionIsFor, D3Vector target) { + super(tactic); + data.put(InstructionType.class.getSimpleName(), type.name()); + data.put("receiver", instructionIsFor); + data.put("target", String.valueOf(target)); + } + + public enum InstructionType { + MOVE, SHOOT + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java new file mode 100644 index 00000000..b0bf5fde --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java @@ -0,0 +1,82 @@ +package org.inaetics.dronessimulator.drone.tactic.messages; + +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.log4j.Log4j; +import org.inaetics.dronessimulator.common.protocol.TacticMessage; +import org.inaetics.dronessimulator.drone.tactic.Tactic; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +@Log4j +public abstract class MyTacticMessage { + private final Tactic tactic; + + public static boolean checkType(TacticMessage newMessage, Class messageClass) { + return messageClass.getSimpleName().equals(newMessage.get("type")); + } + + public static boolean checkType(TacticMessage newMessage, String messageType) { + return messageType != null && messageType.equals(newMessage.get("type")); + } + + public TacticMessage getMessage() { + //Check if the type is registered + if (!MESSAGETYPES.getMessageTypes().contains(getType())) { + throw new InvalidMessageTypeException(getType()); + } + + TacticMessage message = new TacticMessage(); + message.put("id", tactic.getIdentifier()); + message.put("type", getType()); + message.putAll(getData()); + return message; + } + + /** + * This method is called to add all the data to the final message. This should return a map of the data. This + * method can also be used to modify the data of a message from outside the subclass. + * + * @return a map with the data + */ + protected abstract Map getData(); + + /** + * This method is intended to be overridden if a subclass is of a different type of message than its class name + * + * @return the type of the message + */ + protected String getType() { + return getClass().getSimpleName(); + } + + public static class MESSAGETYPES { + public static final String HeartbeatMessage = HeartbeatMessage.class.getSimpleName(); + public static final String RadarImageMessage = RadarImageMessage.class.getSimpleName(); + public static final String InstructionMessage = InstructionMessage.class.getSimpleName(); + public static final String FiredBulletMessage = "FiredBulletMessage"; + public static final String SearchLeaderMessage = "SearchLeaderMessage"; + public static final String IsLeaderMessage = "IsLeaderMessage"; + + public static List getMessageTypes() { + return Arrays.asList(Arrays.stream(MESSAGETYPES.class.getFields()).map(field -> { + try { + return field.get(null); + } catch (IllegalAccessException e) { + log.error(e); + return null; + } + }).toArray(String[]::new)); + } + } + + @AllArgsConstructor + @ToString + public class InvalidMessageTypeException extends RuntimeException { + private String invalidMessageType; + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java new file mode 100644 index 00000000..fda21cfc --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java @@ -0,0 +1,21 @@ +package org.inaetics.dronessimulator.drone.tactic.messages; + +import lombok.AccessLevel; +import lombok.Getter; +import org.inaetics.dronessimulator.common.Tuple; +import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.Tactic; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RadarImageMessage extends MyTacticMessage { + @Getter(AccessLevel.PROTECTED) + private Map data = new HashMap<>(); + + public RadarImageMessage(Tactic tactic, List> radarImage) { + super(tactic); + radarImage.forEach(tup -> data.put(tup.getLeft(), tup.getRight().toString())); + } +} diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java new file mode 100644 index 00000000..a9f51b90 --- /dev/null +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java @@ -0,0 +1,123 @@ +package org.inaetics.dronessimulator.drone.tactic; + +import lombok.extern.log4j.Log4j; +import org.inaetics.dronessimulator.common.Settings; +import org.inaetics.dronessimulator.common.Tuple; +import org.inaetics.dronessimulator.common.protocol.TacticMessage; +import org.inaetics.dronessimulator.common.protocol.TeamTopic; +import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.droneinit.DroneInit; +import org.inaetics.dronessimulator.drone.tactic.messages.HeartbeatMessage; +import org.inaetics.dronessimulator.drone.tactic.messages.InstructionMessage; +import org.inaetics.dronessimulator.pubsub.api.publisher.Publisher; +import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; +import org.inaetics.dronessimulator.test.concurrent.MockPublisher; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.inaetics.dronessimulator.drone.tactic.TacticTesterHelper.setField; +import static org.mockito.Mockito.mock; + +@Log4j +public class TacticTester { + private DroneInit droneInit; + private TheoreticalTactic tactic; + private MockPublisher publisher; + + @Before + public void setup() throws IllegalAccessException, NoSuchFieldException, InstantiationException { + droneInit = new DroneInit(); + publisher = new MockPublisher(); + Subscriber subscriber = mock(Subscriber.class); + tactic = TacticTesterHelper.getTactic(TheoreticalTactic.class, publisher, subscriber, droneInit); + tactic.initializeTactics(); + } + + @Test + public void testCalculateTactics() { + for (int i = 0; i < 10; i++) { + tactic.calculateTactics(); + tactic.radio.handleMessage(new HeartbeatMessage(tactic, tactic.gps).getMessage()); + } + Assert.assertTrue(publisher.getReceivedMessages().size() >= 10); + Assert.assertThat(publisher.getReceivedMessages(), hasItem(new Tuple<>(new TeamTopic("unknown_team"), new + TacticMessage()))); + publisher.getReceivedMessages().forEach(message -> log.debug("Message on topic \"" + message.getLeft().getName + () + "\" with content: " + message.getRight().toString())); + } + + @Test + public void testGettingALeader() throws IllegalAccessException, NoSuchFieldException, InstantiationException, InterruptedException { + Tuple pubSub = TacticTesterHelper.getConnectedMockPubSub(); + DroneInit drone1 = new DroneInit(); + DroneInit drone2 = new DroneInit(); + Tactic tactic = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), + drone1); + tactic.initializeTactics(); + tactic.startTactic(); + Tactic tactic2 = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), + drone2); + tactic2.initializeTactics(); + tactic2.startTactic(); + for (int i = 0; i < 20; i++) { + try { + tactic.work(); + log.debug("do work 1"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + tactic2.work(); + log.debug("do work 2"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Object leader1 = TacticTesterHelper.getField(tactic, "idLeader"); + Object leader2 = TacticTesterHelper.getField(tactic2, "idLeader"); + //After a while they should have the same leader + Assert.assertNotNull(leader1); + Assert.assertNotNull(leader2); + Assert.assertEquals(leader1, leader2); + + //When the leader dies, the remaining drone should be its own leader. + tactic.stopTactic(); + Thread.sleep(TheoreticalTactic.ttlLeader * 1000); + for (int i = 0; i < 20; i++) { + try { + tactic2.work(); + log.debug("do work 2"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Object leader3 = TacticTesterHelper.getField(tactic2, "idLeader"); + Assert.assertNotNull(leader3); + Assert.assertEquals("The only drone is not its own leader anymore", tactic2.getIdentifier(), leader3); + tactic2.stopTactic(); + } + + @Test + public void testCalculateUtility() throws NoSuchFieldException, IllegalAccessException { + tactic.gps.setPosition(D3Vector.UNIT); + Map>> teammembers = new ConcurrentHashMap<>(); + Map mapOfTheWorld = new ConcurrentHashMap<>(); + mapOfTheWorld.put("enemyDrone", new D3Vector(100, 100, 100)); + setField(tactic, "teammembers", teammembers); + setField(tactic, "mapOfTheWorld", mapOfTheWorld); + int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, new D3Vector(50, 50, 50), Arrays.asList("gun", "engine", "radio", "radio")); + + Assert.assertEquals((int) new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length() - (int) new D3Vector(50, 50, 50).length(), + utility); + //TODO + } + +} From 00add0f329f3b8ab5aefa749ba6951a06cd291cd Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 1 Dec 2017 23:22:34 +0100 Subject: [PATCH 05/27] The tactic now properly gives the order to do things. But it still does not really weigh its utility. Still to do is to remove out of date drones from the map of the world. --- .../drone/tactic/TheoreticalTactic.java | 86 ++++++++++++------- .../tactic/messages/RadarImageMessage.java | 7 ++ .../drone/tactic/TacticTester.java | 28 ++++-- .../ruleprocessors/RuleProcessors.java | 6 +- 4 files changed, 84 insertions(+), 43 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java index 573aac22..0dca6289 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java @@ -22,8 +22,8 @@ public class TheoreticalTactic extends Tactic { private static final double MAX_ARENA_DISTANCE = new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length(); private DroneType droneType; private String idLeader; - private Map>> teammembers = new ConcurrentHashMap<>(); - private Map mapOfTheWorld = new ConcurrentHashMap<>(); + private Map> teammembers = new ConcurrentHashMap<>(); + private Map> mapOfTheWorld = new ConcurrentHashMap<>(); private ManagedThread handleBroadcastMessagesThread; private HashMap targetMoveLocations = new HashMap<>(); private D3Vector myTargetMoveLocation; @@ -134,8 +134,10 @@ private void manageIncomingCommunication() { if (newMessage != null) { log.debug("Received a message with type " + String.valueOf(newMessage.get("type"))); if (MyTacticMessage.checkType(newMessage, HeartbeatMessage.class)) { - teammembers.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), Arrays.asList(newMessage.get("components").split(",")))); - mapOfTheWorld.put(newMessage.get("id"), D3Vector.fromString(newMessage.get("position"))); + teammembers.put(newMessage.get("id"), Arrays.asList(newMessage.get("components").split(","))); + mapOfTheWorld.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")))); + } else if (MyTacticMessage.checkType(newMessage, RadarImageMessage.class)) { + RadarImageMessage.parseData(newMessage).forEach((k, v) -> mapOfTheWorld.put(k, new Tuple<>(LocalDateTime.now(), v))); } else if (MyTacticMessage.checkType(newMessage, InstructionMessage.class)) { if (newMessage.get("receiver").equals(getIdentifier())) { executeInstruction(InstructionMessage.InstructionType.valueOf(newMessage.get(InstructionMessage.InstructionType.class.getSimpleName())), @@ -256,44 +258,59 @@ private void broadcastHeartbeat() { private void sendInstructions() { for (String teammember : teammembers.keySet()) { - Map, Integer> utilityMap = new HashMap<>(); + Map, Integer> utilityMapMove = new HashMap<>(); + Map, Integer> utilityMapShoot = new HashMap<>(); //Generate utility calculations to move in any of the 26 directions - generateUtilityCalculations(utilityMap, teammember, InstructionMessage.InstructionType.MOVE); + generateUtilityCalculations(utilityMapMove, teammember, InstructionMessage.InstructionType.MOVE); //Generate utility for movement towards a different drone and shooting towards a drone - for (Map.Entry enemy : mapOfTheWorld.entrySet()) { - utilityMap.put( - new Tuple<>(InstructionMessage.InstructionType.MOVE, enemy.getValue()), + for (Map.Entry> enemy : mapOfTheWorld.entrySet()) { + utilityMapMove.put( + new Tuple<>(InstructionMessage.InstructionType.MOVE, enemy.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.MOVE, - enemy.getValue(), - teammembers.get(teammember).getRight() + mapOfTheWorld.get(teammember).getRight(), + enemy.getValue().getRight(), + teammembers.get(teammember) ) ); - if (teammembers.get(teammember).getRight().contains("gun")) { - utilityMap.put( - new Tuple<>(InstructionMessage.InstructionType.SHOOT, enemy.getValue()), + if (teammembers.get(teammember).contains("gun")) { + utilityMapShoot.put( + new Tuple<>(InstructionMessage.InstructionType.SHOOT, enemy.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.SHOOT, - enemy.getValue(), - teammembers.get(teammember).getRight() + mapOfTheWorld.get(teammember).getRight(), + enemy.getValue().getRight(), + teammembers.get(teammember) ) ); } } - Map.Entry, Integer> highestUtility = utilityMap.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getValue)).findFirst().get(); - Tuple highesUtilityParams = highestUtility.getKey(); - log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + - highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtility.getValue() + " out of " + Arrays.toString(utilityMap.values() - .toArray())); - radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, - highesUtilityParams.getRight()).getMessage()); + Optional, Integer>> highestUtilityShoot = utilityMapShoot.entrySet().stream().sorted((i2, i1) + -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + if (highestUtilityShoot.isPresent()) { + Tuple highesUtilityParams = highestUtilityShoot.get().getKey(); + log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + + highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityShoot.get().getValue() + " out of " + Arrays.toString + (utilityMapShoot.values().toArray())); + radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); + } + Optional, Integer>> highestUtilityMove = utilityMapMove.entrySet().stream().sorted((i2, i1) + -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + if (highestUtilityMove.isPresent()) { + Tuple highesUtilityParams = highestUtilityMove.get().getKey(); + log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + + highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityMove.get().getValue() + " out of " + Arrays.toString + (utilityMapMove.values().toArray())); + radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); + } + } } private void generateUtilityCalculations(Map, Integer> utilityMap, String teammember, InstructionMessage.InstructionType instructionType) { - D3Vector currentLocation = mapOfTheWorld.get(teammember); + D3Vector currentLocation = mapOfTheWorld.get(teammember).getRight(); for (double ix = -MOVE_GENERATION_DELTA; ix <= MOVE_GENERATION_DELTA; ix += MOVE_GENERATION_DELTA) { for (double iy = -MOVE_GENERATION_DELTA; iy <= MOVE_GENERATION_DELTA; iy += MOVE_GENERATION_DELTA) { for (double iz = -MOVE_GENERATION_DELTA; iz <= MOVE_GENERATION_DELTA; iz += MOVE_GENERATION_DELTA) { @@ -302,8 +319,9 @@ private void generateUtilityCalculations(Map(instructionType, targetLocation), calculateUtility( instructionType, + mapOfTheWorld.get(teammember).getRight(), targetLocation, - teammembers.get(teammember).getRight() + teammembers.get(teammember) ) ); } @@ -320,7 +338,7 @@ private D3Vector calculateRandomPositionInField() { ); } - public int calculateUtility(InstructionMessage.InstructionType type, D3Vector target, List availableComponents) { + public Integer calculateUtility(InstructionMessage.InstructionType type, D3Vector droneLocation, D3Vector target, List availableComponents) { int utility = 0; if (type.equals(InstructionMessage.InstructionType.SHOOT) && !availableComponents.contains("gun")) { return -1; //We cannot shoot, so all utility should be negative @@ -329,17 +347,16 @@ public int calculateUtility(InstructionMessage.InstructionType type, D3Vector ta new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT), target)) { return -1; //We never want to move to a location that is out of the bounds of the game } - //Drones that are close have a high likelyhood to kill you, so shoot if possible and move in the opposite - // direction - for (Map.Entry entry : mapOfTheWorld.entrySet()) { + //Drones that are close have a high likelyhood to kill you, so shoot if possible and move in the opposite direction + for (Map.Entry> entry : mapOfTheWorld.entrySet()) { if (!teammembers.containsKey(entry.getKey())) { - Map.Entry enemy = entry; + Map.Entry> enemy = entry; if (type.equals(InstructionMessage.InstructionType.SHOOT)) { //Shooting at the closest enemy gives the highest utility if (target.equals(enemy.getValue())) //If the target to shoot is at the same position as the enemy - utility += (MAX_ARENA_DISTANCE - target.distance_between(gps.getPosition())) * SHOOTING_WEIGHT; + utility += (MAX_ARENA_DISTANCE - target.distance_between(droneLocation)) * SHOOTING_WEIGHT; } else { - double distanceToEnemy = enemy.getValue().distance_between(target); + double distanceToEnemy = enemy.getValue().getRight().distance_between(target); if (availableComponents.contains("gun")) { //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if // we move towards the enemy. @@ -349,6 +366,11 @@ public int calculateUtility(InstructionMessage.InstructionType type, D3Vector ta utility += (distanceToEnemy * MOVING_WEIGHT); } } + } else { + if (type.equals(InstructionMessage.InstructionType.MOVE)) { + //Move with teammates over moving alone + utility += (MAX_ARENA_DISTANCE - (int) entry.getValue().getRight().distance_between(droneLocation)) * MOVING_WEIGHT; + } } } return utility; //TODO diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java index fda21cfc..5440fb29 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java @@ -3,6 +3,7 @@ import lombok.AccessLevel; import lombok.Getter; import org.inaetics.dronessimulator.common.Tuple; +import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.Tactic; @@ -18,4 +19,10 @@ public RadarImageMessage(Tactic tactic, List> radarImage super(tactic); radarImage.forEach(tup -> data.put(tup.getLeft(), tup.getRight().toString())); } + + public static Map parseData(TacticMessage rawMessage) { + Map data = new HashMap<>(); + rawMessage.entrySet().stream().filter(e -> !e.getKey().equals("id") && !e.getKey().equals("type")).forEach(e -> data.put(e.getKey(), D3Vector.fromString(e.getValue()))); + return data; + } } diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java index a9f51b90..ecff2133 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java @@ -107,17 +107,33 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep @Test public void testCalculateUtility() throws NoSuchFieldException, IllegalAccessException { - tactic.gps.setPosition(D3Vector.UNIT); + //Create the world Map>> teammembers = new ConcurrentHashMap<>(); Map mapOfTheWorld = new ConcurrentHashMap<>(); - mapOfTheWorld.put("enemyDrone", new D3Vector(100, 100, 100)); + mapOfTheWorld.put("enemyDrone", new D3Vector(100, 100, 50)); setField(tactic, "teammembers", teammembers); setField(tactic, "mapOfTheWorld", mapOfTheWorld); - int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, new D3Vector(50, 50, 50), Arrays.asList("gun", "engine", "radio", "radio")); - Assert.assertEquals((int) new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length() - (int) new D3Vector(50, 50, 50).length(), - utility); - //TODO + //Move towards a drone since we have a gun + tactic.gps.setPosition(D3Vector.UNIT); + int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(50, 50, 50), Arrays.asList("gun", + "engine", "radio", "radio")); + Assert.assertEquals((int) new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length() - (int) new D3Vector(50, 50, + 0).length(), utility); + + //Do not move out of bounds + tactic.gps.setPosition(D3Vector.UNIT); + utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(-1, -1, -1), Arrays.asList("gun", + "engine", "radio", "radio")); + Assert.assertEquals(-1, utility); + + //Move away from a drone if you do not have a gun. The higher the distance, the better + tactic.gps.setPosition(D3Vector.UNIT); + utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(50, 50, 50), Arrays.asList("engine", + "radio", "radio")); + Assert.assertEquals((int) new D3Vector(50, 50, 50).distance_between(mapOfTheWorld.get("enemyDrone")), utility); + + // } } diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java index 36ceb829..4aedf640 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java @@ -56,11 +56,7 @@ public void start() { this.intervalRules = RuleSets.getIntervalRulesForGameMode(Settings.GAME_MODE, this.m_publisher, this .m_id_mapper); - m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, - (SimulationState from, SimulationAction action, SimulationState to) -> { - configRules(); - } - ); + m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, (from, action, to) -> configRules()); //When the user presses start, reset the Interval rules timeout m_architectureEventController.addHandler(SimulationState.CONFIG, SimulationAction.START, SimulationState.RUNNING, (f, a, t) -> INTERVAL_RULES_TIMEOUT.reset()); From f90320d36043ceeb6128c18fb0924a113e459ff0 Mon Sep 17 00:00:00 2001 From: Martijn Date: Sat, 2 Dec 2017 14:08:38 +0100 Subject: [PATCH 06/27] Changed the location of all tactics and made it possible to use a config parameter to determine the tactic. --- docker-compose.yml | 20 ++++++----- .../drone/tactic/Activator.java | 18 +++++++++- .../dronessimulator/drone/tactic/Tactic.java | 6 ++-- .../{ => example}/MoveToLocationTactic.java | 7 ++-- .../tactic/{ => example}/SimpleTactic.java | 3 +- .../{ => example/basic}/BasicTactic.java | 3 +- .../basic}/BasicTacticCommunication.java | 6 ++-- .../basic}/BasicTacticHeartbeat.java | 6 ++-- .../{ => example/basic}/ProtocolTags.java | 2 +- .../utility}/TheoreticalTactic.java | 22 +++++++----- .../utility}/messages/DataMessage.java | 2 +- .../utility}/messages/HeartbeatMessage.java | 4 +-- .../utility}/messages/InstructionMessage.java | 2 +- .../utility}/messages/MyTacticMessage.java | 2 +- .../utility}/messages/RadarImageMessage.java | 2 +- .../drone/tactic/TacticTesterHelper.java | 2 +- .../MoveToLocationTacticTest.java | 3 +- .../utility/TheoreticalTacticTester.java} | 34 ++++++------------- 18 files changed, 78 insertions(+), 66 deletions(-) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example}/MoveToLocationTactic.java (93%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example}/SimpleTactic.java (98%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/basic}/BasicTactic.java (97%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/basic}/BasicTacticCommunication.java (94%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/basic}/BasicTacticHeartbeat.java (80%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/basic}/ProtocolTags.java (63%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/TheoreticalTactic.java (95%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/messages/DataMessage.java (87%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/messages/HeartbeatMessage.java (84%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/messages/InstructionMessage.java (90%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/messages/MyTacticMessage.java (97%) rename implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/{ => example/utility}/messages/RadarImageMessage.java (93%) rename implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/{ => example}/MoveToLocationTacticTest.java (94%) rename implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/{TacticTester.java => example/utility/TheoreticalTacticTester.java} (83%) diff --git a/docker-compose.yml b/docker-compose.yml index f13a8cf5..84494a00 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,6 +51,7 @@ services: environment: DRONE_TEAM: "teamthales" DRONE_COMPONENTS: "gps,radar,radio" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" drone-thales_2: build: ./docker_images/drone env_file: config.env @@ -60,15 +61,17 @@ services: environment: DRONE_TEAM: "teamthales" DRONE_COMPONENTS: "gps,radio,gun" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" drone-thales_3: - build: ./docker_images/drone - env_file: config.env - depends_on: - rabbitmq: - condition: service_healthy - environment: - DRONE_TEAM: "teamthales" - DRONE_COMPONENTS: "gps,radio,gun" + build: ./docker_images/drone + env_file: config.env + depends_on: + rabbitmq: + condition: service_healthy + environment: + DRONE_TEAM: "teamthales" + DRONE_COMPONENTS: "gps,radio,gun" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" drone-student_1: @@ -80,3 +83,4 @@ services: environment: DRONE_TEAM: "teamstudent" DRONE_COMPONENTS: "gps,radar,radio,gun" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.SimpleTactic" diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java index 0af6e769..37fea2d5 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java @@ -13,6 +13,8 @@ import org.inaetics.dronessimulator.drone.components.radar.Radar; import org.inaetics.dronessimulator.drone.components.radio.Radio; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; +import org.inaetics.dronessimulator.drone.tactic.example.SimpleTactic; +import org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; import org.osgi.framework.BundleContext; @@ -25,7 +27,8 @@ public class Activator extends DependencyActivatorBase { private static final Logger logger = Logger.getLogger(Activator.class); @Override public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception { - Tactic tactic = new TheoreticalTactic(); + Tactic tactic = createNewTactic(); + new TheoreticalTactic(); Component component = createComponent() .setInterface(Tactic.class.getName(), null) @@ -63,6 +66,19 @@ public void init(BundleContext bundleContext, DependencyManager dependencyManage dependencyManager.add(component); } + private Tactic createNewTactic() { + try { + Class possibleTacticClass = Class.forName(System.getenv("DRONE_TACTIC")); + Object possibleTactic = possibleTacticClass.newInstance(); + return (Tactic) possibleTactic; + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException e) { + logger.fatal(String.format("Could not find a tactic with name: %s. Please provide a fully classified class name that extends %s.java. The exception was: " + + "%s:%s", System.getenv("DRONE_TACTIC"), Tactic.class.getName(), e.getClass().getSimpleName(), e.getMessage()), e); + } + //By default return SimpleTactic + return new SimpleTactic(); + } + public List getDroneComponents(DependencyManager dm) { String envComponents = System.getenv("DRONE_COMPONENTS"); if (envComponents == null || "".equals(envComponents)) { diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 9229cbbc..83013b4b 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -38,10 +38,10 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { public static final long tacticTimout = 1;//tck // drone components protected volatile Radar radar; - protected volatile GPS gps; - protected volatile Engine engine; + public volatile GPS gps; + public volatile Engine engine; protected volatile Gun gun; - protected volatile Radio radio; + public volatile Radio radio; /** * Drone Init bundle */ diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java similarity index 93% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTactic.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java index 795c2e4d..83ca0e33 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java @@ -1,8 +1,9 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.Tactic; import java.util.concurrent.ThreadLocalRandom; @@ -34,7 +35,7 @@ protected void finalizeTactics() { log.info("Finalizing tactics.."); } - void moveToLocation(D3Vector location) { + public void moveToLocation(D3Vector location) { D3Vector position = gps.getPosition(); log.info(String.format("location: %s", location)); log.info(String.format("position: %s", position)); @@ -60,7 +61,7 @@ void moveToLocation(D3Vector location) { } } - void calculateMovement(D3Vector moveTarget) { + public void calculateMovement(D3Vector moveTarget) { D3Vector position = gps.getPosition(); log.info(String.format("location: %s", moveTarget)); log.info(String.format("position: %s", position)); diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/SimpleTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java similarity index 98% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/SimpleTactic.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java index 4bf601ee..d89eba9f 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/SimpleTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java @@ -1,8 +1,9 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example; import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.Tuple; import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.Tactic; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java similarity index 97% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java index d23fc4f9..dfec0304 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java @@ -1,9 +1,10 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.basic; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.TimeoutTimer; import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.Tactic; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticCommunication.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticCommunication.java similarity index 94% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticCommunication.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticCommunication.java index d9bfcb61..4391bbc4 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticCommunication.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticCommunication.java @@ -1,6 +1,5 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.basic; -import lombok.AllArgsConstructor; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; @@ -8,9 +7,8 @@ import org.inaetics.dronessimulator.pubsub.api.Message; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import static org.inaetics.dronessimulator.drone.tactic.ProtocolTags.*; +import static org.inaetics.dronessimulator.drone.tactic.example.basic.ProtocolTags.CONNECT_CONFIRM; @Log4j public class BasicTacticCommunication implements Runnable { diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticHeartbeat.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java similarity index 80% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticHeartbeat.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java index 8691d6b2..0fe448e3 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticHeartbeat.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java @@ -1,10 +1,10 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.basic; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import static org.inaetics.dronessimulator.drone.tactic.ProtocolTags.HEARTBEAT_GUN; -import static org.inaetics.dronessimulator.drone.tactic.ProtocolTags.HEARTBEAT_RADAR; +import static org.inaetics.dronessimulator.drone.tactic.example.basic.ProtocolTags.HEARTBEAT_GUN; +import static org.inaetics.dronessimulator.drone.tactic.example.basic.ProtocolTags.HEARTBEAT_RADAR; public class BasicTacticHeartbeat implements Runnable{ diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/ProtocolTags.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/ProtocolTags.java similarity index 63% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/ProtocolTags.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/ProtocolTags.java index 159cf68b..3042ee72 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/ProtocolTags.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/ProtocolTags.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.basic; public enum ProtocolTags { HEARTBEAT_RADAR, MOVE, SHOOT, CONNECT_REQUEST, CONNECT_CONFIRM, HEARTBEAT_GUN diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java similarity index 95% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 0dca6289..7956b915 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -1,11 +1,12 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.utility; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.*; import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; -import org.inaetics.dronessimulator.drone.tactic.messages.*; +import org.inaetics.dronessimulator.drone.tactic.Tactic; +import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -23,7 +24,7 @@ public class TheoreticalTactic extends Tactic { private DroneType droneType; private String idLeader; private Map> teammembers = new ConcurrentHashMap<>(); - private Map> mapOfTheWorld = new ConcurrentHashMap<>(); + private Map> mapOfTheWorld = new ConcurrentHashMap<>(); //TODO remove stale data private ManagedThread handleBroadcastMessagesThread; private HashMap targetMoveLocations = new HashMap<>(); private D3Vector myTargetMoveLocation; @@ -79,6 +80,8 @@ protected void initializeTactics() { */ @Override protected void calculateTactics() { + //Remove old data from the map + mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), ttlLeader)); manageOutgoingCommunication(); // moveToRandomLocation(); //Check leader @@ -287,18 +290,19 @@ private void sendInstructions() { ); } } - Optional, Integer>> highestUtilityShoot = utilityMapShoot.entrySet().stream().sorted((i2, i1) - -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); - if (highestUtilityShoot.isPresent()) { + Optional, Integer>> highestUtilityShoot = + utilityMapShoot.entrySet().stream().sorted((i2, i1) -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + if (highestUtilityShoot.isPresent() && highestUtilityShoot.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityShoot.get().getKey(); log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityShoot.get().getValue() + " out of " + Arrays.toString (utilityMapShoot.values().toArray())); radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); } - Optional, Integer>> highestUtilityMove = utilityMapMove.entrySet().stream().sorted((i2, i1) - -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); - if (highestUtilityMove.isPresent()) { + + Optional, Integer>> highestUtilityMove = + utilityMapMove.entrySet().stream().sorted((i2, i1) -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + if (highestUtilityMove.isPresent() && highestUtilityMove.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityMove.get().getKey(); log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityMove.get().getValue() + " out of " + Arrays.toString diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/DataMessage.java similarity index 87% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/DataMessage.java index 3a25d436..30f5f2a9 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/DataMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/DataMessage.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic.messages; +package org.inaetics.dronessimulator.drone.tactic.example.utility.messages; import lombok.AccessLevel; import lombok.Getter; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java similarity index 84% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java index 500e3a78..9efbe650 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/HeartbeatMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java @@ -1,9 +1,9 @@ -package org.inaetics.dronessimulator.drone.tactic.messages; +package org.inaetics.dronessimulator.drone.tactic.example.utility.messages; import lombok.AccessLevel; import lombok.Getter; import org.inaetics.dronessimulator.drone.components.gps.GPS; -import org.inaetics.dronessimulator.drone.tactic.TheoreticalTactic; +import org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic; import java.util.HashMap; import java.util.Map; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java similarity index 90% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java index 7d60ad09..554aa625 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/InstructionMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic.messages; +package org.inaetics.dronessimulator.drone.tactic.example.utility.messages; import lombok.AccessLevel; import lombok.Getter; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java similarity index 97% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java index b0bf5fde..90e9249e 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/MyTacticMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic.messages; +package org.inaetics.dronessimulator.drone.tactic.example.utility.messages; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java similarity index 93% rename from implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java rename to implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java index 5440fb29..6d3e0219 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/messages/RadarImageMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic.messages; +package org.inaetics.dronessimulator.drone.tactic.example.utility.messages; import lombok.AccessLevel; import lombok.Getter; diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java index 79517afa..d52e7679 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java @@ -69,7 +69,7 @@ public static void setField(Object target, String fieldname, Object value) throw ); } - static Object getField(Object target, String fieldname) throws IllegalAccessException, + public static Object getField(Object target, String fieldname) throws IllegalAccessException, NoSuchFieldException { Optional> result = doWithFields(target.getClass(), field -> { diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTacticTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java similarity index 94% rename from implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTacticTest.java rename to implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java index f18f0037..a9ce667b 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/MoveToLocationTacticTest.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java @@ -1,10 +1,11 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example; import org.inaetics.dronessimulator.common.vector.D3PolarCoordinate; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.components.engine.Engine; import org.inaetics.dronessimulator.drone.components.gps.GPS; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; +import org.inaetics.dronessimulator.drone.tactic.TacticTesterHelper; import org.inaetics.dronessimulator.pubsub.api.publisher.Publisher; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; import org.junit.Assert; diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java similarity index 83% rename from implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java rename to implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java index ecff2133..fdac997c 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTester.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java @@ -1,4 +1,4 @@ -package org.inaetics.dronessimulator.drone.tactic; +package org.inaetics.dronessimulator.drone.tactic.example.utility; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.Settings; @@ -7,8 +7,9 @@ import org.inaetics.dronessimulator.common.protocol.TeamTopic; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; -import org.inaetics.dronessimulator.drone.tactic.messages.HeartbeatMessage; -import org.inaetics.dronessimulator.drone.tactic.messages.InstructionMessage; +import org.inaetics.dronessimulator.drone.tactic.TacticTesterHelper; +import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.HeartbeatMessage; +import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.InstructionMessage; import org.inaetics.dronessimulator.pubsub.api.publisher.Publisher; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; import org.inaetics.dronessimulator.test.concurrent.MockPublisher; @@ -27,7 +28,7 @@ import static org.mockito.Mockito.mock; @Log4j -public class TacticTester { +public class TheoreticalTacticTester { private DroneInit droneInit; private TheoreticalTactic tactic; private MockPublisher publisher; @@ -59,27 +60,17 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep Tuple pubSub = TacticTesterHelper.getConnectedMockPubSub(); DroneInit drone1 = new DroneInit(); DroneInit drone2 = new DroneInit(); - Tactic tactic = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), + TheoreticalTactic tactic = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), drone1); tactic.initializeTactics(); tactic.startTactic(); - Tactic tactic2 = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), + TheoreticalTactic tactic2 = TacticTesterHelper.getTactic(TheoreticalTactic.class, pubSub.getLeft(), pubSub.getRight(), drone2); tactic2.initializeTactics(); tactic2.startTactic(); for (int i = 0; i < 20; i++) { - try { - tactic.work(); - log.debug("do work 1"); - } catch (InterruptedException e) { - e.printStackTrace(); - } - try { - tactic2.work(); - log.debug("do work 2"); - } catch (InterruptedException e) { - e.printStackTrace(); - } + tactic.calculateTactics(); + tactic2.calculateTactics(); } Object leader1 = TacticTesterHelper.getField(tactic, "idLeader"); Object leader2 = TacticTesterHelper.getField(tactic2, "idLeader"); @@ -92,12 +83,7 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep tactic.stopTactic(); Thread.sleep(TheoreticalTactic.ttlLeader * 1000); for (int i = 0; i < 20; i++) { - try { - tactic2.work(); - log.debug("do work 2"); - } catch (InterruptedException e) { - e.printStackTrace(); - } + tactic2.calculateTactics(); } Object leader3 = TacticTesterHelper.getField(tactic2, "idLeader"); Assert.assertNotNull(leader3); From d7bbdf4879440bdda0f0bd8f395ef8d6db3c76fd Mon Sep 17 00:00:00 2001 From: Martijn Date: Sat, 2 Dec 2017 14:15:43 +0100 Subject: [PATCH 07/27] Utility now works for both shooting and moving. --- .../tactic/example/utility/TheoreticalTactic.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 7956b915..f5eb62b3 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -24,11 +24,11 @@ public class TheoreticalTactic extends Tactic { private DroneType droneType; private String idLeader; private Map> teammembers = new ConcurrentHashMap<>(); - private Map> mapOfTheWorld = new ConcurrentHashMap<>(); //TODO remove stale data + private Map> mapOfTheWorld = new ConcurrentHashMap<>(); private ManagedThread handleBroadcastMessagesThread; private HashMap targetMoveLocations = new HashMap<>(); private D3Vector myTargetMoveLocation; - private TimeoutTimer lastRequestForLeader = new TimeoutTimer(3000); //3 sec + private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec public final DroneType getType() { DroneType droneType; @@ -174,8 +174,7 @@ private void executeInstruction(InstructionMessage.InstructionType instructionTy if (hasComponents("gun")) { gun.fireBullet(targetLocation.toPoolCoordinate()); } else { - log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf - (targetLocation)); + log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf(targetLocation)); } break; case MOVE: @@ -184,8 +183,7 @@ private void executeInstruction(InstructionMessage.InstructionType instructionTy // new instruction. moveToLocation(targetLocation); } else { - log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf - (targetLocation)); + log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf(targetLocation)); } break; } @@ -357,7 +355,7 @@ public Integer calculateUtility(InstructionMessage.InstructionType type, D3Vecto Map.Entry> enemy = entry; if (type.equals(InstructionMessage.InstructionType.SHOOT)) { //Shooting at the closest enemy gives the highest utility - if (target.equals(enemy.getValue())) //If the target to shoot is at the same position as the enemy + if (target.equals(enemy.getValue().getRight())) //If the target to shoot is at the same position as the enemy utility += (MAX_ARENA_DISTANCE - target.distance_between(droneLocation)) * SHOOTING_WEIGHT; } else { double distanceToEnemy = enemy.getValue().getRight().distance_between(target); From 94c2ef6cd2b853513ecf25eb86286be97f17007b Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 7 Dec 2017 23:17:04 +0100 Subject: [PATCH 08/27] The tactic is working (kinda). It also includes a modification on the radar (it should only return locations not id's since a radar cannot actually see that). --- docker-compose.yml | 9 +- .../drone/components/gun/Gun.java | 2 +- .../drone/components/radar/Radar.java | 31 +-- .../drone/components/radio/Radio.java | 2 +- .../drone/tactic/DoNothingTactic.java | 18 ++ .../dronessimulator/drone/tactic/Tactic.java | 4 +- .../drone/tactic/example/SimpleTactic.java | 7 +- .../utility/CalculateUtilityHelper.java | 133 +++++++++++ .../example/utility/TheoreticalTactic.java | 222 ++++++++---------- .../utility/messages/RadarImageMessage.java | 6 +- .../utility/TheoreticalTacticTester.java | 26 +- .../gameengine/ruleprocessors/RuleSets.java | 5 +- 12 files changed, 294 insertions(+), 171 deletions(-) create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java diff --git a/docker-compose.yml b/docker-compose.yml index 84494a00..e8c69a3a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,8 +47,9 @@ services: rabbitmq: condition: service_healthy ports: - - "8000:8000" + - "8001:8000" environment: + DRONE_NAME: "drone-thales-1" DRONE_TEAM: "teamthales" DRONE_COMPONENTS: "gps,radar,radio" DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" @@ -58,7 +59,10 @@ services: depends_on: rabbitmq: condition: service_healthy + ports: + - "8002:8000" environment: + DRONE_NAME: "drone-thales-2" DRONE_TEAM: "teamthales" DRONE_COMPONENTS: "gps,radio,gun" DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" @@ -68,7 +72,10 @@ services: depends_on: rabbitmq: condition: service_healthy + ports: + - "8003:8000" environment: + DRONE_NAME: "drone-thales-3" DRONE_TEAM: "teamthales" DRONE_COMPONENTS: "gps,radio,gun" DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" diff --git a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java index e2b36aac..1d86ff1d 100644 --- a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java +++ b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java @@ -107,7 +107,7 @@ public void fireBullet(D3PolarCoordinate direction){ //Run all the callbacks callbacks.forEach(callback -> callback.run(msg)); - log.info("Firing bullet! Next shot possible in " + ((double) (nextShotAtMs - currentTimeMs) / 1000) + " seconds."); + log.info("Firing bullet in direction " + direction + "! Next shot possible in " + ((double) (nextShotAtMs - currentTimeMs) / 1000) + " seconds."); } } diff --git a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java index fc301cd7..ce0a0516 100644 --- a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java +++ b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java @@ -6,7 +6,6 @@ import lombok.Setter; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.architectureevents.ArchitectureEventController; -import org.inaetics.dronessimulator.common.Tuple; import org.inaetics.dronessimulator.common.architecture.SimulationAction; import org.inaetics.dronessimulator.common.architecture.SimulationState; import org.inaetics.dronessimulator.common.protocol.EntityType; @@ -20,7 +19,6 @@ import org.inaetics.dronessimulator.discovery.api.discoverynode.NodeEventHandler; import org.inaetics.dronessimulator.discovery.api.discoverynode.Type; import org.inaetics.dronessimulator.discovery.api.discoverynode.discoveryevent.RemovedNode; -import org.inaetics.dronessimulator.discovery.api.instances.DroneInstance; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; @@ -61,7 +59,7 @@ public class Radar implements MessageHandler { /** * Map of all last known entities and their positions (the first string is the id of the entity, the tuple's string is the team name if applicable and the D3Vector is the location) */ - private final ConcurrentHashMap> allEntities = new ConcurrentHashMap<>(); + private final Map allEntities = new ConcurrentHashMap<>(); /** * The range of this radar */ @@ -95,10 +93,7 @@ public void start() { this.m_subscriber.addHandler(KillMessage.class, this); m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> { - allEntities.clear(); - } - ); + (SimulationState fromState, SimulationAction action, SimulationState toState) -> allEntities.clear()); } /* @@ -110,14 +105,13 @@ public void start() { * * @return The entities in range */ - public List> getRadar() { - List> results; + public List getRadar() { + List results; if (position != null) { - results = allEntities.entrySet() + results = allEntities.values() .stream() - .map(Map.Entry::getValue) - .filter(drone -> position.distance_between(drone.getRight()) <= RADAR_RANGE) + .filter(drone -> position.distance_between(drone) <= RADAR_RANGE) .collect(Collectors.toList()); } else { results = Collections.emptyList(); @@ -129,16 +123,12 @@ public List> getRadar() { /** * Retrieves the nearest target in range * - * @return The nearest entity in range + * @return The nearest entity in range. Note that this could be a teammember */ - public Optional> getNearestTarget() { + public Optional getNearestTarget() { return getRadar() .stream() - .filter(drone -> - drone.getLeft() == null && this.m_drone.getTeamname() == null || - !drone.getLeft().equals(this.m_drone.getTeamname()) - ) - .sorted(Comparator.comparingDouble(e -> e.getRight().distance_between(position))) + .sorted(Comparator.comparingDouble(e -> e.distance_between(position))) .findFirst(); } @@ -169,8 +159,7 @@ private void handleStateMessage(StateMessage stateMessage){ } } else { if (stateMessage.getPosition().isPresent() && stateMessage.getType().equals(EntityType.DRONE)) { - Tuple droneModel = new Tuple<>(DroneInstance.getTeamname(m_discoverer, stateMessage.getIdentifier()), stateMessage.getPosition().get()); - this.allEntities.put(stateMessage.getIdentifier(), droneModel); + this.allEntities.put(stateMessage.getIdentifier(), stateMessage.getPosition().get()); } } } diff --git a/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java b/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java index b2d9ebda..1dfc61df 100644 --- a/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java +++ b/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java @@ -30,7 +30,7 @@ public class Radio implements MessageHandler { private volatile Publisher m_publisher; /** Reference to Drone Init bundle */ private volatile DroneInit m_drone; - /** Queue with received strings */ + /** Queue with received messages */ private ConcurrentLinkedQueue received_queue = new ConcurrentLinkedQueue<>(); private Topic topic; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java new file mode 100644 index 00000000..f51218ce --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java @@ -0,0 +1,18 @@ +package org.inaetics.dronessimulator.drone.tactic; + +public class DoNothingTactic extends Tactic { + @Override + protected void initializeTactics() { + + } + + @Override + protected void calculateTactics() { + //Do nothing :P + } + + @Override + protected void finalizeTactics() { + + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 83013b4b..36540db8 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -37,10 +37,10 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { public static final long tacticTimout = 1;//tck // drone components - protected volatile Radar radar; + public volatile Radar radar; public volatile GPS gps; public volatile Engine engine; - protected volatile Gun gun; + public volatile Gun gun; public volatile Radio radio; /** * Drone Init bundle diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java index d89eba9f..b867b123 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java @@ -1,7 +1,6 @@ package org.inaetics.dronessimulator.drone.tactic.example; import org.inaetics.dronessimulator.common.Settings; -import org.inaetics.dronessimulator.common.Tuple; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.Tactic; @@ -151,10 +150,10 @@ private void calculateAcceleration() { * Checks if a bullet can be fired by the gun. */ private void calculateGun() { - Optional> target = radar.getNearestTarget(); + Optional target = radar.getNearestTarget(); if (target.isPresent()) { - if (target.get().getRight().distance_between(gps.getPosition()) <= gun.getMaxDistance()) { - gun.fireBullet(target.get().getRight().sub(gps.getPosition()).toPoolCoordinate()); + if (target.get().distance_between(gps.getPosition()) <= gun.getMaxDistance()) { + gun.fireBullet(target.get().sub(gps.getPosition()).toPoolCoordinate()); } } } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java new file mode 100644 index 00000000..5358f11e --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -0,0 +1,133 @@ +package org.inaetics.dronessimulator.drone.tactic.example.utility; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j; +import org.inaetics.dronessimulator.common.Settings; +import org.inaetics.dronessimulator.common.Tuple; +import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.InstructionMessage; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + + +@Log4j +@RequiredArgsConstructor +public class CalculateUtilityHelper { + static final int MOVE_GENERATION_DELTA = 1; + private static final double MAX_ARENA_DISTANCE = new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length(); + private static final double SHOOTING_WEIGHT = 1; + private static final double MOVING_WEIGHT = 1; + private static final double MINIMAL_TEAM_DISTANCE = 15d; + private final CalculateUtilityParams params; + + public Integer calculateUtility() { + //Do some pre-checks first + if (params.type.equals(InstructionMessage.InstructionType.SHOOT) && !params.droneHasComponent("gun")) { + return -1; //We cannot shoot, so all utility should be negative + } + + if (params.type.equals(InstructionMessage.InstructionType.SHOOT) && params.target.equals(params.getDroneLocation())) { + return -1; //Do not shoot at yourself + } + + if (params.type.equals(InstructionMessage.InstructionType.MOVE) && !insideRange(D3Vector.ZERO, + new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT), params.target)) { + return -1; //We never want to move to a location that is out of the bounds of the game + } + + //Now really calculate the utility, no more early returns. + final int[] utility = {0}; //A single element array for lambdas as it eeds to be final. + //Drones that are close have a high likelyhood to kill you, so shoot if possible and move in the opposite direction + forEachEnemy(enemy -> { + if (params.type.equals(InstructionMessage.InstructionType.SHOOT)) { + //Shooting at the closest enemy gives the highest utility + if (params.target.equals(enemy.getValue())) //If the target to shoot is at the same position as the enemy + utility[0] += (MAX_ARENA_DISTANCE - params.target.distance_between(params.getDroneLocation())) * SHOOTING_WEIGHT; + } else { + double distanceToEnemy = enemy.getValue().distance_between(params.target); + if (params.droneHasComponent("gun")) { + //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if + // we move towards the enemy. + utility[0] += (MAX_ARENA_DISTANCE - distanceToEnemy) * MOVING_WEIGHT; + } else { + //We cannot shoot it, so evade it. + utility[0] += (distanceToEnemy * MOVING_WEIGHT); + } + } + }); + forEachTeammember(teammember -> { + if (!teammember.getKey().equals(params.droneId)) { //If the teammember is not the current drone + double distanceToTeammate = teammember.getValue().getLeft().distance_between(params.target); + if (params.type.equals(InstructionMessage.InstructionType.MOVE)) { + //we do not want to crash into a teammate + if (distanceToTeammate < MINIMAL_TEAM_DISTANCE) { + utility[0] = -1; + } else { + //Move with teammates over moving alone + utility[0] += (MAX_ARENA_DISTANCE - (int) distanceToTeammate) * MOVING_WEIGHT; + } + } else { + //Do not shoot at teammembers + if (distanceToTeammate < 1) { + utility[0] = -1; + } + } + } + }); + return utility[0]; //TODO + } + + private void forEachEnemy(Consumer> f) { + getEnemiesInWorld().entrySet().parallelStream().forEach(f); + } + + private void forEachTeammember(Consumer>>> f) { + params.teammembers.entrySet().parallelStream().forEach(f); + } + + private Map getEnemiesInWorld() { + return params.mapOfTheWorld.entrySet().parallelStream().filter(e -> !params.teammembers.containsKey(e.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getRight())); + } + + private boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vector testedLocation) { + return + //Check the x location + testedLocation.getX() > startRange.getX() && + testedLocation.getX() < endRange.getX() && + //Check the y location + testedLocation.getY() > startRange.getY() && + testedLocation.getY() < endRange.getY() && + //Check the z location + testedLocation.getZ() > startRange.getZ() && + testedLocation.getZ() < endRange.getZ(); + } + + + /** + * This is a data object that holds all the required parameters to calculate the utility of a move. This is useful to reduce the number of parameters given to the + * calculate utility function. This will hopefully make the call to the function more readable. + */ + @Data + @RequiredArgsConstructor + static class CalculateUtilityParams { + private final Map>> teammembers; + private final Map> mapOfTheWorld; + private final InstructionMessage.InstructionType type; + private final String droneId; + private final D3Vector target; + + D3Vector getDroneLocation() { + return mapOfTheWorld.get(droneId).getRight(); + } + + boolean droneHasComponent(String component) { + return teammembers.get(droneId).getRight().contains(component); + } + } +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index f5eb62b3..274dffcd 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -9,28 +9,33 @@ import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.*; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.IntStream; + +import static org.inaetics.dronessimulator.drone.tactic.example.utility.CalculateUtilityHelper.MOVE_GENERATION_DELTA; @Log4j @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { public static final long ttlLeader = 2; //seconds - private static final double MOVE_GENERATION_DELTA = 1.0; - private static final double SHOOTING_WEIGHT = 1; - private static final double MOVING_WEIGHT = 1; - private static final double MAX_ARENA_DISTANCE = new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length(); private DroneType droneType; private String idLeader; - private Map> teammembers = new ConcurrentHashMap<>(); + /** + * This is a list of the teammembers based on the heartbeat messages + *

+ * The key is the id of the drone + * The left item of the tuple is the location of the teammember + * The right list of the tuple is a list of available components + */ + private Map>> teammembers = new ConcurrentHashMap<>(); private Map> mapOfTheWorld = new ConcurrentHashMap<>(); private ManagedThread handleBroadcastMessagesThread; - private HashMap targetMoveLocations = new HashMap<>(); private D3Vector myTargetMoveLocation; private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec - public final DroneType getType() { + private DroneType getType() { DroneType droneType; if (hasComponents("radar", "radio")) { droneType = DroneType.RADAR; @@ -47,20 +52,20 @@ protected void initializeTactics() { droneType = getType(); handleBroadcastMessagesThread = new LambdaManagedThread(this::manageIncomingCommunication); handleBroadcastMessagesThread.startThread(); - switch (droneType) { - case GUN: - //Send a message if you fire a bullet - gun.registerCallback((fireBulletMessage) -> { - DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FiredBulletMessage); - shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); - shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); - shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); - radio.send(shotMessage.getMessage()); - }); - break; - case RADAR: - break; - } +// switch (droneType) { +// case GUN: +// //Send a message if you fire a bullet +// gun.registerCallback((fireBulletMessage) -> { +// DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FiredBulletMessage); +// shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); +// shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); +// shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); +// radio.send(shotMessage.getMessage()); +// }); +// break; +// case RADAR: +// break; +// } log.debug("Tactic initialized for drone with type " + droneType); } @@ -82,8 +87,10 @@ protected void initializeTactics() { protected void calculateTactics() { //Remove old data from the map mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), ttlLeader)); + teammembers.entrySet().removeIf(e -> + !mapOfTheWorld.containsKey(e.getKey()) + ); manageOutgoingCommunication(); -// moveToRandomLocation(); //Check leader if (!checkIfLeaderIsAlive()) { log.debug("Find a new leader, the leader was " + idLeader); @@ -137,12 +144,16 @@ private void manageIncomingCommunication() { if (newMessage != null) { log.debug("Received a message with type " + String.valueOf(newMessage.get("type"))); if (MyTacticMessage.checkType(newMessage, HeartbeatMessage.class)) { - teammembers.put(newMessage.get("id"), Arrays.asList(newMessage.get("components").split(","))); + teammembers.put(newMessage.get("id"), new Tuple<>(D3Vector.fromString(newMessage.get("position")), Arrays.asList(newMessage.get("components").split(",")))); mapOfTheWorld.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")))); } else if (MyTacticMessage.checkType(newMessage, RadarImageMessage.class)) { - RadarImageMessage.parseData(newMessage).forEach((k, v) -> mapOfTheWorld.put(k, new Tuple<>(LocalDateTime.now(), v))); + RadarImageMessage.parseData(newMessage).forEach((k, v) -> { + mapOfTheWorld.put(k, new Tuple<>(LocalDateTime.now(), v)); + }); } else if (MyTacticMessage.checkType(newMessage, InstructionMessage.class)) { + log.info("Received a message for {}, and I am {}", newMessage.get("receiver"), getIdentifier()); if (newMessage.get("receiver").equals(getIdentifier())) { + //TODO check if all instructions are properly executed executeInstruction(InstructionMessage.InstructionType.valueOf(newMessage.get(InstructionMessage.InstructionType.class.getSimpleName())), D3Vector.fromString(newMessage.get("target"))); } @@ -155,7 +166,6 @@ private void manageIncomingCommunication() { } else if (MyTacticMessage.checkType(newMessage, MyTacticMessage.MESSAGETYPES.IsLeaderMessage)) { setLeader(newMessage.get("id")); lastRequestForLeader.reset(); - targetMoveLocations = new HashMap<>(); } } } @@ -168,19 +178,18 @@ private boolean checkIfLeaderIsAlive() { } private void executeInstruction(InstructionMessage.InstructionType instructionType, D3Vector targetLocation) { - log.debug("Execute instruction " + instructionType + " with target " + String.valueOf(targetLocation)); + log.info("Execute instruction " + instructionType + " with target " + String.valueOf(targetLocation) + ". Current location: " + String.valueOf(gps.getPosition())); switch (instructionType) { case SHOOT: if (hasComponents("gun")) { - gun.fireBullet(targetLocation.toPoolCoordinate()); + gun.fireBullet(targetLocation.sub(gps.getPosition()).toPoolCoordinate()); } else { log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf(targetLocation)); } break; case MOVE: if (hasComponents("engine", "gps")) { - this.myTargetMoveLocation = targetLocation; //Store the target location to move there if there is no - // new instruction. + this.myTargetMoveLocation = targetLocation; //Store the target location to move there if there is no new instruction. moveToLocation(targetLocation); } else { log.error("Could not execute instruction " + instructionType + " with target " + String.valueOf(targetLocation)); @@ -190,6 +199,7 @@ private void executeInstruction(InstructionMessage.InstructionType instructionTy } private void moveToLocation(D3Vector location) { + //TODO replace with better move function D3Vector position = gps.getPosition(); log.info("Moving to " + location.toString() + " from " + position.toString()); if (position.distance_between(location) < 1) { @@ -220,6 +230,7 @@ private void moveToLocation(D3Vector location) { } private void findLeader() { + //TODO Make this quicker (or just move even without info about the leader) if (idLeader == null) { log.debug("Searching for leader"); radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SearchLeaderMessage).getMessage()); @@ -233,7 +244,7 @@ private void setLeader(String idLeader) { } private void randomShooting() { - //TODO + //TODO use mapoftheworld if there is anything present that is not a teammember log.debug("RANDOM SHOOTING AND MOVING!!!"); D3Vector randomLocation = new D3Vector(Math.random() * Settings.ARENA_WIDTH, Math.random() * Settings .ARENA_HEIGHT, Math.random() * Settings.ARENA_DEPTH); @@ -242,8 +253,23 @@ private void randomShooting() { } private void sendRadarimage() { - List> currentRadar = radar.getRadar(); - RadarImageMessage radarImageMessage = new RadarImageMessage(this, currentRadar); + List currentRadar = radar.getRadar(); + Map enhancedRadarImage = new ConcurrentHashMap<>(); + List unknownEntries = new LinkedList<>(); + //First map all teammembers so we known who are the enemies + currentRadar.parallelStream().forEach(location -> { + Optional>>> teammember = teammembers.entrySet().stream().filter(e -> e.getValue().getLeft().equals(location)).findFirst(); + if (teammember.isPresent()) { + enhancedRadarImage.put(teammember.get().getKey(), location); + } else { + if (!unknownEntries.contains(location)) { + unknownEntries.add(location); + } + } + }); + IntStream.range(0, unknownEntries.size()).parallel().forEach(i -> enhancedRadarImage.put("unknown_" + i, unknownEntries.get(i))); + + RadarImageMessage radarImageMessage = new RadarImageMessage(this, enhancedRadarImage); log.debug("sendRadarimage: " + radarImageMessage.getMessage().toString()); radio.send(radarImageMessage.getMessage()); } @@ -258,38 +284,36 @@ private void broadcastHeartbeat() { } private void sendInstructions() { - for (String teammember : teammembers.keySet()) { + teammembers.keySet().parallelStream().forEach(teammember -> { Map, Integer> utilityMapMove = new HashMap<>(); Map, Integer> utilityMapShoot = new HashMap<>(); //Generate utility calculations to move in any of the 26 directions - generateUtilityCalculations(utilityMapMove, teammember, InstructionMessage.InstructionType.MOVE); + generateUtilityCalculations(utilityMapMove, InstructionMessage.InstructionType.MOVE, teammember); //Generate utility for movement towards a different drone and shooting towards a drone - for (Map.Entry> enemy : mapOfTheWorld.entrySet()) { + mapOfTheWorld.entrySet().parallelStream().forEach(entry -> { utilityMapMove.put( - new Tuple<>(InstructionMessage.InstructionType.MOVE, enemy.getValue().getRight()), + new Tuple<>(InstructionMessage.InstructionType.MOVE, entry.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.MOVE, - mapOfTheWorld.get(teammember).getRight(), - enemy.getValue().getRight(), - teammembers.get(teammember) + teammember, + entry.getValue().getRight() ) ); - if (teammembers.get(teammember).contains("gun")) { + if (teammembers.get(teammember).getRight().contains("gun")) { utilityMapShoot.put( - new Tuple<>(InstructionMessage.InstructionType.SHOOT, enemy.getValue().getRight()), + new Tuple<>(InstructionMessage.InstructionType.SHOOT, entry.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.SHOOT, - mapOfTheWorld.get(teammember).getRight(), - enemy.getValue().getRight(), - teammembers.get(teammember) + teammember, + entry.getValue().getRight() ) ); } - } - Optional, Integer>> highestUtilityShoot = - utilityMapShoot.entrySet().stream().sorted((i2, i1) -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + }); + Optional, Integer>> highestUtilityShoot = + utilityMapShoot.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityShoot.isPresent() && highestUtilityShoot.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityShoot.get().getKey(); log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + @@ -298,38 +322,41 @@ private void sendInstructions() { radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); } - Optional, Integer>> highestUtilityMove = - utilityMapMove.entrySet().stream().sorted((i2, i1) -> Integer.compare(i1.getValue(), i2.getValue())).findFirst(); + Optional, Integer>> highestUtilityMove = + utilityMapMove.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityMove.isPresent() && highestUtilityMove.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityMove.get().getKey(); - log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + - highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityMove.get().getValue() + " out of " + Arrays.toString - (utilityMapMove.values().toArray())); radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); } - } + }); } - private void generateUtilityCalculations(Map, Integer> utilityMap, String teammember, InstructionMessage.InstructionType instructionType) { - D3Vector currentLocation = mapOfTheWorld.get(teammember).getRight(); - for (double ix = -MOVE_GENERATION_DELTA; ix <= MOVE_GENERATION_DELTA; ix += MOVE_GENERATION_DELTA) { - for (double iy = -MOVE_GENERATION_DELTA; iy <= MOVE_GENERATION_DELTA; iy += MOVE_GENERATION_DELTA) { - for (double iz = -MOVE_GENERATION_DELTA; iz <= MOVE_GENERATION_DELTA; iz += MOVE_GENERATION_DELTA) { - D3Vector targetLocation = currentLocation.add(new D3Vector(ix, iy, iz)); - utilityMap.put( - new Tuple<>(instructionType, targetLocation), - calculateUtility( - instructionType, - mapOfTheWorld.get(teammember).getRight(), - targetLocation, - teammembers.get(teammember) - ) - ); - } - } + public void generateUtilityCalculations(Map, Integer> utilityMap, InstructionMessage.InstructionType type, + String droneId) { + Tuple teammember = mapOfTheWorld.get(droneId); + if (teammember == null) { + log.error("Team member with id " + droneId + " not found in mapOfTheWorld: " + mapOfTheWorld.entrySet()); + return; } + D3Vector currentLocation = mapOfTheWorld.get(droneId).getRight(); + + IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(x -> + IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(y -> + IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(z -> { + D3Vector targetLocation = currentLocation.add(new D3Vector(x, y, z)); + utilityMap.put( + new Tuple<>(type, targetLocation), + calculateUtility(type, droneId, targetLocation) + ); + }) + ) + ); + } + int calculateUtility(InstructionMessage.InstructionType instructionType, String droneId, D3Vector target) { + CalculateUtilityHelper.CalculateUtilityParams params = new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, instructionType, droneId, target); + return new CalculateUtilityHelper(params).calculateUtility(); } private D3Vector calculateRandomPositionInField() { @@ -340,59 +367,6 @@ private D3Vector calculateRandomPositionInField() { ); } - public Integer calculateUtility(InstructionMessage.InstructionType type, D3Vector droneLocation, D3Vector target, List availableComponents) { - int utility = 0; - if (type.equals(InstructionMessage.InstructionType.SHOOT) && !availableComponents.contains("gun")) { - return -1; //We cannot shoot, so all utility should be negative - } - if (type.equals(InstructionMessage.InstructionType.MOVE) && !insideRange(D3Vector.ZERO, - new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT), target)) { - return -1; //We never want to move to a location that is out of the bounds of the game - } - //Drones that are close have a high likelyhood to kill you, so shoot if possible and move in the opposite direction - for (Map.Entry> entry : mapOfTheWorld.entrySet()) { - if (!teammembers.containsKey(entry.getKey())) { - Map.Entry> enemy = entry; - if (type.equals(InstructionMessage.InstructionType.SHOOT)) { - //Shooting at the closest enemy gives the highest utility - if (target.equals(enemy.getValue().getRight())) //If the target to shoot is at the same position as the enemy - utility += (MAX_ARENA_DISTANCE - target.distance_between(droneLocation)) * SHOOTING_WEIGHT; - } else { - double distanceToEnemy = enemy.getValue().getRight().distance_between(target); - if (availableComponents.contains("gun")) { - //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if - // we move towards the enemy. - utility += (MAX_ARENA_DISTANCE - distanceToEnemy) * MOVING_WEIGHT; - } else { - //We cannot shoot it, so evade it. - utility += (distanceToEnemy * MOVING_WEIGHT); - } - } - } else { - if (type.equals(InstructionMessage.InstructionType.MOVE)) { - //Move with teammates over moving alone - utility += (MAX_ARENA_DISTANCE - (int) entry.getValue().getRight().distance_between(droneLocation)) * MOVING_WEIGHT; - } - } - } - return utility; //TODO - } - - private boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vector testedLocation) { - return - //Check the x location - testedLocation.getX() > startRange.getX() && - testedLocation.getX() < endRange.getX() && - //Check the y location - testedLocation.getY() > startRange.getY() && - testedLocation.getY() < endRange.getY() && - //Check the z location - testedLocation.getZ() > startRange.getZ() && - testedLocation.getZ() < endRange.getZ(); - - - } - private enum DroneType { GUN, RADAR } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java index 6d3e0219..d29868e7 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java @@ -2,22 +2,20 @@ import lombok.AccessLevel; import lombok.Getter; -import org.inaetics.dronessimulator.common.Tuple; import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.Tactic; import java.util.HashMap; -import java.util.List; import java.util.Map; public class RadarImageMessage extends MyTacticMessage { @Getter(AccessLevel.PROTECTED) private Map data = new HashMap<>(); - public RadarImageMessage(Tactic tactic, List> radarImage) { + public RadarImageMessage(Tactic tactic, Map radarImage) { super(tactic); - radarImage.forEach(tup -> data.put(tup.getLeft(), tup.getRight().toString())); + radarImage.forEach((k, v) -> data.put(k, v.toString())); } public static Map parseData(TacticMessage rawMessage) { diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java index fdac997c..89b0dfcd 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java @@ -6,6 +6,9 @@ import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.protocol.TeamTopic; import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.components.engine.Engine; +import org.inaetics.dronessimulator.drone.components.gun.Gun; +import org.inaetics.dronessimulator.drone.components.radio.Radio; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; import org.inaetics.dronessimulator.drone.tactic.TacticTesterHelper; import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.HeartbeatMessage; @@ -18,7 +21,6 @@ import org.junit.Test; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -94,30 +96,32 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep @Test public void testCalculateUtility() throws NoSuchFieldException, IllegalAccessException { //Create the world - Map>> teammembers = new ConcurrentHashMap<>(); - Map mapOfTheWorld = new ConcurrentHashMap<>(); - mapOfTheWorld.put("enemyDrone", new D3Vector(100, 100, 50)); + Map>> teammembers = new ConcurrentHashMap<>(); + Map> mapOfTheWorld = new ConcurrentHashMap<>(); + mapOfTheWorld.put("enemyDrone", new Tuple<>(LocalDateTime.now(), new D3Vector(100, 100, 50))); setField(tactic, "teammembers", teammembers); setField(tactic, "mapOfTheWorld", mapOfTheWorld); + tactic.engine = new Engine(); + tactic.radio = new Radio(); //Move towards a drone since we have a gun +// CalculateUtilityHelper.CalculateUtilityParams test1Params = new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, InstructionMessage.InstructionType.MOVE, "1", new D3Vector(50, 50, 50)); tactic.gps.setPosition(D3Vector.UNIT); - int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(50, 50, 50), Arrays.asList("gun", - "engine", "radio", "radio")); + tactic.gun = new Gun(); + int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(50, 50, 50)); Assert.assertEquals((int) new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length() - (int) new D3Vector(50, 50, 0).length(), utility); //Do not move out of bounds tactic.gps.setPosition(D3Vector.UNIT); - utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(-1, -1, -1), Arrays.asList("gun", - "engine", "radio", "radio")); + utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(-1, -1, -1)); Assert.assertEquals(-1, utility); //Move away from a drone if you do not have a gun. The higher the distance, the better tactic.gps.setPosition(D3Vector.UNIT); - utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.gps.getPosition(), new D3Vector(50, 50, 50), Arrays.asList("engine", - "radio", "radio")); - Assert.assertEquals((int) new D3Vector(50, 50, 50).distance_between(mapOfTheWorld.get("enemyDrone")), utility); + tactic.gun = null; + utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(50, 50, 50)); + Assert.assertEquals((int) new D3Vector(50, 50, 50).distance_between(mapOfTheWorld.get("enemyDrone").getRight()), utility); // } diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java index bff70148..2248fc4f 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java @@ -20,6 +20,8 @@ public static List getRulesForGameMode(GameMode gameMode, Publisher publis result.add(new KillOutOfBounds()); result.add(new CollisionRule()); result.add(new KillEntitiesRule()); + result.add(new RemoveStrayBullets()); + result.add(new RemoveStaleStateData()); //Game mode specific rules switch (gameMode) { @@ -38,8 +40,7 @@ public static List getIntervalRulesForGameMode(GameMode gameMode, Publishe idMapper) { List result = new LinkedList<>(); //General rules that are applicable in any game mode - result.add(new RemoveStrayBullets()); - result.add(new RemoveStaleStateData()); + // None (yet) //Game mode specific rules switch (gameMode) { From f1df9cb5e7cce7818276301f7817b36137f34a9a Mon Sep 17 00:00:00 2001 From: Tim108 Date: Fri, 8 Dec 2017 00:49:13 +0100 Subject: [PATCH 09/27] just so it is online --- .../drone/components/engine/Engine.java | 11 ++- .../drone/tactic/Activator.java | 2 +- .../drone/tactic/BasicTactic.java | 93 ++++++++++--------- .../drone/tactic/BasicTacticTest.java | 61 ++++++++++++ 4 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index 73980b36..fff8b3e8 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -81,9 +81,12 @@ public D3Vector maximize_acceleration(D3Vector input) { private D3Vector limit_velocity(D3Vector input) { D3Vector output = input; // Check velocity - if (m_gps.getVelocity().length() >= Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() >= m_gps.getVelocity() - .length()) { + if (m_gps.getVelocity().length() >= Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() >= m_gps.getVelocity().length()) { output = new D3Vector(); + } else if (m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { + double diff = Settings.MAX_DRONE_VELOCITY - m_gps.getVelocity().length(); + double correctionFactor = diff / input.length(); + output = input.scale(correctionFactor); } return output; } @@ -118,7 +121,9 @@ public void changeAcceleration(D3Vector input_acceleration) { acceleration = this.limit_acceleration(acceleration); acceleration = this.limit_velocity(acceleration); - acceleration = this.stagnate_acceleration(acceleration); +// acceleration = this.stagnate_acceleration(acceleration); + + log.debug("TESTACC | " + input_acceleration.length() + " | " + acceleration.length() + " | " + ((acceleration.length() > Settings.MAX_DRONE_ACCELERATION || m_gps.getVelocity().add(acceleration).length() > Settings.MAX_DRONE_VELOCITY) ? 1 : 0)); if (Double.isNaN(acceleration.getX()) || Double.isNaN(acceleration.getY()) || Double.isNaN(acceleration.getZ())) { throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java index 21b12bce..ba7a3240 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java @@ -25,7 +25,7 @@ public class Activator extends DependencyActivatorBase { private static final Logger logger = Logger.getLogger(Activator.class); @Override public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception { - Tactic tactic = new MoveToLocationTactic(); + Tactic tactic = new BasicTactic(); Component component = createComponent() .setInterface(Tactic.class.getName(), null) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java index 9f15124b..4bf2a0d9 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java @@ -17,6 +17,7 @@ public class BasicTactic extends Tactic { private static final int DRONE_TIMEOUT = 2; // seconds private final TimeoutTimer tacticTimer = new TimeoutTimer(1000); //ms D3Vector moveTarget = null; + D3Vector lastMoveTarget = null; D3Vector attackTarget = null; Map radarDrones = new HashMap<>(); Map gunDrones = new HashMap<>(); @@ -30,6 +31,36 @@ public class BasicTactic extends Tactic { private D3Vector lastPosition; private D3Vector lastAttackTarget; + protected static D3Vector calculateMovement(D3Vector position, D3Vector target, D3Vector velocity) { + + double distance = position.distance_between(target); + + // stationary, on target + if (distance < 1 && velocity.length() < 1) { + System.out.println("TEST22 | on target | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + new D3Vector().length()); + return new D3Vector(); + } + + // stationary/not stationary, not on target, accelerating + else if (distance > ((velocity.length() * velocity.length()) / (2 * Settings.MAX_DRONE_ACCELERATION))) { + D3Vector newAcceleration = target.sub(position); + System.out.println("TEST22 | accelerating | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + newAcceleration.length()); + return newAcceleration; + } + + // not stationary, not on target, decelerating + else if (distance != 0) { + + double acceleration = -(velocity.length() * velocity.length()) / (2 * distance); + D3Vector newAcceleration = velocity.normalize().scale(acceleration); + + System.out.println("TEST22 | decelerating | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + new D3Vector().length()); + return newAcceleration; + } + log.debug("field size = " + new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT)); + return new D3Vector(); + } + @Override protected void initializeTactics() { log.info("Initializing tactics.."); @@ -52,24 +83,24 @@ protected void initializeTactics() { // } } - @Override protected void calculateTactics() { - if (tacticTimer.timeIsExceeded()) { - tacticTimer.reset(); - updateTactics(); - } +// if (tacticTimer.timeIsExceeded()) { +// tacticTimer.reset(); +// updateTactics(); +// } if (moveTarget == null) { moveTarget = new D3Vector(ThreadLocalRandom.current().nextInt(100, 300), ThreadLocalRandom.current().nextInt(100, 300), ThreadLocalRandom.current().nextInt(100, 300)); + log.debug("Initialized move target to: " + moveTarget); } - calculateMovement(); + engine.changeAcceleration(calculateMovement(gps.getPosition(), moveTarget, gps.getVelocity())); - if (isRadar && (lastPosition == null || gps.getPosition().distance_between(lastPosition) > 1)) { - organizeMovement(); - lastPosition = gps.getPosition(); - } +// if (isRadar && (lastMoveTarget == null || !lastMoveTarget.equals(moveTarget))) { +// organizeMovement(); +// lastMoveTarget = moveTarget; +// } } @Override @@ -102,40 +133,12 @@ private void organize() { } - private void calculateMovement() { - - D3Vector position = gps.getPosition(); - log.debug("distance to target = " + position.distance_between(moveTarget)); - - double distance = position.distance_between(moveTarget); - double velocity = gps.getVelocity().length(); - - // stationary, on target - if (distance < 1 && velocity < 1) { - log.debug("TEST21 - " + position.toString() + " - doing nothing.. "); - engine.changeAcceleration(new D3Vector()); - } - - // stationary/not stationary, not on target, accelerating - else if (distance > ((velocity * velocity) / (2 * Settings.MAX_DRONE_ACCELERATION))) { - D3Vector newAcceleration = moveTarget.sub(position).scale(0.5); - log.debug("TEST21 - " + position.toString() + " - accelerating.. " + newAcceleration); - engine.changeAcceleration(newAcceleration); - } - - // not stationary, not on target, decelerating - else if (distance != 0) { - - double acceleration = -(velocity * velocity) / (2 * distance); - D3Vector newAcceleration = (gps.getVelocity()).normalize().scale(acceleration); - log.debug(String.format("CALCULOG d=%f, v=%f, a=%f", distance, velocity, acceleration)); + private void organizeMovement() { - log.debug("TEST21 - " + position.toString() + " - decelerating.. " + newAcceleration); - engine.changeAcceleration(newAcceleration); + D3Vector moveFocus = gps.getPosition(); + if (moveTarget != null) { + moveFocus = moveTarget; } - } - - private void organizeMovement() { int number = myGunDrones.size(); log.debug("number is " + number); @@ -144,9 +147,9 @@ private void organizeMovement() { int numberSpawned = 0; for (String id : myGunDrones) { - D3Vector gunPosition = new D3Vector(Math.cos(spawnAngle * numberSpawned) * spawnRadius + gps.getPosition().getX() - , Math.sin(spawnAngle * numberSpawned) * spawnRadius + gps.getPosition().getY() - , gps.getPosition().getZ()); + D3Vector gunPosition = new D3Vector(Math.cos(spawnAngle * numberSpawned) * spawnRadius + moveFocus.getX() + , Math.sin(spawnAngle * numberSpawned) * spawnRadius + moveFocus.getY() + , moveFocus.getZ()); numberSpawned++; comm.sendMessage(id, ProtocolTags.MOVE, gunPosition); log.debug("gundrone " + id + " target location set to " + gunPosition); diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java new file mode 100644 index 00000000..badc83ba --- /dev/null +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java @@ -0,0 +1,61 @@ +package org.inaetics.dronessimulator.drone.tactic; + +import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.junit.Assert; +import org.junit.Test; + +public class BasicTacticTest { + + @Test + public void testCalculateMovement() { + + // accelerate towards target from stationary + D3Vector pos1 = new D3Vector(0, 0, 0); + D3Vector tar1 = new D3Vector(200, 0, 0); + D3Vector vel1 = new D3Vector(0, 0, 0); + + D3Vector res1 = BasicTactic.calculateMovement(pos1, tar1, vel1); + Assert.assertEquals(new D3Vector(200, 0, 0), res1); + + // accelerate towards target from moving + D3Vector pos2 = new D3Vector(0, 0, 0); + D3Vector tar2 = new D3Vector(200, 0, 0); + D3Vector vel2 = new D3Vector(20, 0, 0); + + D3Vector res2 = BasicTactic.calculateMovement(pos2, tar2, vel2); + Assert.assertEquals(new D3Vector(200, 0, 0), res2); + + // decelerate from moving to moving + D3Vector pos3 = new D3Vector(180, 0, 0); + D3Vector tar3 = new D3Vector(200, 0, 0); + D3Vector vel3 = new D3Vector(20, 0, 0); + + D3Vector res3 = BasicTactic.calculateMovement(pos3, tar3, vel3); + Assert.assertEquals(new D3Vector(-10, 0, 0), res3); + + // decelerate from moving to stationary + D3Vector pos4 = new D3Vector(199, 0, 0); + D3Vector tar4 = new D3Vector(200, 0, 0); + D3Vector vel4 = new D3Vector(1, 0, 0); + + D3Vector res4 = BasicTactic.calculateMovement(pos4, tar4, vel4); + Assert.assertEquals(new D3Vector(1, 0, 0), res4); + + // stand still on target + D3Vector pos5 = new D3Vector(50, 0, 0); + D3Vector tar5 = new D3Vector(50.9, 0, 0); + D3Vector vel5 = new D3Vector(0, 0, 0); + + D3Vector res5 = BasicTactic.calculateMovement(pos5, tar5, vel5); + Assert.assertEquals(new D3Vector(0, 0, 0), res5); + + // move just outside of target + D3Vector pos6 = new D3Vector(51, 0, 0); + D3Vector tar6 = new D3Vector(50, 0, 0); + D3Vector vel6 = new D3Vector(1, 0, 0); + + D3Vector res6 = BasicTactic.calculateMovement(pos6, tar6, vel6); + Assert.assertEquals(new D3Vector(0, 0, 0), res6); + } + +} \ No newline at end of file From e191e9d9860cc999c2f80691ea32d577cec75f7c Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 8 Dec 2017 11:48:12 +0100 Subject: [PATCH 10/27] Did some cleanup and added documentation --- .../dronessimulator/common/TimeoutTimer.java | 17 ++- .../common/TimeoutTimerTest.java | 38 +++++ .../drone/tactic/DoNothingTactic.java | 4 +- .../dronessimulator/drone/tactic/Tactic.java | 44 ++++-- .../utility/CalculateUtilityHelper.java | 60 ++++---- .../example/utility/TheoreticalTactic.java | 139 +++++++++--------- .../utility/messages/MyTacticMessage.java | 19 ++- .../example/MoveToLocationTacticTest.java | 12 +- .../utility/TheoreticalTacticTester.java | 20 +-- 9 files changed, 217 insertions(+), 136 deletions(-) create mode 100644 implementation/common/src/test/java/org/inaetics/dronessimulator/common/TimeoutTimerTest.java diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/TimeoutTimer.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/TimeoutTimer.java index 6465bea3..420e14d3 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/TimeoutTimer.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/TimeoutTimer.java @@ -19,7 +19,6 @@ public class TimeoutTimer { * @return true if the timeout has been exceeded, false otherwise. */ public static boolean isTimeExceeded(long startTime, double timeout) { - return (startTime + timeout) < System.currentTimeMillis(); } @@ -34,6 +33,22 @@ public static boolean isTimeExceeded(LocalDateTime startTime, long timeout) { return startTime.plusSeconds(timeout).isBefore(LocalDateTime.now()); } + /** + * Check if a timeout has exceeded since the given starttime + * + * @param startTime a LocalDateTime object with the time since when the timeout must be counted + * @param timeout a timeout in seconds (note that it will be compared up-to nano second accuracy). + * @return true if the timeout has been exceeded, false otherwise. + */ + public static boolean isTimeExceeded(LocalDateTime startTime, double timeout) { + LocalDateTime test = startTime.plusNanos((long) (timeout * 1e9)); + LocalDateTime nu = LocalDateTime.now(); + return test.isBefore(nu); + } + + /** + * Reset the timer. You must call this everytime a new measurement should start. + */ public synchronized void reset() { lastTime = System.currentTimeMillis(); } diff --git a/implementation/common/src/test/java/org/inaetics/dronessimulator/common/TimeoutTimerTest.java b/implementation/common/src/test/java/org/inaetics/dronessimulator/common/TimeoutTimerTest.java new file mode 100644 index 00000000..ffbd54fb --- /dev/null +++ b/implementation/common/src/test/java/org/inaetics/dronessimulator/common/TimeoutTimerTest.java @@ -0,0 +1,38 @@ +package org.inaetics.dronessimulator.common; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDateTime; + +public class TimeoutTimerTest { + @Test + public void isTimeExceeded() throws Exception { + long testDelta = 1000; + long oldTime = System.currentTimeMillis() - testDelta; + + Assert.assertTrue(TimeoutTimer.isTimeExceeded(oldTime, testDelta * 0.5)); + Assert.assertFalse(TimeoutTimer.isTimeExceeded(oldTime, testDelta * 2)); + } + + @Test + public void isTimeExceeded1() throws Exception { + long testDelta = 1000; + LocalDateTime oldTime = LocalDateTime.now().minusSeconds(testDelta); + + //Use long as timeout + Assert.assertTrue(TimeoutTimer.isTimeExceeded(oldTime, (long) (testDelta * 0.5))); + Assert.assertFalse(TimeoutTimer.isTimeExceeded(oldTime, testDelta * 2)); + + //Use double as timeout + Assert.assertTrue(TimeoutTimer.isTimeExceeded(oldTime, (testDelta * 0.5))); + Assert.assertFalse(TimeoutTimer.isTimeExceeded(oldTime, testDelta * 2.0)); + + //A very small number + double testDeltaDouble = 0.005; + oldTime = LocalDateTime.now().minusNanos((long) (testDeltaDouble * 1e9)); + Assert.assertTrue(TimeoutTimer.isTimeExceeded(oldTime, (testDeltaDouble * 0.5))); + Assert.assertFalse(TimeoutTimer.isTimeExceeded(oldTime, testDeltaDouble * 2.0)); + } + +} \ No newline at end of file diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java index f51218ce..fe354f8e 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/DoNothingTactic.java @@ -3,7 +3,7 @@ public class DoNothingTactic extends Tactic { @Override protected void initializeTactics() { - + //Do nothing :P } @Override @@ -13,6 +13,6 @@ protected void calculateTactics() { @Override protected void finalizeTactics() { - + //Do nothing :P } } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 36540db8..0a4d3a8a 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -1,6 +1,7 @@ package org.inaetics.dronessimulator.drone.tactic; +import lombok.Getter; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.architectureevents.ArchitectureEventController; import org.inaetics.dronessimulator.common.ManagedThread; @@ -35,33 +36,41 @@ */ @Log4j public abstract class Tactic extends ManagedThread implements MessageHandler { - public static final long tacticTimout = 1;//tck + private static final long TACTIC_TIMOUT = 1;//tck + private final TimeoutTimer workTimoutTimer = new TimeoutTimer(TACTIC_TIMOUT * Settings.TICK_TIME); // drone components - public volatile Radar radar; - public volatile GPS gps; - public volatile Engine engine; - public volatile Gun gun; - public volatile Radio radio; + @Getter + protected volatile Radar radar; + @Getter + protected volatile GPS gps; + @Getter + protected volatile Engine engine; + @Getter + protected volatile Gun gun; /** * Drone Init bundle */ protected volatile DroneInit m_drone; + @Getter + protected volatile Radio radio; /** * Architecture Event controller bundle */ + @SuppressWarnings("unused") //Assigned through OSGi private volatile ArchitectureEventController m_architectureEventController; /** * Subscriber bundle */ + @SuppressWarnings("unused") //Assigned through OSGi private volatile Subscriber m_subscriber; + private Instance simulationInstance; + private boolean registered = false; + private AtomicBoolean initialized = new AtomicBoolean(false); /** * Discoverer bundle */ + @SuppressWarnings("unused") //Assigned through OSGi private volatile Discoverer m_discoverer; - private Instance simulationInstance; - private boolean registered = false; - private AtomicBoolean initialized = new AtomicBoolean(false); - private final TimeoutTimer workTimoutTimer = new TimeoutTimer(tacticTimout*Settings.TICK_TIME); private final TimeoutTimer ticker = new TimeoutTimer(Settings.TICK_TIME); /** @@ -243,9 +252,9 @@ private void handleKillMessage(KillMessage killMessage) { "\tacceleration: " + gps.getAcceleration().toString() + "\n" ); this.stopSimulation(); -// if (!log.isDebugEnabled()){ -// System.exit(10); -// } + if (!log.isDebugEnabled()) { + System.exit(10); + } } } @@ -285,6 +294,11 @@ protected final void validateRequiredComponents(String... requiredComponents) th } + /** + * Checks if the components are non-null. + * + * @return returns a set with all available components. + */ public final Set getAvailableComponents() { Set componentlist = new HashSet<>(); if (radar != null) { @@ -317,6 +331,10 @@ public final Set getAvailableComponents() { */ protected abstract void calculateTactics(); + + /** + * This method MIGHT be called when the drone is killed. This is not a guarantee. Use this to stop spawned threads to avoid infinite threads for each restart. + */ protected abstract void finalizeTactics(); public class MissingComponentsException extends Exception { diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index 5358f11e..86a28ce6 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -12,8 +12,6 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.stream.Collectors; - @Log4j @RequiredArgsConstructor @@ -25,6 +23,32 @@ public class CalculateUtilityHelper { private static final double MINIMAL_TEAM_DISTANCE = 15d; private final CalculateUtilityParams params; + private static boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vector testedLocation) { + return + //Check the x location + testedLocation.getX() > startRange.getX() && + testedLocation.getX() < endRange.getX() && + //Check the y location + testedLocation.getY() > startRange.getY() && + testedLocation.getY() < endRange.getY() && + //Check the z location + testedLocation.getZ() > startRange.getZ() && + testedLocation.getZ() < endRange.getZ(); + } + + /** + * Really calculate the utility here. It is based on the following statements + * - If we do not have a gun, we cannot shoot -> utility becomes -1 + * - Do not shoot yourself -> utility becomes -1 + * - Do not move to a location that is outside the game area -> utility becomes -1 + * - We would like to shoot the enemy that is closest. + * - We would like to move away from enemies if we cannot shoot it. + * - We would like to move towards enemies if we can shoot it because it will improve our accuracy. + * - We would like to move close to a team member, but not fly into it. + * - We do NOT want to shoot team members. + * + * @return the utility as an integer + */ public Integer calculateUtility() { //Do some pre-checks first if (params.type.equals(InstructionMessage.InstructionType.SHOOT) && !params.droneHasComponent("gun")) { @@ -46,10 +70,10 @@ public Integer calculateUtility() { forEachEnemy(enemy -> { if (params.type.equals(InstructionMessage.InstructionType.SHOOT)) { //Shooting at the closest enemy gives the highest utility - if (params.target.equals(enemy.getValue())) //If the target to shoot is at the same position as the enemy + if (params.target.equals(enemy.getRight())) //If the target to shoot is at the same position as the enemy utility[0] += (MAX_ARENA_DISTANCE - params.target.distance_between(params.getDroneLocation())) * SHOOTING_WEIGHT; } else { - double distanceToEnemy = enemy.getValue().distance_between(params.target); + double distanceToEnemy = enemy.getRight().distance_between(params.target); if (params.droneHasComponent("gun")) { //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if // we move towards the enemy. @@ -79,36 +103,20 @@ public Integer calculateUtility() { } } }); - return utility[0]; //TODO + return utility[0]; } - private void forEachEnemy(Consumer> f) { - getEnemiesInWorld().entrySet().parallelStream().forEach(f); + private void forEachEnemy(Consumer> f) { + params.mapOfTheWorld.entrySet().parallelStream() + .filter(e -> !params.teammembers.containsKey(e.getKey())) + .map(e -> new Tuple<>(e.getKey(), e.getValue().getRight())) + .forEach(f); } private void forEachTeammember(Consumer>>> f) { params.teammembers.entrySet().parallelStream().forEach(f); } - private Map getEnemiesInWorld() { - return params.mapOfTheWorld.entrySet().parallelStream().filter(e -> !params.teammembers.containsKey(e.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getRight())); - } - - private boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vector testedLocation) { - return - //Check the x location - testedLocation.getX() > startRange.getX() && - testedLocation.getX() < endRange.getX() && - //Check the y location - testedLocation.getY() > startRange.getY() && - testedLocation.getY() < endRange.getY() && - //Check the z location - testedLocation.getZ() > startRange.getZ() && - testedLocation.getZ() < endRange.getZ(); - } - - /** * This is a data object that holds all the required parameters to calculate the utility of a move. This is useful to reduce the number of parameters given to the * calculate utility function. This will hopefully make the call to the function more readable. diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 274dffcd..adc9fa99 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -9,6 +9,8 @@ import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.*; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; @@ -19,7 +21,7 @@ @Log4j @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { - public static final long ttlLeader = 2; //seconds + public static final double ttlLeader = 3 * Settings.getTickTime(ChronoUnit.SECONDS); //seconds private DroneType droneType; private String idLeader; /** @@ -33,7 +35,7 @@ public class TheoreticalTactic extends Tactic { private Map> mapOfTheWorld = new ConcurrentHashMap<>(); private ManagedThread handleBroadcastMessagesThread; private D3Vector myTargetMoveLocation; - private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec + private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec //TODO check if this is really needed private DroneType getType() { DroneType droneType; @@ -52,20 +54,20 @@ protected void initializeTactics() { droneType = getType(); handleBroadcastMessagesThread = new LambdaManagedThread(this::manageIncomingCommunication); handleBroadcastMessagesThread.startThread(); -// switch (droneType) { -// case GUN: -// //Send a message if you fire a bullet -// gun.registerCallback((fireBulletMessage) -> { -// DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FiredBulletMessage); -// shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); -// shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); -// shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); -// radio.send(shotMessage.getMessage()); -// }); -// break; -// case RADAR: -// break; -// } + switch (droneType) { + case GUN: + //Send a message if you fire a bullet + gun.registerCallback((fireBulletMessage) -> { + DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FIRED_BULLET_MESSAGE); + shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); + shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); + shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); + radio.send(shotMessage.getMessage()); + }); + break; + case RADAR: + break; + } log.debug("Tactic initialized for drone with type " + droneType); } @@ -85,12 +87,12 @@ protected void initializeTactics() { */ @Override protected void calculateTactics() { - //Remove old data from the map + //Remove stale data from the map mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), ttlLeader)); - teammembers.entrySet().removeIf(e -> - !mapOfTheWorld.containsKey(e.getKey()) - ); + teammembers.entrySet().removeIf(e -> !mapOfTheWorld.containsKey(e.getKey())); + manageOutgoingCommunication(); + //Check leader if (!checkIfLeaderIsAlive()) { log.debug("Find a new leader, the leader was " + idLeader); @@ -107,6 +109,8 @@ protected void calculateTactics() { } else if (idLeader.equals(getIdentifier())) { sendInstructions(); } + + //Keep moving if we have a target move location. Otherwise we will overshoot our target since acceleration corrections are required. if (myTargetMoveLocation != null) { moveToLocation(myTargetMoveLocation); } @@ -135,46 +139,48 @@ private void manageOutgoingCommunication() { } /** - * This method handles all the required incoming communication. The method is called from within a thread. All - * outgoing communication should be done in manageOutgoingCommunication. + * This method handles all the required incoming communication. The method is called from within a thread. + * All outgoing communication should be done in manageOutgoingCommunication. */ private void manageIncomingCommunication() { if (radio.getMessages().size() > 0) { TacticMessage newMessage = radio.getMessage(TacticMessage.class); if (newMessage != null) { log.debug("Received a message with type " + String.valueOf(newMessage.get("type"))); - if (MyTacticMessage.checkType(newMessage, HeartbeatMessage.class)) { + //@formatter:off + if (MyTacticMessage.checkType(newMessage, + HeartbeatMessage.class)) { teammembers.put(newMessage.get("id"), new Tuple<>(D3Vector.fromString(newMessage.get("position")), Arrays.asList(newMessage.get("components").split(",")))); mapOfTheWorld.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")))); - } else if (MyTacticMessage.checkType(newMessage, RadarImageMessage.class)) { - RadarImageMessage.parseData(newMessage).forEach((k, v) -> { - mapOfTheWorld.put(k, new Tuple<>(LocalDateTime.now(), v)); - }); - } else if (MyTacticMessage.checkType(newMessage, InstructionMessage.class)) { - log.info("Received a message for {}, and I am {}", newMessage.get("receiver"), getIdentifier()); + } else if (MyTacticMessage.checkType(newMessage, + RadarImageMessage.class)) { + RadarImageMessage.parseData(newMessage).entrySet().parallelStream(). + forEach((e) -> mapOfTheWorld.put(e.getKey(), new Tuple<>(LocalDateTime.now(), e.getValue()))); + } else if (MyTacticMessage.checkType(newMessage, + InstructionMessage.class)) { if (newMessage.get("receiver").equals(getIdentifier())) { - //TODO check if all instructions are properly executed executeInstruction(InstructionMessage.InstructionType.valueOf(newMessage.get(InstructionMessage.InstructionType.class.getSimpleName())), D3Vector.fromString(newMessage.get("target"))); } - } else if (MyTacticMessage.checkType(newMessage, MyTacticMessage.MESSAGETYPES.SearchLeaderMessage) && - droneType.equals(DroneType.RADAR) && idLeader == null) { + } else if (MyTacticMessage.checkType(newMessage, + MyTacticMessage.MESSAGETYPES.SEARCH_LEADER_MESSAGE + ) && droneType.equals(DroneType.RADAR) && idLeader == null) { //Become a leader yourself and prepare setLeader(getIdentifier()); - radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.IsLeaderMessage).getMessage()); + radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.IS_LEADER_MESSAGE).getMessage()); log.info("Drone " + getIdentifier() + " is the leader of team " + m_drone.getTeamname()); - } else if (MyTacticMessage.checkType(newMessage, MyTacticMessage.MESSAGETYPES.IsLeaderMessage)) { + } else if (MyTacticMessage.checkType(newMessage, + MyTacticMessage.MESSAGETYPES.IS_LEADER_MESSAGE)) { setLeader(newMessage.get("id")); lastRequestForLeader.reset(); } + //@formatter:on } } } private boolean checkIfLeaderIsAlive() { - LocalDateTime testingTime = LocalDateTime.now(); - return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(testingTime, - ttlLeader); + return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(LocalDateTime.now(), ttlLeader); } private void executeInstruction(InstructionMessage.InstructionType instructionType, D3Vector targetLocation) { @@ -233,7 +239,7 @@ private void findLeader() { //TODO Make this quicker (or just move even without info about the leader) if (idLeader == null) { log.debug("Searching for leader"); - radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SearchLeaderMessage).getMessage()); + radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SEARCH_LEADER_MESSAGE).getMessage()); lastRequestForLeader.reset(); } } @@ -284,29 +290,29 @@ private void broadcastHeartbeat() { } private void sendInstructions() { - teammembers.keySet().parallelStream().forEach(teammember -> { + teammembers.entrySet().parallelStream().forEach(teammember -> { Map, Integer> utilityMapMove = new HashMap<>(); Map, Integer> utilityMapShoot = new HashMap<>(); //Generate utility calculations to move in any of the 26 directions - generateUtilityCalculations(utilityMapMove, InstructionMessage.InstructionType.MOVE, teammember); + generateUtilityCalculations(utilityMapMove, InstructionMessage.InstructionType.MOVE, teammember.getKey()); //Generate utility for movement towards a different drone and shooting towards a drone mapOfTheWorld.entrySet().parallelStream().forEach(entry -> { utilityMapMove.put( new Tuple<>(InstructionMessage.InstructionType.MOVE, entry.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.MOVE, - teammember, + teammember.getKey(), entry.getValue().getRight() ) ); - if (teammembers.get(teammember).getRight().contains("gun")) { + if (teammember.getValue().getRight().contains("gun")) { utilityMapShoot.put( new Tuple<>(InstructionMessage.InstructionType.SHOOT, entry.getValue().getRight()), calculateUtility( InstructionMessage.InstructionType.SHOOT, - teammember, + teammember.getKey(), entry.getValue().getRight() ) ); @@ -316,57 +322,50 @@ private void sendInstructions() { utilityMapShoot.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityShoot.isPresent() && highestUtilityShoot.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityShoot.get().getKey(); - log.info("sendInstructions type: " + highesUtilityParams.getLeft() + ", to " + teammember + "location: " + - highesUtilityParams.getRight().toString() + ", because its utility was " + highestUtilityShoot.get().getValue() + " out of " + Arrays.toString - (utilityMapShoot.values().toArray())); - radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); + radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember.getKey(), highesUtilityParams.getRight()).getMessage()); } Optional, Integer>> highestUtilityMove = utilityMapMove.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityMove.isPresent() && highestUtilityMove.get().getValue() > 0) { Tuple highesUtilityParams = highestUtilityMove.get().getKey(); - radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember, highesUtilityParams.getRight()).getMessage()); + radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember.getKey(), highesUtilityParams.getRight()).getMessage()); } }); } - public void generateUtilityCalculations(Map, Integer> utilityMap, InstructionMessage.InstructionType type, - String droneId) { - Tuple teammember = mapOfTheWorld.get(droneId); - if (teammember == null) { - log.error("Team member with id " + droneId + " not found in mapOfTheWorld: " + mapOfTheWorld.entrySet()); - return; - } + /** + * Use three for loops to walk over all neighboring positions of the current position and calculate the utility for that location. + * + * @param utilityMap the map to which the utility is added + * @param type the instruction type for which we need to calculate the utility + * @param droneId the id of the drone for which we will generate all this + */ + private void generateUtilityCalculations(Map, Integer> utilityMap, InstructionMessage.InstructionType type, + String droneId) { D3Vector currentLocation = mapOfTheWorld.get(droneId).getRight(); IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(x -> IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(y -> - IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(z -> { - D3Vector targetLocation = currentLocation.add(new D3Vector(x, y, z)); - utilityMap.put( - new Tuple<>(type, targetLocation), - calculateUtility(type, droneId, targetLocation) - ); - }) + IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel() + .mapToObj(z -> currentLocation.add(new D3Vector(x, y, z))) + .forEach(targetLocation -> utilityMap.put( + new Tuple<>(type, targetLocation), + calculateUtility(type, droneId, targetLocation) + )) ) ); } + /** + * Helper function that wraps the parameters into an object and calls the calculate utility on the correct object. + */ int calculateUtility(InstructionMessage.InstructionType instructionType, String droneId, D3Vector target) { CalculateUtilityHelper.CalculateUtilityParams params = new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, instructionType, droneId, target); return new CalculateUtilityHelper(params).calculateUtility(); } - private D3Vector calculateRandomPositionInField() { - return new D3Vector( - (Math.random() * (Settings.ARENA_WIDTH - 200) + 100), - (Math.random() * (Settings.ARENA_DEPTH - 200) + 100), - (Math.random() * (Settings.ARENA_HEIGHT - 200) + 100) - ); - } - private enum DroneType { GUN, RADAR } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java index 90e9249e..c6fdbf1d 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/MyTacticMessage.java @@ -20,7 +20,7 @@ public static boolean checkType(TacticMessage newMes return messageClass.getSimpleName().equals(newMessage.get("type")); } - public static boolean checkType(TacticMessage newMessage, String messageType) { + public static boolean checkType(TacticMessage newMessage, String messageType) { return messageType != null && messageType.equals(newMessage.get("type")); } @@ -55,12 +55,15 @@ protected String getType() { } public static class MESSAGETYPES { - public static final String HeartbeatMessage = HeartbeatMessage.class.getSimpleName(); - public static final String RadarImageMessage = RadarImageMessage.class.getSimpleName(); - public static final String InstructionMessage = InstructionMessage.class.getSimpleName(); - public static final String FiredBulletMessage = "FiredBulletMessage"; - public static final String SearchLeaderMessage = "SearchLeaderMessage"; - public static final String IsLeaderMessage = "IsLeaderMessage"; + public static final String HEARTBEAT_MESSAGE = HeartbeatMessage.class.getSimpleName(); + public static final String RADAR_IMAGE_MESSAGE = RadarImageMessage.class.getSimpleName(); + public static final String INSTRUCTION_MESSAGE = InstructionMessage.class.getSimpleName(); + public static final String FIRED_BULLET_MESSAGE = "FIRED_BULLET_MESSAGE"; + public static final String SEARCH_LEADER_MESSAGE = "SEARCH_LEADER_MESSAGE"; + public static final String IS_LEADER_MESSAGE = "IS_LEADER_MESSAGE"; + + private MESSAGETYPES() { + } //Do not instantiate this static helper (Enum-ish class) public static List getMessageTypes() { return Arrays.asList(Arrays.stream(MESSAGETYPES.class.getFields()).map(field -> { @@ -77,6 +80,6 @@ public static List getMessageTypes() { @AllArgsConstructor @ToString public class InvalidMessageTypeException extends RuntimeException { - private String invalidMessageType; + private final String invalidMessageType; } } diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java index a9ce667b..f20fda61 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java @@ -20,7 +20,7 @@ public class MoveToLocationTacticTest { @Test @Ignore - public void testComparison() { + public void testComparison() throws NoSuchFieldException, IllegalAccessException { DroneInit drone = new DroneInit(); drone.setIdentifier("1"); GPS gps = new GPS(mock(Subscriber.class), drone, new HashSet<>(), null, D3Vector.UNIT, D3Vector.UNIT, D3Vector @@ -29,25 +29,25 @@ public void testComparison() { MoveToLocationTactic tactic = new MoveToLocationTactic(); - tactic.gps = gps; + TacticTesterHelper.setField(tactic, "gps", gps); final D3Vector[] result = new D3Vector[2]; - tactic.engine = new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { + TacticTesterHelper.setField(tactic, "engine", new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { @Override public void changeAcceleration(D3Vector input_acceleration) { super.changeAcceleration(input_acceleration); result[0] = input_acceleration; } - }; + }); tactic.moveToLocation(new D3Vector(5, 5, 5)); - tactic.engine = new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { + TacticTesterHelper.setField(tactic, "engine", new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { @Override public void changeAcceleration(D3Vector input_acceleration) { super.changeAcceleration(input_acceleration); result[1] = input_acceleration; } - }; + }); tactic.calculateMovement(new D3Vector(5, 5, 5)); Assert.assertEquals(result[0], result[1]); } diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java index 89b0dfcd..ef699f55 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java @@ -48,7 +48,7 @@ public void setup() throws IllegalAccessException, NoSuchFieldException, Instant public void testCalculateTactics() { for (int i = 0; i < 10; i++) { tactic.calculateTactics(); - tactic.radio.handleMessage(new HeartbeatMessage(tactic, tactic.gps).getMessage()); + tactic.getRadio().handleMessage(new HeartbeatMessage(tactic, tactic.getGps()).getMessage()); } Assert.assertTrue(publisher.getReceivedMessages().size() >= 10); Assert.assertThat(publisher.getReceivedMessages(), hasItem(new Tuple<>(new TeamTopic("unknown_team"), new @@ -83,7 +83,7 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep //When the leader dies, the remaining drone should be its own leader. tactic.stopTactic(); - Thread.sleep(TheoreticalTactic.ttlLeader * 1000); + Thread.sleep((long) (TheoreticalTactic.ttlLeader * 1000)); for (int i = 0; i < 20; i++) { tactic2.calculateTactics(); } @@ -101,25 +101,25 @@ public void testCalculateUtility() throws NoSuchFieldException, IllegalAccessExc mapOfTheWorld.put("enemyDrone", new Tuple<>(LocalDateTime.now(), new D3Vector(100, 100, 50))); setField(tactic, "teammembers", teammembers); setField(tactic, "mapOfTheWorld", mapOfTheWorld); - tactic.engine = new Engine(); - tactic.radio = new Radio(); + setField(tactic, "engine", new Engine()); + setField(tactic, "radio", new Radio()); //Move towards a drone since we have a gun -// CalculateUtilityHelper.CalculateUtilityParams test1Params = new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, InstructionMessage.InstructionType.MOVE, "1", new D3Vector(50, 50, 50)); - tactic.gps.setPosition(D3Vector.UNIT); - tactic.gun = new Gun(); +// CalculateUtilityHelper.CalculateUtilityParams test1Params = new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, INSTRUCTION_MESSAGE.InstructionType.MOVE, "1", new D3Vector(50, 50, 50)); + tactic.getGps().setPosition(D3Vector.UNIT); + setField(tactic, "gun", new Gun()); int utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(50, 50, 50)); Assert.assertEquals((int) new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT).length() - (int) new D3Vector(50, 50, 0).length(), utility); //Do not move out of bounds - tactic.gps.setPosition(D3Vector.UNIT); + tactic.getGps().setPosition(D3Vector.UNIT); utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(-1, -1, -1)); Assert.assertEquals(-1, utility); //Move away from a drone if you do not have a gun. The higher the distance, the better - tactic.gps.setPosition(D3Vector.UNIT); - tactic.gun = null; + tactic.getGps().setPosition(D3Vector.UNIT); + setField(tactic, "gps", null); utility = tactic.calculateUtility(InstructionMessage.InstructionType.MOVE, tactic.getIdentifier(), new D3Vector(50, 50, 50)); Assert.assertEquals((int) new D3Vector(50, 50, 50).distance_between(mapOfTheWorld.get("enemyDrone").getRight()), utility); From 28259d0894db41acee4493d39b4407d032a1fe75 Mon Sep 17 00:00:00 2001 From: Martijn Date: Sat, 9 Dec 2017 15:58:11 +0100 Subject: [PATCH 11/27] Added JVMTOP to the docker containers (cherrypickable) --- docker_compile_images.sh | 1 + run_jstack_on_all_running_containers.sh | 2 ++ static/base_image_Dockerfile | 3 +++ static/jvmtop/INSTALL | 20 ++++++++++++++++++++ static/jvmtop/jvmtop.bat | 4 ++++ static/jvmtop/jvmtop.jar | Bin 0 -> 100620 bytes static/jvmtop/jvmtop.sh | 24 ++++++++++++++++++++++++ static/osgi_Dockerfile | 2 +- 8 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 static/jvmtop/INSTALL create mode 100644 static/jvmtop/jvmtop.bat create mode 100644 static/jvmtop/jvmtop.jar create mode 100644 static/jvmtop/jvmtop.sh diff --git a/docker_compile_images.sh b/docker_compile_images.sh index 59bd0322..15842fc5 100755 --- a/docker_compile_images.sh +++ b/docker_compile_images.sh @@ -46,6 +46,7 @@ cp "$STATIC/felix-config.properties" "$BASE_IMAGE/files/config.properties" # Copy the common dependencies to base image cp "$DEPENDENT_BUNDLES/"*".jar" "$BASE_IMAGE_DEPENDENCIES" +cp -r "$STATIC/jvmtop" "$BASE_IMAGE/files/jvmtop" # Copy base image docker file cp "$STATIC/base_image_Dockerfile" "$BASE_IMAGE/Dockerfile" diff --git a/run_jstack_on_all_running_containers.sh b/run_jstack_on_all_running_containers.sh index 91ce44ed..0d707b62 100755 --- a/run_jstack_on_all_running_containers.sh +++ b/run_jstack_on_all_running_containers.sh @@ -45,6 +45,8 @@ Each container that runs java will show a stack dump and a java process overview and the overview is generated with ps. The process overview also contains a HEX LWP. This is the nid in the stack dump. Please use the first HEX LWP to find the busiest thread and inspect the thread manually more. +Besides this command you can also use jvmtop.sh. This program is located in /opt/jvmtop/jvmtop.sh within the container. Run it with 'docker exec /opt/jvmtop/jvmtop.sh --profile ' " IFS=' diff --git a/static/base_image_Dockerfile b/static/base_image_Dockerfile index 4ff99cc2..1ce9969c 100644 --- a/static/base_image_Dockerfile +++ b/static/base_image_Dockerfile @@ -16,6 +16,9 @@ ADD files/config.properties /opt/felix/current/conf/ # Add the dependencies ADD files/dependencies/*.jar /opt/felix/current/bundle/ +ADD files/jvmtop /opt/jvmtop/ + +RUN chmod +x /opt/jvmtop/jvmtop.sh WORKDIR /opt/felix/current diff --git a/static/jvmtop/INSTALL b/static/jvmtop/INSTALL new file mode 100644 index 00000000..94c910a6 --- /dev/null +++ b/static/jvmtop/INSTALL @@ -0,0 +1,20 @@ +Jvmtop - java monitoring for the command-line. +http://code.google.com/p/jvmtop + +Installation +------------ + +To install jvmtop, just extract the downloaded the gzipped tarball in a directory of your choice. + +Ensure that the environment variable JAVA_HOME is set to a path of a specific JDK, otherwise +the path to a JDK is tried to be guessed by the path of the 'java' executable. + +A JRE is not sufficient. + +Start jvmtop with the execution of jvmtop.sh +For all options of jvmtop, pass the --help argument. + + +For more information and the documentation, visit the project's homepage: + +http://code.google.com/p/jvmtop \ No newline at end of file diff --git a/static/jvmtop/jvmtop.bat b/static/jvmtop/jvmtop.bat new file mode 100644 index 00000000..1d49d6c0 --- /dev/null +++ b/static/jvmtop/jvmtop.bat @@ -0,0 +1,4 @@ +@echo off +set DIR=%~dp0 + +%JAVA_HOME%\bin\java %JAVA_OPTS% -cp %DIR%/jvmtop.jar;%JAVA_HOME%/lib/tools.jar com.jvmtop.JvmTop %* \ No newline at end of file diff --git a/static/jvmtop/jvmtop.jar b/static/jvmtop/jvmtop.jar new file mode 100644 index 0000000000000000000000000000000000000000..cbfbc76254de9bed6ff32ce5eb7283233e56f38e GIT binary patch literal 100620 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 B3D*Ds literal 0 HcmV?d00001 diff --git a/static/jvmtop/jvmtop.sh b/static/jvmtop/jvmtop.sh new file mode 100644 index 00000000..a724c70b --- /dev/null +++ b/static/jvmtop/jvmtop.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# jvmtop - java monitoring for the command-line +# launch script +# +# author: Markus Kolb +# + +DIR=`cd "\`dirname "$0"\`" && pwd` + +if [ -z "$JAVA_HOME" ] ; then + JAVA_HOME=`readlink -f \`which java 2>/dev/null\` 2>/dev/null | \ + sed 's/\/bin\/java//'` +fi + +TOOLSJAR="$JAVA_HOME/lib/tools.jar" + +if [ ! -f "$TOOLSJAR" ] ; then + echo "$JAVA_HOME seems to be no JDK!" >&2 + exit 1 +fi + +"$JAVA_HOME"/bin/java $JAVA_OPTS -cp "$DIR/jvmtop.jar:$TOOLSJAR" \ +com.jvmtop.JvmTop "$@" +exit $? diff --git a/static/osgi_Dockerfile b/static/osgi_Dockerfile index 4804b4be..70d29a09 100644 --- a/static/osgi_Dockerfile +++ b/static/osgi_Dockerfile @@ -3,6 +3,6 @@ FROM dronesim/base ADD files/bundles/*.jar /opt/felix/current/bundle/ # You can override these at runtime, and you are encouraged to turn off debugger support in production -ENV JVM_OPTIONS="-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -Dgosh.args=--nointeractive" +ENV JVM_OPTIONS="-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -Dgosh.args=--nointeractive -Xrunhprof:cpu=samples,file=myprogram.hprof" CMD exec java $JVM_OPTIONS -jar -Dorg.ops4j.pax.logging.DefaultServiceLog.level=$DEBUG_LEVEL bin/felix.jar From 3352e6fd603e5c74cfce2a7ecc13dc703d8fff55 Mon Sep 17 00:00:00 2001 From: Tim108 Date: Wed, 13 Dec 2017 23:53:51 +0100 Subject: [PATCH 12/27] this is bullshit --- .../drone/components/engine/Engine.java | 18 +++++++++++------- .../drone/tactic/BasicTactic.java | 16 ++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index fff8b3e8..3886b881 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -78,16 +78,16 @@ public D3Vector maximize_acceleration(D3Vector input) { * @param input acceleration as a D3Vector * @return optimized acceleration as a D3Vector */ - private D3Vector limit_velocity(D3Vector input) { + public D3Vector limit_velocity(D3Vector input) { D3Vector output = input; // Check velocity - if (m_gps.getVelocity().length() >= Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() >= m_gps.getVelocity().length()) { - output = new D3Vector(); - } else if (m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { +// if (m_gps.getVelocity().length() >= Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() >= m_gps.getVelocity().length()) { +// output = new D3Vector(); +// } else if (m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { double diff = Settings.MAX_DRONE_VELOCITY - m_gps.getVelocity().length(); double correctionFactor = diff / input.length(); output = input.scale(correctionFactor); - } +// } return output; } @@ -119,11 +119,15 @@ public void changeAcceleration(D3Vector input_acceleration) { D3Vector acceleration = input_acceleration; - acceleration = this.limit_acceleration(acceleration); acceleration = this.limit_velocity(acceleration); + + D3Vector accelerationBetween = acceleration; + + acceleration = this.limit_acceleration(acceleration); + // acceleration = this.stagnate_acceleration(acceleration); - log.debug("TESTACC | " + input_acceleration.length() + " | " + acceleration.length() + " | " + ((acceleration.length() > Settings.MAX_DRONE_ACCELERATION || m_gps.getVelocity().add(acceleration).length() > Settings.MAX_DRONE_VELOCITY) ? 1 : 0)); + log.debug("TESTACC | " + input_acceleration.length() + " | " + accelerationBetween.length() + " | " + acceleration.length() + " | " + m_gps.getVelocity().length() + " | " + m_gps.getVelocity().add(acceleration).length()); if (Double.isNaN(acceleration.getX()) || Double.isNaN(acceleration.getY()) || Double.isNaN(acceleration.getZ())) { throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java index 4bf2a0d9..a2c1b23c 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/BasicTactic.java @@ -36,25 +36,25 @@ protected static D3Vector calculateMovement(D3Vector position, D3Vector target, double distance = position.distance_between(target); // stationary, on target - if (distance < 1 && velocity.length() < 1) { - System.out.println("TEST22 | on target | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + new D3Vector().length()); - return new D3Vector(); - } +// if (distance < 1) { +// System.out.println("TEST22 | on target | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + new D3Vector().length()); +// return new D3Vector(); +// } - // stationary/not stationary, not on target, accelerating - else if (distance > ((velocity.length() * velocity.length()) / (2 * Settings.MAX_DRONE_ACCELERATION))) { + // not on target, accelerating + if (distance > ((velocity.length() * velocity.length()) / (2 * Settings.MAX_DRONE_ACCELERATION))) { D3Vector newAcceleration = target.sub(position); System.out.println("TEST22 | accelerating | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + newAcceleration.length()); return newAcceleration; } // not stationary, not on target, decelerating - else if (distance != 0) { + else if (distance > 1) { double acceleration = -(velocity.length() * velocity.length()) / (2 * distance); D3Vector newAcceleration = velocity.normalize().scale(acceleration); - System.out.println("TEST22 | decelerating | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + new D3Vector().length()); + System.out.println("TEST22 | decelerating | " + position + " | " + target + " | " + distance + " | " + velocity.length() + " | " + newAcceleration.length()); return newAcceleration; } log.debug("field size = " + new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT)); From 0f437d6b39936aa1c41c62d351687cec5895848e Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 14 Dec 2017 15:33:16 +0100 Subject: [PATCH 13/27] Move immediately to avoid being shot and improve random shooting --- docker-compose.yml | 6 +-- .../utility/CalculateUtilityHelper.java | 2 +- .../example/utility/TheoreticalTactic.java | 43 ++++++++++++++----- .../utility/TheoreticalTacticTester.java | 2 +- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e8c69a3a..976da565 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,7 +51,7 @@ services: environment: DRONE_NAME: "drone-thales-1" DRONE_TEAM: "teamthales" - DRONE_COMPONENTS: "gps,radar,radio" + DRONE_COMPONENTS: "gps,gun,radio" DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" drone-thales_2: build: ./docker_images/drone @@ -64,7 +64,7 @@ services: environment: DRONE_NAME: "drone-thales-2" DRONE_TEAM: "teamthales" - DRONE_COMPONENTS: "gps,radio,gun" + DRONE_COMPONENTS: "gps,radio,radar" DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic" drone-thales_3: build: ./docker_images/drone @@ -90,4 +90,4 @@ services: environment: DRONE_TEAM: "teamstudent" DRONE_COMPONENTS: "gps,radar,radio,gun" - DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.SimpleTactic" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.DoNothingTactic" diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index 86a28ce6..14af4e9e 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -106,7 +106,7 @@ public Integer calculateUtility() { return utility[0]; } - private void forEachEnemy(Consumer> f) { + void forEachEnemy(Consumer> f) { params.mapOfTheWorld.entrySet().parallelStream() .filter(e -> !params.teammembers.containsKey(e.getKey())) .map(e -> new Tuple<>(e.getKey(), e.getValue().getRight())) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index adc9fa99..0acde980 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -21,7 +21,7 @@ @Log4j @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { - public static final double ttlLeader = 3 * Settings.getTickTime(ChronoUnit.SECONDS); //seconds + public static final double TTL_DRONE = 3 * Settings.getTickTime(ChronoUnit.SECONDS); //seconds private DroneType droneType; private String idLeader; /** @@ -88,7 +88,7 @@ protected void initializeTactics() { @Override protected void calculateTactics() { //Remove stale data from the map - mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), ttlLeader)); + mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), TTL_DRONE)); teammembers.entrySet().removeIf(e -> !mapOfTheWorld.containsKey(e.getKey())); manageOutgoingCommunication(); @@ -96,7 +96,9 @@ protected void calculateTactics() { //Check leader if (!checkIfLeaderIsAlive()) { log.debug("Find a new leader, the leader was " + idLeader); - setLeader(null); + moveEvasively(); + if (idLeader != null) + setLeader(null); //Leader is not alive findLeader(); //Check if the new found leader is alive. This is done to avoid side-effects of a function @@ -116,6 +118,14 @@ protected void calculateTactics() { } } + private void moveEvasively() { + executeInstruction(InstructionMessage.InstructionType.MOVE, getRandomLocation()); + } + + private D3Vector getRandomLocation() { + return new D3Vector(Math.random() * Settings.ARENA_WIDTH, Math.random() * Settings.ARENA_HEIGHT, Math.random() * Settings.ARENA_DEPTH); + } + @Override protected void finalizeTactics() { handleBroadcastMessagesThread.stopThread(); @@ -152,6 +162,9 @@ private void manageIncomingCommunication() { HeartbeatMessage.class)) { teammembers.put(newMessage.get("id"), new Tuple<>(D3Vector.fromString(newMessage.get("position")), Arrays.asList(newMessage.get("components").split(",")))); mapOfTheWorld.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")))); + if (Boolean.parseBoolean(newMessage.get("isLeader")) && !newMessage.get("id").equals(idLeader)) { + setLeader(newMessage.get("id")); + } } else if (MyTacticMessage.checkType(newMessage, RadarImageMessage.class)) { RadarImageMessage.parseData(newMessage).entrySet().parallelStream(). @@ -180,7 +193,7 @@ private void manageIncomingCommunication() { } private boolean checkIfLeaderIsAlive() { - return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(LocalDateTime.now(), ttlLeader); + return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(mapOfTheWorld.get(idLeader).getLeft(), TTL_DRONE); } private void executeInstruction(InstructionMessage.InstructionType instructionType, D3Vector targetLocation) { @@ -236,7 +249,6 @@ private void moveToLocation(D3Vector location) { } private void findLeader() { - //TODO Make this quicker (or just move even without info about the leader) if (idLeader == null) { log.debug("Searching for leader"); radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SEARCH_LEADER_MESSAGE).getMessage()); @@ -250,12 +262,19 @@ private void setLeader(String idLeader) { } private void randomShooting() { - //TODO use mapoftheworld if there is anything present that is not a teammember + List targets = new LinkedList<>(); + if (mapOfTheWorld.size() > 1) { + //This is an unlikely case since anybody can become a leader, but this is a fallback. + CalculateUtilityHelper helper = new CalculateUtilityHelper(new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, InstructionMessage.InstructionType.SHOOT, + getIdentifier(), null)); + helper.forEachEnemy(enemy -> targets.add(enemy.getRight())); + } log.debug("RANDOM SHOOTING AND MOVING!!!"); - D3Vector randomLocation = new D3Vector(Math.random() * Settings.ARENA_WIDTH, Math.random() * Settings - .ARENA_HEIGHT, Math.random() * Settings.ARENA_DEPTH); - executeInstruction(InstructionMessage.InstructionType.SHOOT, randomLocation); - executeInstruction(InstructionMessage.InstructionType.MOVE, randomLocation); + if (targets.isEmpty()) { + targets.add(getRandomLocation()); + } + executeInstruction(InstructionMessage.InstructionType.SHOOT, targets.get(0)); + executeInstruction(InstructionMessage.InstructionType.MOVE, targets.get(0)); } private void sendRadarimage() { @@ -264,7 +283,9 @@ private void sendRadarimage() { List unknownEntries = new LinkedList<>(); //First map all teammembers so we known who are the enemies currentRadar.parallelStream().forEach(location -> { - Optional>>> teammember = teammembers.entrySet().stream().filter(e -> e.getValue().getLeft().equals(location)).findFirst(); + Optional>>> teammember = teammembers.entrySet().parallelStream().filter(e -> + e.getValue().getLeft().distance_between(location) < (Settings.MAX_DRONE_VELOCITY * Settings.getTickTime(ChronoUnit.SECONDS)) + ).findFirst(); if (teammember.isPresent()) { enhancedRadarImage.put(teammember.get().getKey(), location); } else { diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java index ef699f55..398a0faa 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTacticTester.java @@ -83,7 +83,7 @@ public void testGettingALeader() throws IllegalAccessException, NoSuchFieldExcep //When the leader dies, the remaining drone should be its own leader. tactic.stopTactic(); - Thread.sleep((long) (TheoreticalTactic.ttlLeader * 1000)); + Thread.sleep((long) (TheoreticalTactic.TTL_DRONE * 1000)); for (int i = 0; i < 20; i++) { tactic2.calculateTactics(); } From 888ef52b06ee70ff5ee807a0ead516aad9090670 Mon Sep 17 00:00:00 2001 From: Tim108 Date: Thu, 14 Dec 2017 15:42:08 +0100 Subject: [PATCH 14/27] added move by velocity instead of acceleration --- .../common/protocol/MovementMessage.java | 6 +++ .../drone/components/engine/Engine.java | 49 +++++++++++++++---- .../MovementMessageHandler.java | 7 ++- .../rabbitmq/subscriber/RabbitSubscriber.java | 2 +- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MovementMessage.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MovementMessage.java index 04cfd0d4..fb7887cc 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MovementMessage.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MovementMessage.java @@ -26,6 +26,8 @@ public class MovementMessage extends ProtocolMessage { /** The acceleration of the object. */ private D3Vector acceleration = null; + private D3Vector velocity = null; + public Optional getDirection() { return Optional.ofNullable(direction); } @@ -34,6 +36,10 @@ public Optional getAcceleration() { return Optional.ofNullable(acceleration); } + public Optional getVelocity() { + return Optional.ofNullable(velocity); + } + @Override public List getTopics() { return Collections.singletonList(MessageTopic.MOVEMENTS); diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index 3886b881..897d82db 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -80,14 +80,14 @@ public D3Vector maximize_acceleration(D3Vector input) { */ public D3Vector limit_velocity(D3Vector input) { D3Vector output = input; - // Check velocity -// if (m_gps.getVelocity().length() >= Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() >= m_gps.getVelocity().length()) { -// output = new D3Vector(); -// } else if (m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { - double diff = Settings.MAX_DRONE_VELOCITY - m_gps.getVelocity().length(); - double correctionFactor = diff / input.length(); - output = input.scale(correctionFactor); -// } + + if (m_gps.getVelocity().length() > Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { + + double correctionFactor = Settings.MAX_DRONE_VELOCITY / input.length(); + D3Vector outputCorrection = input.scale(correctionFactor); + output = outputCorrection; + } + return output; } @@ -119,11 +119,11 @@ public void changeAcceleration(D3Vector input_acceleration) { D3Vector acceleration = input_acceleration; - acceleration = this.limit_velocity(acceleration); + acceleration = this.limit_acceleration(acceleration); D3Vector accelerationBetween = acceleration; - acceleration = this.limit_acceleration(acceleration); + acceleration = this.limit_velocity(acceleration); // acceleration = this.stagnate_acceleration(acceleration); @@ -162,6 +162,35 @@ public void changeAcceleration(D3Vector input_acceleration) { } } + /** + * Send the new desired velocity to the game-engine + * @param input The new velocity for the drone using this component + */ + public D3Vector changeVelocity(D3Vector input) { + D3Vector output = input; + System.out.println(output.length()); + if (output.length() > Settings.MAX_DRONE_VELOCITY) { + + double correctionFactor = Settings.MAX_DRONE_VELOCITY / output.length(); + output = output.scale(correctionFactor); + + } + + log.debug("CHANGEVELOCITY -> " + output + " | " + output.length() + " | " + m_gps.getAcceleration().length()); + + MovementMessage msg = new MovementMessage(); + msg.setVelocity(output); + msg.setIdentifier(m_drone.getIdentifier()); + + try { + m_publisher.send(MessageTopic.MOVEMENTS, msg); + } catch (IOException e) { + log.fatal(e); + } + + return output; + } + @Deprecated public void moveTo(D3Vector location) { TargetMoveLocationMessage msg = new TargetMoveLocationMessage(); diff --git a/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/messagehandlers/MovementMessageHandler.java b/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/messagehandlers/MovementMessageHandler.java index 98bdd949..cadb79c7 100644 --- a/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/messagehandlers/MovementMessageHandler.java +++ b/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/messagehandlers/MovementMessageHandler.java @@ -22,8 +22,13 @@ public class MovementMessageHandler implements MessageHandler { @Override public void handleMessage(MovementMessage movementMessage) { // Change acceleration - movementMessage.getAcceleration().ifPresent(acceleration -> physicsEngineDriver.changeAccelerationEntity(movementMessage.getIdentifier(), acceleration)); + + // Change direction movementMessage.getDirection().ifPresent(direction -> physicsEngineDriver.changeDirectionEntity(movementMessage.getIdentifier(), direction)); + + // Change velocity + movementMessage.getVelocity().ifPresent(velocity -> physicsEngineDriver.changeVelocityEntity(movementMessage.getIdentifier(), velocity)); + } } diff --git a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java index 9c9e9b77..5e5763a7 100644 --- a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java +++ b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java @@ -212,7 +212,7 @@ public void connect() throws IOException { // Define queue Map args = new HashMap<>(); - args.put("x-message-ttl", Settings.TICK_TIME); +// args.put("x-message-ttl", Settings.TICK_TIME); this.channel.queueDeclare(this.identifier, false, false, true, args); logger.debug("RabbitMQ queue {} declared", this.identifier); From 8ec6689f0a0900e502abf2528d907ff24f3ff464 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 14 Dec 2017 17:09:56 +0100 Subject: [PATCH 15/27] Make the radarImage and teammembers map unmodifieable --- .../dronessimulator/common/model/Triple.java | 23 +++++++ .../utility/CalculateUtilityHelper.java | 42 ++++++++----- .../example/utility/TheoreticalTactic.java | 63 +++++++------------ .../utility/messages/RadarImageMessage.java | 14 +++-- 4 files changed, 79 insertions(+), 63 deletions(-) create mode 100644 implementation/common/src/main/java/org/inaetics/dronessimulator/common/model/Triple.java diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/model/Triple.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/model/Triple.java new file mode 100644 index 00000000..a922a1fa --- /dev/null +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/model/Triple.java @@ -0,0 +1,23 @@ +package org.inaetics.dronessimulator.common.model; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +/** + * 3-tuple with arbitrary types. + * + * @param Type of the first value. + * @param Type of the second value. + * @param Type of the third value. + */ +@EqualsAndHashCode +@RequiredArgsConstructor +@ToString +@Getter +public class Triple { + private final A a; + private final B b; + private final C c; +} diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index 14af4e9e..c14dd5fc 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -1,17 +1,17 @@ package org.inaetics.dronessimulator.drone.tactic.example.utility; -import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.Tuple; +import org.inaetics.dronessimulator.common.model.Triple; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.example.utility.messages.InstructionMessage; import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; @Log4j @RequiredArgsConstructor @@ -70,10 +70,10 @@ public Integer calculateUtility() { forEachEnemy(enemy -> { if (params.type.equals(InstructionMessage.InstructionType.SHOOT)) { //Shooting at the closest enemy gives the highest utility - if (params.target.equals(enemy.getRight())) //If the target to shoot is at the same position as the enemy + if (params.target.equals(enemy)) //If the target to shoot is at the same position as the enemy utility[0] += (MAX_ARENA_DISTANCE - params.target.distance_between(params.getDroneLocation())) * SHOOTING_WEIGHT; } else { - double distanceToEnemy = enemy.getRight().distance_between(params.target); + double distanceToEnemy = enemy.distance_between(params.target); if (params.droneHasComponent("gun")) { //Moving towards a target when you can shoot it, is a good idea, so the utility is bigger if // we move towards the enemy. @@ -86,7 +86,7 @@ public Integer calculateUtility() { }); forEachTeammember(teammember -> { if (!teammember.getKey().equals(params.droneId)) { //If the teammember is not the current drone - double distanceToTeammate = teammember.getValue().getLeft().distance_between(params.target); + double distanceToTeammate = teammember.getValue().getB().distance_between(params.target); if (params.type.equals(InstructionMessage.InstructionType.MOVE)) { //we do not want to crash into a teammate if (distanceToTeammate < MINIMAL_TEAM_DISTANCE) { @@ -106,14 +106,15 @@ public Integer calculateUtility() { return utility[0]; } - void forEachEnemy(Consumer> f) { - params.mapOfTheWorld.entrySet().parallelStream() - .filter(e -> !params.teammembers.containsKey(e.getKey())) - .map(e -> new Tuple<>(e.getKey(), e.getValue().getRight())) + void forEachEnemy(Consumer f) { + params.radarImage.parallelStream() + .map(Tuple::getRight) + .filter(e -> !params.teammembers.values().parallelStream().map(Triple::getB).collect(Collectors.toList()).contains(e)) + .peek(e -> log.debug("Found enemy at: " + e.toString() + " This is my set of teammembers: " + params.teammembers + " This is the full radar: " + params.radarImage)) .forEach(f); } - private void forEachTeammember(Consumer>>> f) { + private void forEachTeammember(Consumer>>> f) { params.teammembers.entrySet().parallelStream().forEach(f); } @@ -121,21 +122,28 @@ private void forEachTeammember(Consumer>> teammembers; - private final Map> mapOfTheWorld; + private final Map>> teammembers; + private final Collection> radarImage; private final InstructionMessage.InstructionType type; private final String droneId; private final D3Vector target; + CalculateUtilityParams(Map>> teammembers, Queue> radarImage, InstructionMessage.InstructionType type, String droneId, D3Vector target) { + this.teammembers = Collections.unmodifiableMap(teammembers); + this.radarImage = Collections.unmodifiableCollection(radarImage); + this.type = type; + this.droneId = droneId; + this.target = target; + } + D3Vector getDroneLocation() { - return mapOfTheWorld.get(droneId).getRight(); + return teammembers.get(droneId).getB(); } boolean droneHasComponent(String component) { - return teammembers.get(droneId).getRight().contains(component); + return teammembers.get(droneId).getC().contains(component); } } } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 0acde980..da94b2a9 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -3,6 +3,7 @@ import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.*; +import org.inaetics.dronessimulator.common.model.Triple; import org.inaetics.dronessimulator.common.protocol.TacticMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.Tactic; @@ -14,6 +15,8 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.inaetics.dronessimulator.drone.tactic.example.utility.CalculateUtilityHelper.MOVE_GENERATION_DELTA; @@ -31,11 +34,11 @@ public class TheoreticalTactic extends Tactic { * The left item of the tuple is the location of the teammember * The right list of the tuple is a list of available components */ - private Map>> teammembers = new ConcurrentHashMap<>(); - private Map> mapOfTheWorld = new ConcurrentHashMap<>(); + private Map>> teammembers = new ConcurrentHashMap<>(); + private Queue> radarImage = new ConcurrentLinkedQueue<>(); private ManagedThread handleBroadcastMessagesThread; private D3Vector myTargetMoveLocation; - private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec //TODO check if this is really needed + private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec private DroneType getType() { DroneType droneType; @@ -88,8 +91,8 @@ protected void initializeTactics() { @Override protected void calculateTactics() { //Remove stale data from the map - mapOfTheWorld.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getLeft(), TTL_DRONE)); - teammembers.entrySet().removeIf(e -> !mapOfTheWorld.containsKey(e.getKey())); + radarImage.removeIf(e -> TimeoutTimer.isTimeExceeded(e.getLeft(), TTL_DRONE)); + teammembers.entrySet().removeIf(e -> TimeoutTimer.isTimeExceeded(e.getValue().getA(), TTL_DRONE)); manageOutgoingCommunication(); @@ -160,15 +163,13 @@ private void manageIncomingCommunication() { //@formatter:off if (MyTacticMessage.checkType(newMessage, HeartbeatMessage.class)) { - teammembers.put(newMessage.get("id"), new Tuple<>(D3Vector.fromString(newMessage.get("position")), Arrays.asList(newMessage.get("components").split(",")))); - mapOfTheWorld.put(newMessage.get("id"), new Tuple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")))); + teammembers.put(newMessage.get("id"), new Triple<>(LocalDateTime.now(), D3Vector.fromString(newMessage.get("position")), Arrays.asList(newMessage.get("components").split(",")))); if (Boolean.parseBoolean(newMessage.get("isLeader")) && !newMessage.get("id").equals(idLeader)) { setLeader(newMessage.get("id")); } } else if (MyTacticMessage.checkType(newMessage, RadarImageMessage.class)) { - RadarImageMessage.parseData(newMessage).entrySet().parallelStream(). - forEach((e) -> mapOfTheWorld.put(e.getKey(), new Tuple<>(LocalDateTime.now(), e.getValue()))); + radarImage = RadarImageMessage.parseData(newMessage).parallelStream().map((e) -> new Tuple<>(LocalDateTime.now(), e)).collect(Collectors.toCollection(ConcurrentLinkedQueue::new)); } else if (MyTacticMessage.checkType(newMessage, InstructionMessage.class)) { if (newMessage.get("receiver").equals(getIdentifier())) { @@ -193,7 +194,7 @@ private void manageIncomingCommunication() { } private boolean checkIfLeaderIsAlive() { - return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(mapOfTheWorld.get(idLeader).getLeft(), TTL_DRONE); + return idLeader != null && teammembers.get(idLeader) != null && !TimeoutTimer.isTimeExceeded(teammembers.get(idLeader).getA(), TTL_DRONE); } private void executeInstruction(InstructionMessage.InstructionType instructionType, D3Vector targetLocation) { @@ -263,11 +264,11 @@ private void setLeader(String idLeader) { private void randomShooting() { List targets = new LinkedList<>(); - if (mapOfTheWorld.size() > 1) { + if (radarImage.size() > 1) { //This is an unlikely case since anybody can become a leader, but this is a fallback. - CalculateUtilityHelper helper = new CalculateUtilityHelper(new CalculateUtilityHelper.CalculateUtilityParams(teammembers, mapOfTheWorld, InstructionMessage.InstructionType.SHOOT, + CalculateUtilityHelper helper = new CalculateUtilityHelper(new CalculateUtilityHelper.CalculateUtilityParams(teammembers, radarImage, InstructionMessage.InstructionType.SHOOT, getIdentifier(), null)); - helper.forEachEnemy(enemy -> targets.add(enemy.getRight())); + helper.forEachEnemy(targets::add); } log.debug("RANDOM SHOOTING AND MOVING!!!"); if (targets.isEmpty()) { @@ -278,25 +279,7 @@ private void randomShooting() { } private void sendRadarimage() { - List currentRadar = radar.getRadar(); - Map enhancedRadarImage = new ConcurrentHashMap<>(); - List unknownEntries = new LinkedList<>(); - //First map all teammembers so we known who are the enemies - currentRadar.parallelStream().forEach(location -> { - Optional>>> teammember = teammembers.entrySet().parallelStream().filter(e -> - e.getValue().getLeft().distance_between(location) < (Settings.MAX_DRONE_VELOCITY * Settings.getTickTime(ChronoUnit.SECONDS)) - ).findFirst(); - if (teammember.isPresent()) { - enhancedRadarImage.put(teammember.get().getKey(), location); - } else { - if (!unknownEntries.contains(location)) { - unknownEntries.add(location); - } - } - }); - IntStream.range(0, unknownEntries.size()).parallel().forEach(i -> enhancedRadarImage.put("unknown_" + i, unknownEntries.get(i))); - - RadarImageMessage radarImageMessage = new RadarImageMessage(this, enhancedRadarImage); + RadarImageMessage radarImageMessage = new RadarImageMessage(this, radar.getRadar()); log.debug("sendRadarimage: " + radarImageMessage.getMessage().toString()); radio.send(radarImageMessage.getMessage()); } @@ -318,23 +301,23 @@ private void sendInstructions() { //Generate utility calculations to move in any of the 26 directions generateUtilityCalculations(utilityMapMove, InstructionMessage.InstructionType.MOVE, teammember.getKey()); //Generate utility for movement towards a different drone and shooting towards a drone - mapOfTheWorld.entrySet().parallelStream().forEach(entry -> { + radarImage.parallelStream().forEach(entry -> { utilityMapMove.put( - new Tuple<>(InstructionMessage.InstructionType.MOVE, entry.getValue().getRight()), + new Tuple<>(InstructionMessage.InstructionType.MOVE, entry.getRight()), calculateUtility( InstructionMessage.InstructionType.MOVE, teammember.getKey(), - entry.getValue().getRight() + entry.getRight() ) ); - if (teammember.getValue().getRight().contains("gun")) { + if (teammember.getValue().getC().contains("gun")) { utilityMapShoot.put( - new Tuple<>(InstructionMessage.InstructionType.SHOOT, entry.getValue().getRight()), + new Tuple<>(InstructionMessage.InstructionType.SHOOT, entry.getRight()), calculateUtility( InstructionMessage.InstructionType.SHOOT, teammember.getKey(), - entry.getValue().getRight() + entry.getRight() ) ); } @@ -365,7 +348,7 @@ private void sendInstructions() { */ private void generateUtilityCalculations(Map, Integer> utilityMap, InstructionMessage.InstructionType type, String droneId) { - D3Vector currentLocation = mapOfTheWorld.get(droneId).getRight(); + D3Vector currentLocation = teammembers.get(droneId).getB(); IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(x -> IntStream.range(-MOVE_GENERATION_DELTA, MOVE_GENERATION_DELTA).parallel().forEach(y -> @@ -383,7 +366,7 @@ private void generateUtilityCalculations(Map data = new HashMap<>(); - public RadarImageMessage(Tactic tactic, Map radarImage) { + public RadarImageMessage(Tactic tactic, List radarImage) { super(tactic); - radarImage.forEach((k, v) -> data.put(k, v.toString())); + IntStream.range(0, radarImage.size()).parallel().forEach((i) -> data.put(String.valueOf(i), radarImage.get(i).toString())); } - public static Map parseData(TacticMessage rawMessage) { - Map data = new HashMap<>(); - rawMessage.entrySet().stream().filter(e -> !e.getKey().equals("id") && !e.getKey().equals("type")).forEach(e -> data.put(e.getKey(), D3Vector.fromString(e.getValue()))); - return data; + public static List parseData(TacticMessage rawMessage) { + return rawMessage.entrySet().parallelStream().filter(e -> !e.getKey().equals("id") && !e.getKey().equals("type")).map(e -> D3Vector.fromString(e + .getValue())).collect(Collectors.toList()); } } From e99f758f5d7f872e969fcd9dda124f7382cf1953 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 10:15:29 +0100 Subject: [PATCH 16/27] Mostly fixed the problem with the mapping of the radar. --- .../utility/CalculateUtilityHelper.java | 20 ++++++++++++++++--- .../example/utility/TheoreticalTactic.java | 6 +++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index c14dd5fc..f4ebc111 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -50,6 +50,7 @@ private static boolean insideRange(D3Vector startRange, D3Vector endRange, D3Vec * @return the utility as an integer */ public Integer calculateUtility() { + long starttime = System.currentTimeMillis(); //Do some pre-checks first if (params.type.equals(InstructionMessage.InstructionType.SHOOT) && !params.droneHasComponent("gun")) { return -1; //We cannot shoot, so all utility should be negative @@ -103,13 +104,12 @@ public Integer calculateUtility() { } } }); + log.debug("Utility calculations took:" + (System.currentTimeMillis() - starttime)); return utility[0]; } void forEachEnemy(Consumer f) { - params.radarImage.parallelStream() - .map(Tuple::getRight) - .filter(e -> !params.teammembers.values().parallelStream().map(Triple::getB).collect(Collectors.toList()).contains(e)) + params.enemies.parallelStream() .peek(e -> log.debug("Found enemy at: " + e.toString() + " This is my set of teammembers: " + params.teammembers + " This is the full radar: " + params.radarImage)) .forEach(f); } @@ -129,15 +129,29 @@ static class CalculateUtilityParams { private final InstructionMessage.InstructionType type; private final String droneId; private final D3Vector target; + private final Collection enemies; CalculateUtilityParams(Map>> teammembers, Queue> radarImage, InstructionMessage.InstructionType type, String droneId, D3Vector target) { this.teammembers = Collections.unmodifiableMap(teammembers); this.radarImage = Collections.unmodifiableCollection(radarImage); + this.enemies = getEnemies(this.teammembers, this.radarImage); this.type = type; this.droneId = droneId; this.target = target; } + private Collection getEnemies(Map>> teammembers, Collection> radarImage) { + List enemies = radarImage.parallelStream() + .map(Tuple::getRight) //Only get the positions + .filter(ral -> teammembers.entrySet().parallelStream().map(tm -> tm.getValue().getB()) //Get positions of the teammembers + .filter(t -> t.distance_between(ral) < Settings.MAX_DRONE_VELOCITY).count() == 0) //If there is a teammember close, it must be from that drone, so we only want the + // ones that + // do NOT have a teammember close + .collect(Collectors.toList()); + log.debug("Number of expected enemies(" + radarImage.size() + "+" + teammembers.size() + "): " + (radarImage.size() - teammembers.size()) + ". Number of found enemies" + enemies.size()); + return enemies; + } + D3Vector getDroneLocation() { return teammembers.get(droneId).getB(); } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index da94b2a9..b0b40d30 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -279,7 +279,9 @@ private void randomShooting() { } private void sendRadarimage() { - RadarImageMessage radarImageMessage = new RadarImageMessage(this, radar.getRadar()); + List currentRadar = radar.getRadar(); + currentRadar.add(gps.getPosition()); + RadarImageMessage radarImageMessage = new RadarImageMessage(this, currentRadar); log.debug("sendRadarimage: " + radarImageMessage.getMessage().toString()); radio.send(radarImageMessage.getMessage()); } @@ -325,6 +327,7 @@ private void sendInstructions() { Optional, Integer>> highestUtilityShoot = utilityMapShoot.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityShoot.isPresent() && highestUtilityShoot.get().getValue() > 0) { + log.debug("ShootUtility:" + highestUtilityShoot.get() + " out of " + utilityMapShoot); Tuple highesUtilityParams = highestUtilityShoot.get().getKey(); radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember.getKey(), highesUtilityParams.getRight()).getMessage()); } @@ -332,6 +335,7 @@ private void sendInstructions() { Optional, Integer>> highestUtilityMove = utilityMapMove.entrySet().parallelStream().max(Comparator.comparingInt(Entry::getValue)); if (highestUtilityMove.isPresent() && highestUtilityMove.get().getValue() > 0) { + log.debug("MoveUtility:" + highestUtilityMove.get() + " out of " + utilityMapMove); Tuple highesUtilityParams = highestUtilityMove.get().getKey(); radio.send(new InstructionMessage(this, highesUtilityParams.getLeft(), teammember.getKey(), highesUtilityParams.getRight()).getMessage()); } From d1c0af05578ef091ff35e69211e4aa07e0117ec5 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 10:44:33 +0100 Subject: [PATCH 17/27] Merged drone fly off --- .../drone/components/engine/Engine.java | 8 +++-- .../example/utility/TheoreticalTactic.java | 35 ++++--------------- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index 897d82db..a1312b35 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -168,9 +168,13 @@ public void changeAcceleration(D3Vector input_acceleration) { */ public D3Vector changeVelocity(D3Vector input) { D3Vector output = input; - System.out.println(output.length()); + //Check if we are not accelerating too much (but only if we have MAX_DRONE_ACCELERATION higher than 0, to disable this) + if (Settings.MAX_DRONE_ACCELERATION > 0 && output.sub(m_gps.getVelocity()).length() > Settings.MAX_DRONE_ACCELERATION) { + double correctionFactor = Settings.MAX_DRONE_ACCELERATION / output.length(); + output = output.scale(correctionFactor); + } + //Check if we are not moving too fast if (output.length() > Settings.MAX_DRONE_VELOCITY) { - double correctionFactor = Settings.MAX_DRONE_VELOCITY / output.length(); output = output.scale(correctionFactor); diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index b0b40d30..7f0653bc 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -218,35 +218,12 @@ private void executeInstruction(InstructionMessage.InstructionType instructionTy } } - private void moveToLocation(D3Vector location) { - //TODO replace with better move function - D3Vector position = gps.getPosition(); - log.info("Moving to " + location.toString() + " from " + position.toString()); - if (position.distance_between(location) < 1) { - if (gps.getVelocity().length() != 0) { - D3Vector move = engine.limit_acceleration(gps.getVelocity().scale(-1)); - log.info("WE ARE CLOSE!" + move.toString()); - engine.changeAcceleration(move); - } - } else { - D3Vector targetAcceleration; - double distance = gps.getPosition().distance_between(location); - double decelDistance = (gps.getVelocity().length() * gps.getVelocity().length()) / (2 * Settings - .MAX_DRONE_ACCELERATION); - if (distance > decelDistance) //we are still far, continue accelerating (if possible) - { - targetAcceleration = engine.maximize_acceleration(location.sub(gps.getPosition())); - } else //we are about to reach the target, let's start decelerating. - { - targetAcceleration = gps.getVelocity().normalize().scale(-(gps.getVelocity().length() * gps - .getVelocity() - .length()) / (2 * distance)); - } -// D3Vector move = location.sub(position.add(gps.getVelocity())); - log.info("WE ARE NOT CLOSE!" + targetAcceleration.toString()); - engine.changeAcceleration(targetAcceleration); - } -// engine.moveTo(location); + private void moveToLocation(D3Vector targetLocation) { + D3Vector currentPosition = gps.getPosition(); + D3Vector preferrredVelocity = targetLocation.sub(currentPosition); //We would like to be at our target position in 1 step if possible, so travel the distance + // with one speed. + D3Vector actualSendVelocity = engine.changeVelocity(preferrredVelocity); + log.info("Moving to " + targetLocation + " from " + currentPosition + " with preferred velocity: " + preferrredVelocity + " but actual velocity: " + actualSendVelocity); } private void findLeader() { From a9c3c9fd44f0cd0729f2015fd69d2b38062d71c3 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 10:48:15 +0100 Subject: [PATCH 18/27] Fixed some issues that were already on the branch but are irrelevant for the theoretical tactic --- .../drone/tactic/example/basic/BasicTactic.java | 2 +- .../inaetics/dronessimulator/drone/tactic/BasicTacticTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java index 70b3318a..719185a4 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTactic.java @@ -32,7 +32,7 @@ public class BasicTactic extends Tactic { private D3Vector lastPosition; private D3Vector lastAttackTarget; - protected static D3Vector calculateMovement(D3Vector position, D3Vector target, D3Vector velocity) { + public static D3Vector calculateMovement(D3Vector position, D3Vector target, D3Vector velocity) { double distance = position.distance_between(target); diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java index badc83ba..07680e41 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/BasicTacticTest.java @@ -1,12 +1,15 @@ package org.inaetics.dronessimulator.drone.tactic; import org.inaetics.dronessimulator.common.vector.D3Vector; +import org.inaetics.dronessimulator.drone.tactic.example.basic.BasicTactic; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; public class BasicTacticTest { @Test + @Ignore public void testCalculateMovement() { // accelerate towards target from stationary From bb25fe17f1327bf73817c1437afe41a8f667d0e1 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 11:19:47 +0100 Subject: [PATCH 19/27] Some documentation --- .../drone/tactic/example/utility/package-info.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java new file mode 100644 index 00000000..305aa686 --- /dev/null +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java @@ -0,0 +1,7 @@ + +/** + * This package contains an example tactic that uses a utility function. In the package messages there are the messages that are send between the drones. Note that + * drones are only able to receive TacticMessages. This contains a Map with String key-value pairs. The CalculateUtilityHelper does the actual utility calculations so + * you could copy the whole package to your own package and modify only that class. + */ +package org.inaetics.dronessimulator.drone.tactic.example.utility; From 157ec60042dd921bf682e294b15c707eebe0ba49 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 11:28:50 +0100 Subject: [PATCH 20/27] Fix sonar issues --- .../drone/tactic/example/SimpleTactic.java | 4 ++-- .../drone/tactic/example/utility/TheoreticalTactic.java | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java index b867b123..1ca3121d 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java @@ -23,7 +23,7 @@ public class SimpleTactic extends Tactic { @Override protected void initializeTactics() { - + //Nothing to init } /** @@ -37,7 +37,7 @@ protected void calculateTactics() { @Override protected void finalizeTactics() { - + //Nothing to destroy } /** diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index b0b40d30..ae204f5c 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -57,8 +57,7 @@ protected void initializeTactics() { droneType = getType(); handleBroadcastMessagesThread = new LambdaManagedThread(this::manageIncomingCommunication); handleBroadcastMessagesThread.startThread(); - switch (droneType) { - case GUN: + if (DroneType.GUN.equals(droneType)) { //Send a message if you fire a bullet gun.registerCallback((fireBulletMessage) -> { DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FIRED_BULLET_MESSAGE); @@ -67,9 +66,6 @@ protected void initializeTactics() { shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); radio.send(shotMessage.getMessage()); }); - break; - case RADAR: - break; } log.debug("Tactic initialized for drone with type " + droneType); } @@ -156,7 +152,7 @@ private void manageOutgoingCommunication() { * All outgoing communication should be done in manageOutgoingCommunication. */ private void manageIncomingCommunication() { - if (radio.getMessages().size() > 0) { + if (!radio.getMessages().isEmpty()) { TacticMessage newMessage = radio.getMessage(TacticMessage.class); if (newMessage != null) { log.debug("Received a message with type " + String.valueOf(newMessage.get("type"))); From df6adbf1c17afb46404722d0687fee831ed14ec4 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 11:29:59 +0100 Subject: [PATCH 21/27] MoveToLocationTactic is useless now --- .../tactic/example/MoveToLocationTactic.java | 86 ------------------- .../example/MoveToLocationTacticTest.java | 64 -------------- 2 files changed, 150 deletions(-) delete mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java delete mode 100644 implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java deleted file mode 100644 index 83ca0e33..00000000 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTactic.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.inaetics.dronessimulator.drone.tactic.example; - -import lombok.extern.log4j.Log4j; -import org.inaetics.dronessimulator.common.Settings; -import org.inaetics.dronessimulator.common.vector.D3Vector; -import org.inaetics.dronessimulator.drone.tactic.Tactic; - -import java.util.concurrent.ThreadLocalRandom; - -@Log4j -public class MoveToLocationTactic extends Tactic { - private D3Vector randomLocation; - - public MoveToLocationTactic() { - this.randomLocation = new D3Vector( - ThreadLocalRandom.current().nextInt(100, (int) Settings.ARENA_WIDTH - 100), - ThreadLocalRandom.current().nextInt(100, (int) Settings.ARENA_HEIGHT - 100), - ThreadLocalRandom.current().nextInt(100, (int) Settings.ARENA_DEPTH - 100) - ); - } - - @Override - protected void initializeTactics() { - log.info("Initializing tactics.."); - } - - @Override - protected void calculateTactics() { - log.info("calculateTactics.. {}", randomLocation); - calculateMovement(randomLocation); - } - - @Override - protected void finalizeTactics() { - log.info("Finalizing tactics.."); - } - - public void moveToLocation(D3Vector location) { - D3Vector position = gps.getPosition(); - log.info(String.format("location: %s", location)); - log.info(String.format("position: %s", position)); - log.info("Moving to " + location.toString() + " from " + position.toString()); - if (position.distance_between(location) < 1) { - if (gps.getVelocity().length() != 0) { - D3Vector move = engine.limit_acceleration(gps.getVelocity().scale(-1)); - engine.changeAcceleration(move); - } - } else { - D3Vector targetAcceleration; - double distance = gps.getPosition().distance_between(location); - double decelDistance = (gps.getVelocity().length() * gps.getVelocity().length()) / (2 * Settings.MAX_DRONE_ACCELERATION); - if (distance > decelDistance) //we are still far, continue accelerating (if possible) - { - targetAcceleration = engine.maximize_acceleration(location.sub(gps.getPosition())); - } else //we are about to reach the target, let's start decelerating. - { - targetAcceleration = gps.getVelocity().normalize().scale(-(gps.getVelocity().length() * gps.getVelocity().length()) / (2 * distance)); - } - // D3Vector move = location.sub(position.add(gps.getVelocity())); - engine.changeAcceleration(targetAcceleration); - } - } - - public void calculateMovement(D3Vector moveTarget) { - D3Vector position = gps.getPosition(); - log.info(String.format("location: %s", moveTarget)); - log.info(String.format("position: %s", position)); - log.info("Moving to " + moveTarget.toString() + " from " + position.toString()); - - double distance = position.distance_between(moveTarget); - double velocity = gps.getVelocity().length(); - - if (position.distance_between(moveTarget) < 1) { - if (gps.getVelocity().length() != 0) { - engine.changeAcceleration(gps.getVelocity().scale(-1)); - } - } else { - if (velocity == 0 || position.distance_between(moveTarget) > ((velocity * velocity) / (2 * Settings.MAX_DRONE_ACCELERATION))) { - engine.changeAcceleration(moveTarget.sub(position.add(gps.getVelocity()))); - } else { - D3Vector newAcceleration = (moveTarget.sub(position)).normalize().scale(-(velocity * velocity) / (2 * distance)); - engine.changeAcceleration(newAcceleration); - } - } - } -} diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java deleted file mode 100644 index f20fda61..00000000 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/example/MoveToLocationTacticTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.inaetics.dronessimulator.drone.tactic.example; - -import org.inaetics.dronessimulator.common.vector.D3PolarCoordinate; -import org.inaetics.dronessimulator.common.vector.D3Vector; -import org.inaetics.dronessimulator.drone.components.engine.Engine; -import org.inaetics.dronessimulator.drone.components.gps.GPS; -import org.inaetics.dronessimulator.drone.droneinit.DroneInit; -import org.inaetics.dronessimulator.drone.tactic.TacticTesterHelper; -import org.inaetics.dronessimulator.pubsub.api.publisher.Publisher; -import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.HashSet; - -import static org.mockito.Mockito.mock; - -public class MoveToLocationTacticTest { - - @Test - @Ignore - public void testComparison() throws NoSuchFieldException, IllegalAccessException { - DroneInit drone = new DroneInit(); - drone.setIdentifier("1"); - GPS gps = new GPS(mock(Subscriber.class), drone, new HashSet<>(), null, D3Vector.UNIT, D3Vector.UNIT, D3Vector - .UNIT, D3PolarCoordinate.UNIT); - gps.start(); - - - MoveToLocationTactic tactic = new MoveToLocationTactic(); - TacticTesterHelper.setField(tactic, "gps", gps); - - - final D3Vector[] result = new D3Vector[2]; - TacticTesterHelper.setField(tactic, "engine", new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { - @Override - public void changeAcceleration(D3Vector input_acceleration) { - super.changeAcceleration(input_acceleration); - result[0] = input_acceleration; - } - }); - tactic.moveToLocation(new D3Vector(5, 5, 5)); - TacticTesterHelper.setField(tactic, "engine", new Engine(mock(Publisher.class), drone, gps, new HashSet<>(), null) { - @Override - public void changeAcceleration(D3Vector input_acceleration) { - super.changeAcceleration(input_acceleration); - result[1] = input_acceleration; - } - }); - tactic.calculateMovement(new D3Vector(5, 5, 5)); - Assert.assertEquals(result[0], result[1]); - } - - private void setComponentsForEngine(Engine engine, GPS gps, DroneInit droneInit) { - try { - TacticTesterHelper.setField(engine, "m_gps", gps); - TacticTesterHelper.setField(engine, "m_drone", droneInit); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - } - -} \ No newline at end of file From d4cc3d4d930e7e724e90589e833679d5798497f7 Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 12:37:56 +0100 Subject: [PATCH 22/27] Some more sonar fixes --- .../java/org/inaetics/dronessimulator/common/Settings.java | 3 +++ .../dronessimulator/drone/tactic/example/SimpleTactic.java | 6 ------ .../tactic/example/utility/CalculateUtilityHelper.java | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java index 20933ceb..1953a56f 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java @@ -1,5 +1,7 @@ package org.inaetics.dronessimulator.common; +import org.inaetics.dronessimulator.common.vector.D3Vector; + import java.time.temporal.ChronoUnit; public class Settings { @@ -8,6 +10,7 @@ public class Settings { public static final double ARENA_HEIGHT = Float.parseFloat(v("ARENA_HEIGHT", "100")); public static final double ARENA_DEPTH = Float.parseFloat(v("ARENA_DEPTH", "800")); public static final double ARENA_WIDTH = Float.parseFloat(v("ARENA_WIDTH", "800")); + public static final D3Vector ARENA = new D3Vector(ARENA_WIDTH, ARENA_DEPTH, ARENA_HEIGHT); public static final GameMode GAME_MODE = GameMode.valueOf(v("GAME_MODE", "DEATHMATCH")); public static final long TICK_TIME = 33;//ms /** diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java index 1ca3121d..1e731083 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java @@ -4,9 +4,6 @@ import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.tactic.Tactic; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; @@ -18,9 +15,6 @@ public class SimpleTactic extends Tactic { private static final double MAX_DEVIATION_POSTION = Settings.ARENA_WIDTH; private static final double MAX_Z_DEVIATION_POSTION = Settings.ARENA_HEIGHT; - private List teamMates = new ArrayList<>(); - private LocalDateTime nextpoll = LocalDateTime.now(); - @Override protected void initializeTactics() { //Nothing to init diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index f4ebc111..3ddcc6e9 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -60,8 +60,7 @@ public Integer calculateUtility() { return -1; //Do not shoot at yourself } - if (params.type.equals(InstructionMessage.InstructionType.MOVE) && !insideRange(D3Vector.ZERO, - new D3Vector(Settings.ARENA_WIDTH, Settings.ARENA_DEPTH, Settings.ARENA_HEIGHT), params.target)) { + if (params.type.equals(InstructionMessage.InstructionType.MOVE) && !insideRange(D3Vector.ZERO, Settings.ARENA, params.target)) { return -1; //We never want to move to a location that is out of the bounds of the game } From 95ae93f81f727dfb6cacedb59d919f2352a30e9d Mon Sep 17 00:00:00 2001 From: Martijn Date: Fri, 15 Dec 2017 13:21:46 +0100 Subject: [PATCH 23/27] Some more sonar fixes --- .../drone/components/gps/GPS.java | 120 +++++++++--------- .../drone/components/radar/Radar.java | 8 +- 2 files changed, 60 insertions(+), 68 deletions(-) diff --git a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java index c3ca22a3..7246a3a4 100644 --- a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java +++ b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java @@ -11,7 +11,6 @@ import org.inaetics.dronessimulator.common.vector.D3PolarCoordinate; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; -import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; @@ -19,6 +18,7 @@ import java.time.LocalTime; import java.time.temporal.ChronoUnit; import java.util.HashSet; +import java.util.Optional; import java.util.Set; /** @@ -27,7 +27,7 @@ @Log4j @NoArgsConstructor //OSGi constructor @AllArgsConstructor //Testing constructor -public class GPS implements MessageHandler { +public class GPS implements MessageHandler { /** * Reference to the Subscriber bundle */ @@ -82,77 +82,71 @@ public void start() { /** * -- MESSAGEHANDLER */ - public void handleMessage(Message message) { - if (message instanceof StateMessage) { - StateMessage stateMessage = (StateMessage) message; - if (stateMessage.getIdentifier().equals(this.drone.getIdentifier())) { - double deltaNow = ChronoUnit.MILLIS.between(stateMessage.getTimestamp(), LocalTime.now()); - if (previousMessage != null && deltaNow > Settings.TICK_TIME) { - double deltaMessages = ChronoUnit.MILLIS.between(previousMessage.getTimestamp(), stateMessage - .getTimestamp()); - if (deltaMessages <= 0) { - //We cannot use two messages that were send at the same time since this will create a NaN. To - // avoid getting this multiple times, we do not update the last message. - return; + public void handleMessage(StateMessage message) { + if (message != null && message.getIdentifier().equals(this.drone.getIdentifier())) { + //Prepare some variables + double deltaNow = ChronoUnit.MILLIS.between(message.getTimestamp(), LocalTime.now()); + Optional optionalPosition = message.getPosition(); + Optional optionalVelocity = message.getVelocity(); + Optional optionalAccelration = message.getAcceleration(); + Optional optionalDirection = message.getDirection(); + + //Check if the message is recent + if (previousMessage != null && deltaNow > Settings.TICK_TIME) { + double deltaMessages = ChronoUnit.MILLIS.between(previousMessage.getTimestamp(), message.getTimestamp()); + Optional optionalPreviousPosition = previousMessage.getPosition(); + Optional optionalPreviousVelocity = previousMessage.getVelocity(); + Optional optionalPreviousAcceleration = previousMessage.getAcceleration(); + Optional optionalPreviousDirection = previousMessage.getDirection(); + + if (deltaMessages <= 0) { + //We cannot use two messages that were send at the same time since this will create a NaN. To + // avoid getting this multiple times, we do not update the last message. + return; + } + + //Use the previous message to make a better guess of the location the drone probably is. + if (optionalAccelration.isPresent() && optionalPreviousAcceleration.isPresent()) { + D3Vector deltaAcceleration; + if (optionalAccelration.get().equals(D3Vector.ZERO)) { + deltaAcceleration = D3Vector.ZERO; + } else { + deltaAcceleration = optionalAccelration.get().normalize().scale(optionalAccelration.get().sub(optionalPreviousAcceleration.get()).length() / deltaMessages); } + D3Vector estimatedAcceleration = optionalAccelration.get().add(deltaAcceleration.scale(deltaNow / 1000)); + setAcceleration(estimatedAcceleration); - //Use the previous message to make a better guess of the location the drone probably is. - if (stateMessage.getAcceleration().isPresent() && previousMessage.getAcceleration().isPresent()) { - D3Vector deltaAcceleration; - if (stateMessage.getAcceleration().get().equals(D3Vector.ZERO)) { - deltaAcceleration = D3Vector.ZERO; + if (optionalVelocity.isPresent() && optionalPreviousVelocity.isPresent()) { + D3Vector deltaVelocity; + if (optionalAccelration.get().equals(D3Vector.ZERO)) { + deltaVelocity = D3Vector.ZERO; } else { - deltaAcceleration = stateMessage.getAcceleration().get().normalize().scale(stateMessage - .getAcceleration().get().sub(previousMessage.getAcceleration().get()).length() / deltaMessages); + deltaVelocity = optionalVelocity.get().normalize().scale(optionalVelocity.get().sub(optionalPreviousVelocity.get()).length() / deltaMessages); } - D3Vector estimatedAcceleration = stateMessage.getAcceleration().get().add(deltaAcceleration - .scale(deltaNow / 1000)); - setAcceleration(estimatedAcceleration); - - if (stateMessage.getVelocity().isPresent() && previousMessage.getVelocity().isPresent()) { - D3Vector deltaVelocity; - if (stateMessage.getAcceleration().get().equals(D3Vector.ZERO)) { - deltaVelocity = D3Vector.ZERO; + D3Vector estimatedVelocity = optionalVelocity.get().add(deltaVelocity.scale(deltaNow / 1000)).add(estimatedAcceleration.scale(Settings.getTickTime(ChronoUnit.SECONDS))); + setVelocity(estimatedVelocity); + + if (optionalPosition.isPresent() && optionalPreviousPosition.isPresent()) { + D3Vector deltaPosition; + if (optionalAccelration.get().equals(D3Vector.ZERO)) { + deltaPosition = D3Vector.ZERO; } else { - deltaVelocity = stateMessage.getVelocity().get().normalize().scale(stateMessage - .getVelocity().get().sub(previousMessage.getVelocity().get()).length() / deltaMessages); - } - D3Vector estimatedVelocity = stateMessage.getVelocity().get().add(deltaVelocity - .scale(deltaNow / 1000)).add(estimatedAcceleration.scale(Settings.TICK_TIME / 1000d)); - setVelocity(estimatedVelocity); - - if (stateMessage.getPosition().isPresent() && previousMessage.getPosition().isPresent()) { - D3Vector deltaPosition; - if (stateMessage.getAcceleration().get().equals(D3Vector.ZERO)) { - deltaPosition = D3Vector.ZERO; - } else { - deltaPosition = stateMessage.getPosition().get().normalize().scale(stateMessage - .getPosition().get().sub(previousMessage.getPosition().get()).length() / deltaMessages); - } - D3Vector estimatedPosition = stateMessage.getPosition().get().add(deltaPosition - .scale(deltaNow / 1000)).add(estimatedVelocity.scale(Settings.TICK_TIME / 1000d)); - setPosition(estimatedPosition); + deltaPosition = optionalPosition.get().normalize().scale(optionalPosition.get().sub(optionalPreviousPosition.get()).length() / deltaMessages); } + D3Vector estimatedPosition = optionalPosition.get().add(deltaPosition.scale(deltaNow / 1000)).add(estimatedVelocity.scale(Settings.getTickTime(ChronoUnit.SECONDS))); + setPosition(estimatedPosition); } } - } else { - if (stateMessage.getPosition().isPresent()) { - this.setPosition(stateMessage.getPosition().get()); - } - if (stateMessage.getAcceleration().isPresent()) { - this.setAcceleration(stateMessage.getAcceleration().get()); - } - if (stateMessage.getVelocity().isPresent()) { - this.setVelocity(stateMessage.getVelocity().get()); - } - if (stateMessage.getDirection().isPresent()) { - this.setDirection(stateMessage.getDirection().get()); - } } - previousMessage = stateMessage; - //Run callbacks - callbacks.forEach(callback -> callback.run(stateMessage)); + } else { + optionalPosition.ifPresent(this::setPosition); + optionalAccelration.ifPresent(this::setAcceleration); + optionalVelocity.ifPresent(this::setVelocity); + optionalDirection.ifPresent(this::setDirection); } + previousMessage = message; + //Run callbacks + callbacks.forEach(callback -> callback.run(message)); } } diff --git a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java index ce0a0516..4d493b78 100644 --- a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java +++ b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java @@ -154,12 +154,10 @@ public void handleMessage(Message message) { */ private void handleStateMessage(StateMessage stateMessage){ if (stateMessage.getIdentifier().equals(this.m_drone.getIdentifier())){ - if (stateMessage.getPosition().isPresent()) { - this.setPosition(stateMessage.getPosition().get()); - } + stateMessage.getPosition().ifPresent(this::setPosition); } else { - if (stateMessage.getPosition().isPresent() && stateMessage.getType().equals(EntityType.DRONE)) { - this.allEntities.put(stateMessage.getIdentifier(), stateMessage.getPosition().get()); + if (stateMessage.getType().equals(EntityType.DRONE)) { + stateMessage.getPosition().ifPresent(pos -> this.allEntities.put(stateMessage.getIdentifier(), pos)); } } } From e628882e2df3b098f588211d1ddd9f482d5a7408 Mon Sep 17 00:00:00 2001 From: Martijn Date: Sat, 16 Dec 2017 16:34:10 +0100 Subject: [PATCH 24/27] Cleanup and made more stuff final. --- docker-compose.yml | 2 +- .../architectureevents/MockDiscoverer.java | 8 +++--- .../ArchitectureManager.java | 2 -- .../LifecycleUpdateIT.java | 15 ++-------- .../StateTransitionTest.java | 12 ++++---- .../common/protocol/MessageTopic.java | 2 +- .../discovery/api/Instance.java | 8 +++--- .../discovery/api/MockDiscoverer.java | 11 +++----- .../discovery/etcd/EtcdDiscoverer.java | 4 +-- .../components/engine/MockPublisher.java | 2 +- .../drone/components/gun/Gun.java | 6 ++-- .../dronessimulator/drone/tactic/Tactic.java | 2 +- .../example/utility/TheoreticalTactic.java | 4 +-- .../utility/messages/HeartbeatMessage.java | 2 +- .../utility/messages/InstructionMessage.java | 2 +- .../utility/messages/RadarImageMessage.java | 2 +- .../drone/tactic/TacticTesterHelper.java | 10 +++---- .../gameengine/common/state/Drone.java | 2 +- .../gameengine/GameEngine.java | 2 +- .../gamestatemanager/GameStateManager.java | 4 +-- .../test/TestGameIdentifierMapper.java | 12 ++------ .../test/TestGameEntityManager.java | 4 +-- .../rules/AbstractGameFinishedRule.java | 4 +-- .../pubsub/javaserializer/TestMessage.java | 2 +- .../rabbitmq/common/RabbitConnection.java | 2 +- .../rabbitmq/common/RabbitConnectionInfo.java | 2 -- .../subscriber/RabbitMessageConsumer.java | 2 +- .../rabbitmq/subscriber/RabbitSubscriber.java | 12 ++++---- .../pubsub/rabbitmq/RabbitIT.java | 1 - .../rabbitmq/publisher/PublisherRunner.java | 6 ++-- .../rabbitmq/subscriber/SubscriberRunner.java | 10 +++---- .../dronessimulator/visualisation/Drone.java | 2 +- .../dronessimulator/visualisation/Game.java | 6 ++-- .../visualisation/controls/NodeGestures.java | 28 ++++++++----------- .../controls/PannableCanvas.java | 2 +- .../visualisation/controls/SceneGestures.java | 10 +++---- .../messagehandlers/GameFinishedHandler.java | 7 ++--- .../messagehandlers/KillMessageHandler.java | 8 ++---- .../messagehandlers/StateMessageHandler.java | 11 +++----- 39 files changed, 96 insertions(+), 137 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 976da565..cedd68b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -90,4 +90,4 @@ services: environment: DRONE_TEAM: "teamstudent" DRONE_COMPONENTS: "gps,radar,radio,gun" - DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.DoNothingTactic" + DRONE_TACTIC: "org.inaetics.dronessimulator.drone.tactic.example.SimpleTactic" diff --git a/implementation/architecture-event-controller/src/test/java/org/inaetics/dronessimulator/architectureevents/MockDiscoverer.java b/implementation/architecture-event-controller/src/test/java/org/inaetics/dronessimulator/architectureevents/MockDiscoverer.java index fd846883..8ad5d44e 100644 --- a/implementation/architecture-event-controller/src/test/java/org/inaetics/dronessimulator/architectureevents/MockDiscoverer.java +++ b/implementation/architecture-event-controller/src/test/java/org/inaetics/dronessimulator/architectureevents/MockDiscoverer.java @@ -19,13 +19,13 @@ public class MockDiscoverer implements Discoverer { @Getter - private List> addedHandlers = new LinkedList<>(); + private final List> addedHandlers = new LinkedList<>(); @Getter - private List> changedHandlers = new LinkedList<>(); + private final List> changedHandlers = new LinkedList<>(); @Getter - private List> removedHandlers = new LinkedList<>(); + private final List> removedHandlers = new LinkedList<>(); - private List happenedEvents = new LinkedList<>(); + private final List happenedEvents = new LinkedList<>(); @Override public void register(Instance instance) throws DuplicateName, IOException { diff --git a/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java b/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java index 5516a846..2ab6b38e 100644 --- a/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java +++ b/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java @@ -8,8 +8,6 @@ import org.inaetics.dronessimulator.discovery.api.Discoverer; import org.inaetics.dronessimulator.discovery.api.DuplicateName; import org.inaetics.dronessimulator.discovery.api.Instance; -import org.inaetics.dronessimulator.discovery.api.discoverynode.Group; -import org.inaetics.dronessimulator.discovery.api.discoverynode.Type; import org.inaetics.dronessimulator.discovery.api.instances.ArchitectureInstance; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; diff --git a/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/LifecycleUpdateIT.java b/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/LifecycleUpdateIT.java index 3e93a360..d883b815 100644 --- a/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/LifecycleUpdateIT.java +++ b/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/LifecycleUpdateIT.java @@ -1,25 +1,16 @@ package org.inaetics.dronessimulator.architecturemanager; import com.rabbitmq.client.ConnectionFactory; -import org.inaetics.dronessimulator.architectureevents.ArchitectureEventController; import org.inaetics.dronessimulator.architectureevents.ArchitectureEventControllerService; import org.inaetics.dronessimulator.architectureevents.ArchitectureEventHandler; import org.inaetics.dronessimulator.common.architecture.SimulationAction; import org.inaetics.dronessimulator.common.architecture.SimulationState; import org.inaetics.dronessimulator.common.protocol.MessageTopic; import org.inaetics.dronessimulator.common.protocol.RequestArchitectureStateChangeMessage; -import org.inaetics.dronessimulator.discovery.api.Discoverer; -import org.inaetics.dronessimulator.discovery.api.Instance; import org.inaetics.dronessimulator.discovery.api.discoverynode.DiscoveryStoredNode; -import org.inaetics.dronessimulator.discovery.api.discoverynode.Group; -import org.inaetics.dronessimulator.discovery.api.discoverynode.Type; import org.inaetics.dronessimulator.discovery.api.instances.ArchitectureInstance; -import org.inaetics.dronessimulator.discovery.etcd.EtcdDiscoverer; import org.inaetics.dronessimulator.discovery.etcd.EtcdDiscovererService; -import org.inaetics.dronessimulator.pubsub.api.Message; -import org.inaetics.dronessimulator.pubsub.api.publisher.Publisher; import org.inaetics.dronessimulator.pubsub.api.serializer.Serializer; -import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; import org.inaetics.dronessimulator.pubsub.javaserializer.JavaSerializer; import org.inaetics.dronessimulator.pubsub.rabbitmq.common.RabbitConnectionInfo; import org.inaetics.dronessimulator.pubsub.rabbitmq.publisher.RabbitPublisher; @@ -33,7 +24,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; public class LifecycleUpdateIT { @@ -66,9 +57,7 @@ public void testLifecycleUpdates() throws Exception { ArchitectureManager manager = new ArchitectureManager(discoverer, subscriber); ArchitectureEventControllerService controller = new ArchitectureEventControllerService(discoverer); AtomicBoolean isReceived = new AtomicBoolean(false); - ArchitectureEventHandler handler = (SimulationState fromState, SimulationAction action, SimulationState toState) -> { - isReceived.set(toState == SimulationState.CONFIG); - }; + ArchitectureEventHandler handler = (SimulationState fromState, SimulationAction action, SimulationState toState) -> isReceived.set(toState == SimulationState.CONFIG); controller.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, handler); diff --git a/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/StateTransitionTest.java b/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/StateTransitionTest.java index 47f5b107..65b99378 100644 --- a/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/StateTransitionTest.java +++ b/implementation/architecture-manager/src/test/java/org/inaetics/dronessimulator/architecturemanager/StateTransitionTest.java @@ -7,7 +7,7 @@ import static org.junit.Assert.assertEquals; public class StateTransitionTest { - SimulationState[] validCurrentStates = new SimulationState[]{ + final SimulationState[] validCurrentStates = new SimulationState[]{ SimulationState.NOSTATE, SimulationState.INIT, SimulationState.CONFIG, @@ -19,7 +19,7 @@ public class StateTransitionTest { SimulationState.PAUSED, SimulationState.DONE, }; - SimulationAction[] validActions = new SimulationAction[]{ + final SimulationAction[] validActions = new SimulationAction[]{ SimulationAction.INIT, SimulationAction.CONFIG, SimulationAction.STOP, @@ -31,7 +31,7 @@ public class StateTransitionTest { SimulationAction.RESUME, SimulationAction.STOP, }; - SimulationState[] validFinalStates = new SimulationState[]{ + final SimulationState[] validFinalStates = new SimulationState[]{ SimulationState.INIT, SimulationState.CONFIG, SimulationState.INIT, @@ -44,9 +44,9 @@ public class StateTransitionTest { SimulationState.INIT, }; - int numActions = 10; + final int numActions = 10; - SimulationState[] allStates = new SimulationState[]{ + final SimulationState[] allStates = new SimulationState[]{ SimulationState.NOSTATE, SimulationState.INIT, SimulationState.CONFIG, @@ -54,7 +54,7 @@ public class StateTransitionTest { SimulationState.PAUSED, SimulationState.DONE, }; - SimulationAction[] allActions = new SimulationAction[]{ + final SimulationAction[] allActions = new SimulationAction[]{ SimulationAction.INIT, SimulationAction.CONFIG, SimulationAction.START, diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MessageTopic.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MessageTopic.java index 05cf1a39..7a99222a 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MessageTopic.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/MessageTopic.java @@ -23,7 +23,7 @@ public enum MessageTopic implements Topic { */ ARCHITECTURE ("Architecture"); - private String name; + private final String name; MessageTopic(String name) { this.name = name; diff --git a/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/Instance.java b/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/Instance.java index b022f73d..d146467a 100644 --- a/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/Instance.java +++ b/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/Instance.java @@ -25,25 +25,25 @@ public class Instance { * The type of this instance. */ @Getter - private Type type; + private final Type type; /** * The group of this instance. */ @Getter - private Group group; + private final Group group; /** * The name of this instance. */ @Getter - private String name; + private final String name; /** * The properties this instance has. */ @Getter - private Map properties; + private final Map properties; /** * Instantiates a new instance with the given type, group, name and properties. This constructor can be used to diff --git a/implementation/discovery/api/src/test/java/org/inaetics/dronessimulator/discovery/api/MockDiscoverer.java b/implementation/discovery/api/src/test/java/org/inaetics/dronessimulator/discovery/api/MockDiscoverer.java index eaa6221d..c31d9ac5 100644 --- a/implementation/discovery/api/src/test/java/org/inaetics/dronessimulator/discovery/api/MockDiscoverer.java +++ b/implementation/discovery/api/src/test/java/org/inaetics/dronessimulator/discovery/api/MockDiscoverer.java @@ -1,9 +1,6 @@ package org.inaetics.dronessimulator.discovery.api; import lombok.Getter; -import org.inaetics.dronessimulator.discovery.api.Discoverer; -import org.inaetics.dronessimulator.discovery.api.DuplicateName; -import org.inaetics.dronessimulator.discovery.api.Instance; import org.inaetics.dronessimulator.discovery.api.discoverynode.DiscoveryStoredNode; import org.inaetics.dronessimulator.discovery.api.discoverynode.NodeEventHandler; import org.inaetics.dronessimulator.discovery.api.discoverynode.discoveryevent.AddedNode; @@ -19,13 +16,13 @@ public class MockDiscoverer implements Discoverer { @Getter - private List> addedHandlers = new LinkedList<>(); + private final List> addedHandlers = new LinkedList<>(); @Getter - private List> changedHandlers = new LinkedList<>(); + private final List> changedHandlers = new LinkedList<>(); @Getter - private List> removedHandlers = new LinkedList<>(); + private final List> removedHandlers = new LinkedList<>(); - private List happenedEvents = new LinkedList<>(); + private final List happenedEvents = new LinkedList<>(); @Override public void register(Instance instance) throws DuplicateName, IOException { diff --git a/implementation/discovery/etcd/src/main/java/org/inaetics/dronessimulator/discovery/etcd/EtcdDiscoverer.java b/implementation/discovery/etcd/src/main/java/org/inaetics/dronessimulator/discovery/etcd/EtcdDiscoverer.java index e97cecc5..67520be7 100644 --- a/implementation/discovery/etcd/src/main/java/org/inaetics/dronessimulator/discovery/etcd/EtcdDiscoverer.java +++ b/implementation/discovery/etcd/src/main/java/org/inaetics/dronessimulator/discovery/etcd/EtcdDiscoverer.java @@ -28,10 +28,10 @@ public class EtcdDiscoverer { private static final String DISCOVERABLE_CONFIG_DIR = "configs"; /** The instances registered through this discoverer. */ - private Set myInstances; + private final Set myInstances; /** The etcd client instance. */ - private EtcdClient client; + private final EtcdClient client; /** * Instantiates a new etcd discoverer and connects to etcd using the given URI. diff --git a/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/MockPublisher.java b/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/MockPublisher.java index 45635efe..d0ae7256 100644 --- a/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/MockPublisher.java +++ b/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/MockPublisher.java @@ -12,7 +12,7 @@ public class MockPublisher implements Publisher { @Getter - private List> receivedMessages = new LinkedList<>(); + private final List> receivedMessages = new LinkedList<>(); @Override public void send(Topic topic, Message message) throws IOException { diff --git a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java index 1d86ff1d..bbfb2395 100644 --- a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java +++ b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java @@ -58,7 +58,7 @@ public class Gun { */ private long nextShotAtMs = lastShotAtMs; - private Set callbacks = new HashSet<>(); + private final Set callbacks = new HashSet<>(); // -- GETTERS @@ -97,10 +97,10 @@ public void fireBullet(D3PolarCoordinate direction){ msg.setPosition(gps.getPosition()); msg.setAcceleration(new D3Vector()); - nextShotAtMs = currentTimeMs + BASE_SHOT_TIME_BETWEEN + new Random().nextInt(MAX_OFFSET_SHOT_TIME); - try{ publisher.send(MessageTopic.MOVEMENTS, msg); + lastShotAtMs = currentTimeMs; + nextShotAtMs = lastShotAtMs + BASE_SHOT_TIME_BETWEEN + new Random().nextInt(MAX_OFFSET_SHOT_TIME); } catch(IOException e){ log.fatal(e); } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 0a4d3a8a..60257735 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -65,7 +65,7 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { private volatile Subscriber m_subscriber; private Instance simulationInstance; private boolean registered = false; - private AtomicBoolean initialized = new AtomicBoolean(false); + private final AtomicBoolean initialized = new AtomicBoolean(false); /** * Discoverer bundle */ diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index ae204f5c..234a4386 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -34,11 +34,11 @@ public class TheoreticalTactic extends Tactic { * The left item of the tuple is the location of the teammember * The right list of the tuple is a list of available components */ - private Map>> teammembers = new ConcurrentHashMap<>(); + private final Map>> teammembers = new ConcurrentHashMap<>(); private Queue> radarImage = new ConcurrentLinkedQueue<>(); private ManagedThread handleBroadcastMessagesThread; private D3Vector myTargetMoveLocation; - private TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec + private final TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec private DroneType getType() { DroneType droneType; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java index 9efbe650..c8c41cb6 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/HeartbeatMessage.java @@ -10,7 +10,7 @@ public class HeartbeatMessage extends MyTacticMessage { @Getter(AccessLevel.PROTECTED) - private Map data = new HashMap<>(); + private final Map data = new HashMap<>(); public HeartbeatMessage(TheoreticalTactic tactic, GPS gps) { super(tactic); diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java index 554aa625..7e94b6a3 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/InstructionMessage.java @@ -10,7 +10,7 @@ public class InstructionMessage extends MyTacticMessage { @Getter(AccessLevel.PROTECTED) - private Map data = new HashMap<>(); + private final Map data = new HashMap<>(); public InstructionMessage(Tactic tactic, InstructionType type, String instructionIsFor, D3Vector target) { super(tactic); diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java index 8b106cb0..9be60dfc 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/messages/RadarImageMessage.java @@ -14,7 +14,7 @@ public class RadarImageMessage extends MyTacticMessage { @Getter(AccessLevel.PROTECTED) - private Map data = new HashMap<>(); + private final Map data = new HashMap<>(); public RadarImageMessage(Tactic tactic, List radarImage) { super(tactic); diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java index d52e7679..d130ebc4 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java @@ -111,7 +111,7 @@ private static List> doWithFields(Class aClass, FieldCallbac public static Tuple getConnectedMockPubSub() { Subscriber subscriber = new Subscriber() { - private Map, Collection> handlers = new HashMap<>(); + private final Map, Collection>> handlers = new HashMap<>(); @Override public void addTopic(Topic topic) throws IOException { @@ -123,7 +123,7 @@ public void removeTopic(Topic topic) throws IOException { @Override public void addHandler(Class messageClass, MessageHandler handler) { - Collection handlers = this.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); + Collection> handlers = this.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); handlers.add(handler); } @@ -134,7 +134,7 @@ public void addHandlerIfNotExists(Class messageClass, Message @Override public void removeHandler(Class messageClass, MessageHandler handler) { - Collection handlers = this.handlers.get(messageClass); + Collection> handlers = this.handlers.get(messageClass); if (handlers != null) { handlers.remove(handler); } @@ -142,11 +142,11 @@ public void removeHandler(Class messageClass, MessageHandler @Override public void receive(Message message) { - Collection handlers = this.handlers.get(message.getClass()); + Collection> handlers = this.handlers.get(message.getClass()); // Pass the message to every defined handler if (handlers != null) { - for (MessageHandler handler : handlers) { + for (MessageHandler handler : handlers) { handler.handleMessage(message); } } diff --git a/implementation/gameengine/common/src/main/java/org/inaetics/dronessimulator/gameengine/common/state/Drone.java b/implementation/gameengine/common/src/main/java/org/inaetics/dronessimulator/gameengine/common/state/Drone.java index 5948f295..5f4769c0 100644 --- a/implementation/gameengine/common/src/main/java/org/inaetics/dronessimulator/gameengine/common/state/Drone.java +++ b/implementation/gameengine/common/src/main/java/org/inaetics/dronessimulator/gameengine/common/state/Drone.java @@ -20,7 +20,7 @@ public class Drone extends HealthGameEntity { private final String teamname; @Getter - private D3Vector targetLocation; + private final D3Vector targetLocation; //Test constructor public Drone(int id, String teamname, D3Vector position, D3Vector velocity, D3Vector acceleration, diff --git a/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/GameEngine.java b/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/GameEngine.java index ad2c2d78..53dbca45 100644 --- a/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/GameEngine.java +++ b/implementation/gameengine/gameengine/src/main/java/org/inaetics/dronessimulator/gameengine/GameEngine.java @@ -71,7 +71,7 @@ public class GameEngine { private volatile ArchitectureEventController m_architectureEventListener; - private List lobbiedDrones = new ArrayList<>(); + private final List lobbiedDrones = new ArrayList<>(); /** * Concrete message handlers. diff --git a/implementation/gameengine/gamestate-manager/src/main/java/org/inaetics/dronessimulator/gameengine/gamestatemanager/GameStateManager.java b/implementation/gameengine/gamestate-manager/src/main/java/org/inaetics/dronessimulator/gameengine/gamestatemanager/GameStateManager.java index a561ef98..a794c56e 100644 --- a/implementation/gameengine/gamestate-manager/src/main/java/org/inaetics/dronessimulator/gameengine/gamestatemanager/GameStateManager.java +++ b/implementation/gameengine/gamestate-manager/src/main/java/org/inaetics/dronessimulator/gameengine/gamestatemanager/GameStateManager.java @@ -74,9 +74,7 @@ public List getWithType(EntityType type) { * Starts the game state manager service. */ public void start() { - m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, (SimulationState fromState, SimulationAction action, SimulationState toState) -> { - this.state.clear(); - }); + m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.state.clear()); Logger.getLogger(GameStateManager.class).info("Started GameState Manager!"); } diff --git a/implementation/gameengine/identifier-mapper/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameIdentifierMapper.java b/implementation/gameengine/identifier-mapper/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameIdentifierMapper.java index 754c5259..4c84395e 100644 --- a/implementation/gameengine/identifier-mapper/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameIdentifierMapper.java +++ b/implementation/gameengine/identifier-mapper/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameIdentifierMapper.java @@ -23,13 +23,9 @@ public void testConcurrent() { int amount = 30000; ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); - concurrentAddAndGenerate.addJob(amount, 10, (i) -> { - id_mapper.setMapping(i, Integer.toString(i)); - }); + concurrentAddAndGenerate.addJob(amount, 10, (i) -> id_mapper.setMapping(i, Integer.toString(i))); - concurrentAddAndGenerate.addJob(amount, 10, (i) -> { - queue.add(id_mapper.getNewGameEngineId()); - }); + concurrentAddAndGenerate.addJob(amount, 10, (i) -> queue.add(id_mapper.getNewGameEngineId())); concurrentAddAndGenerate.start(); concurrentAddAndGenerate.waitTillDone(); @@ -44,9 +40,7 @@ public void testConcurrent() { ConcurrentExecute concurrentRemove = new ConcurrentExecute(100); - concurrentRemove.addJob(amount, 10, (i) -> { - id_mapper.removeMapping(i); - }); + concurrentRemove.addJob(amount, 10, (i) -> id_mapper.removeMapping(i)); concurrentRemove.start(); concurrentRemove.waitTillDone(); diff --git a/implementation/gameengine/physicsengine/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameEntityManager.java b/implementation/gameengine/physicsengine/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameEntityManager.java index a46a4de9..3e68d5b7 100644 --- a/implementation/gameengine/physicsengine/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameEntityManager.java +++ b/implementation/gameengine/physicsengine/src/test/java/org/inaetics/dronessimulator/gameengine/test/TestGameEntityManager.java @@ -32,9 +32,7 @@ public void testConcurrentModifications() { for(int i = 0; i < 300; i++) { final int i_ = i; - concurrentExecuteAdd.addJob(1, 100, (j) -> { - this.manager.addInsert(new Entity(i_, new Size(0,0,0))); - }); + concurrentExecuteAdd.addJob(1, 100, (j) -> this.manager.addInsert(new Entity(i_, new Size(0, 0, 0)))); } for(int i = 0; i < 300; i++) { diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/AbstractGameFinishedRule.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/AbstractGameFinishedRule.java index d6cce7e3..a45037e2 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/AbstractGameFinishedRule.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/AbstractGameFinishedRule.java @@ -16,8 +16,8 @@ @Log4j public abstract class AbstractGameFinishedRule extends Rule { protected final IdentifierMapper idMapper; - private AtomicBoolean gameFinishedEventWasSend = new AtomicBoolean(false); - private AtomicBoolean wasGameFinishedInPreviousRun = new AtomicBoolean(false); + private final AtomicBoolean gameFinishedEventWasSend = new AtomicBoolean(false); + private final AtomicBoolean wasGameFinishedInPreviousRun = new AtomicBoolean(false); public AbstractGameFinishedRule(IdentifierMapper idMapper) { this.idMapper = idMapper; diff --git a/implementation/pubsub/javaserializer/src/test/java/org/inaetics/dronessimulator/pubsub/javaserializer/TestMessage.java b/implementation/pubsub/javaserializer/src/test/java/org/inaetics/dronessimulator/pubsub/javaserializer/TestMessage.java index a1be4f16..1cad2295 100644 --- a/implementation/pubsub/javaserializer/src/test/java/org/inaetics/dronessimulator/pubsub/javaserializer/TestMessage.java +++ b/implementation/pubsub/javaserializer/src/test/java/org/inaetics/dronessimulator/pubsub/javaserializer/TestMessage.java @@ -6,7 +6,7 @@ * Message implementation to use for testing the Java serializer. */ public class TestMessage implements Message { - private transient String notSerialized; + private final transient String notSerialized; private String message; public TestMessage(String message) { diff --git a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java index 5b58ad2c..9b88bc25 100644 --- a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java +++ b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java @@ -57,7 +57,7 @@ public abstract class RabbitConnection implements ManagedService { /** * Collection of topics for which an exchange has been declared. */ - private Collection declaredTopics; + private final Collection declaredTopics; /** * Sets up the connection for use. diff --git a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnectionInfo.java b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnectionInfo.java index b75f9a00..e195e936 100644 --- a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnectionInfo.java +++ b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnectionInfo.java @@ -5,9 +5,7 @@ import lombok.RequiredArgsConstructor; import org.apache.log4j.Logger; import org.inaetics.dronessimulator.discovery.api.Discoverer; -import org.inaetics.dronessimulator.discovery.api.discoverynode.DiscoveryStoredNode; import org.inaetics.dronessimulator.discovery.api.instances.RabbitInstance; -import org.osgi.service.cm.ConfigurationException; import java.net.URISyntaxException; import java.security.KeyManagementException; diff --git a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitMessageConsumer.java b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitMessageConsumer.java index 95bc3074..8588999b 100644 --- a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitMessageConsumer.java +++ b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitMessageConsumer.java @@ -13,7 +13,7 @@ */ class RabbitMessageConsumer extends DefaultConsumer { /** A RabbitMQ subscriber instance. */ - private RabbitSubscriber subscriber; + private final RabbitSubscriber subscriber; /** * Instantiates a new RabbitMQ consumer based on the given subscriber. diff --git a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java index 9c9e9b77..dc2d5740 100644 --- a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java +++ b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java @@ -28,7 +28,7 @@ public class RabbitSubscriber extends RabbitConnection implements Subscriber { private String identifier; /** The handlers for each message class this subscriber processes. */ - private static Map, Collection> handlers = new HashMap<>(); + private static final Map, Collection>> handlers = new HashMap<>(); /** The topics this subscriber is subscribed to. */ private Map topics; @@ -137,7 +137,7 @@ public void removeTopic(Topic topic) throws IOException { @Override public void addHandler(Class messageClass, MessageHandler handler) { // Create new set for this message class if needed - Collection handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); + Collection> handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); handlers.add(handler); logger.debug("Handler {} set for message class {}", handler, messageClass); } @@ -145,7 +145,7 @@ public void addHandler(Class messageClass, MessageHandler han @Override public void addHandlerIfNotExists(Class messageClass, MessageHandler handler) { // Create new set for this message class if needed - Collection handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); + Collection> handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); if (handlers.stream().filter(h -> h.getClass().equals(handler.getClass())).count() == 0) { handlers.add(handler); logger.debug("Handler {} set for message class {}", handler, messageClass); @@ -159,7 +159,7 @@ public void addHandlerIfNotExists(Class messageClass, Message */ @Override public void removeHandler(Class messageClass, MessageHandler handler) { - Collection handlers = RabbitSubscriber.handlers.get(messageClass); + Collection> handlers = RabbitSubscriber.handlers.get(messageClass); // Remove the handler for the class if any handler set is defined if (handlers != null) { @@ -184,11 +184,11 @@ public void receive(Message message) { } // apparently not a compressed message, lets continue - Collection handlers = RabbitSubscriber.handlers.get(message.getClass()); + Collection> handlers = RabbitSubscriber.handlers.get(message.getClass()); // Pass the message to every defined handler if (handlers != null) { - for (MessageHandler handler : handlers) { + for (MessageHandler handler : handlers) { handler.handleMessage(message); } } else { diff --git a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/RabbitIT.java b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/RabbitIT.java index 0df1953d..19bcc2e7 100644 --- a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/RabbitIT.java +++ b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/RabbitIT.java @@ -4,7 +4,6 @@ import com.rabbitmq.client.ConnectionFactory; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.Topic; -import org.inaetics.dronessimulator.pubsub.rabbitmq.common.RabbitConnectionInfo; import org.inaetics.dronessimulator.pubsub.rabbitmq.publisher.PublisherRunner; import org.inaetics.dronessimulator.pubsub.rabbitmq.subscriber.SubscriberRunner; import org.junit.Before; diff --git a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java index 95501ab4..bc430571 100644 --- a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java +++ b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java @@ -18,13 +18,13 @@ public class PublisherRunner implements Runnable { public static final long SLEEP_TIME = 1000; /** The publisher under test. */ - private RabbitPublisher publisher; + private final RabbitPublisher publisher; /** The topic to send test messages on. */ - private Topic topic; + private final Topic topic; /** List of messages to send. */ - private ArrayList testMessages; + private final ArrayList testMessages; /** * @param connectionFactory The connection settings to use for tests. diff --git a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java index f4027447..a2161dc7 100644 --- a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java +++ b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java @@ -17,19 +17,19 @@ */ public class SubscriberRunner implements Runnable { /** The identifier of the subscriber queue. */ - private String identifier; + private final String identifier; /** The subscriber under test. */ - private RabbitSubscriber subscriber; + private final RabbitSubscriber subscriber; /** The topic to receive test messages from. */ - private Topic topic; + private final Topic topic; /** List of received messages. */ - private ArrayList testMessages; + private final ArrayList testMessages; /** Time to wait for messages. */ - private long timeout; + private final long timeout; /** * @param connectionFactory The connection settings to use for tests. diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java index 36ba918b..4ab63f03 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java @@ -20,7 +20,7 @@ public abstract class Drone extends BaseEntity { /** Current hitpoints of a drone */ private int currentHP; /** Text attribute containing the height */ - private Text heightText; + private final Text heightText; /** * Creates a drone based on a sprite diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java index 6bbf5370..72279a13 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java @@ -100,7 +100,7 @@ public class Game extends Application { /** * check to see if the method onRabbitConnect is executed */ - private AtomicBoolean onRabbitConnectExecuted = new AtomicBoolean(false); + private final AtomicBoolean onRabbitConnectExecuted = new AtomicBoolean(false); private Stage primaryStage; /** * counter for the logger to output once every 100 times @@ -114,7 +114,7 @@ public class Game extends Application { * Close event handler * When the window closes, rabbitmq and the discoverer disconnect */ - private EventHandler onCloseEventHandler = new EventHandler() { + private final EventHandler onCloseEventHandler = new EventHandler() { boolean isClosed = false; @Override @@ -138,7 +138,7 @@ public void handle(WindowEvent t) { }; private RabbitConnectionInfo rabbitConnectionInfo; - private Instance visualisationInstance = new Instance(Type.SERVICE, org.inaetics.dronessimulator.discovery.api.discoverynode.Group.SERVICES, "visualisation", new HashMap<>()); + private final Instance visualisationInstance = new Instance(Type.SERVICE, org.inaetics.dronessimulator.discovery.api.discoverynode.Group.SERVICES, "visualisation", new HashMap<>()); /** * Instantiates a new game object diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java index e291deb5..471f3d2b 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java @@ -10,9 +10,9 @@ public class NodeGestures { /** mouse drag context */ - private DragContext nodeDragContext = new DragContext(); + private final DragContext nodeDragContext = new DragContext(); /** Canvas that can be zoomed and panned */ - private PannableCanvas canvas; + private final PannableCanvas canvas; /** * Instantiates the listeners for making the canvas pannable and zoomable @@ -27,24 +27,20 @@ public NodeGestures(PannableCanvas canvas) { * * This sets the x and y coordinates, which are used when dragging */ - private EventHandler onMousePressedEventHandler = new EventHandler() { + private EventHandler onMousePressedEventHandler = event -> { - public void handle(MouseEvent event) { - - // left mouse button => dragging - if (!event.isPrimaryButtonDown()) - return; - - nodeDragContext.mouseAnchorX = event.getSceneX(); - nodeDragContext.mouseAnchorY = event.getSceneY(); + // left mouse button => dragging + if (!event.isPrimaryButtonDown()) + return; - Node node = (Node) event.getSource(); - System.out.format("%10f,%10f%n", node.getTranslateX(), node.getTranslateY()); + nodeDragContext.mouseAnchorX = event.getSceneX(); + nodeDragContext.mouseAnchorY = event.getSceneY(); - nodeDragContext.translateAnchorX = node.getTranslateX(); - nodeDragContext.translateAnchorY = node.getTranslateY(); + Node node = (Node) event.getSource(); + System.out.format("%10f,%10f%n", node.getTranslateX(), node.getTranslateY()); - } + nodeDragContext.translateAnchorX = node.getTranslateX(); + nodeDragContext.translateAnchorY = node.getTranslateY(); }; diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/PannableCanvas.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/PannableCanvas.java index 89c0efcc..d33953f2 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/PannableCanvas.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/PannableCanvas.java @@ -12,7 +12,7 @@ */ public class PannableCanvas extends Pane { /** Scale of the canvas */ - private DoubleProperty myScale = new SimpleDoubleProperty(1.0); + private final DoubleProperty myScale = new SimpleDoubleProperty(1.0); /** * Instantiates a new pannable and zoomable canvas diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/SceneGestures.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/SceneGestures.java index 1cec095a..4dc5bf45 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/SceneGestures.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/SceneGestures.java @@ -15,10 +15,10 @@ public class SceneGestures { private static final double MIN_SCALE = 1.0d; /** Mouse drag context */ - private DragContext sceneDragContext = new DragContext(); + private final DragContext sceneDragContext = new DragContext(); /** Pannable and zommable canvas */ - private PannableCanvas canvas; + private final PannableCanvas canvas; /** Handlers for the mouse events */ public SceneGestures(PannableCanvas canvas) { @@ -52,7 +52,7 @@ public EventHandler getOnScrollEventHandler() { /** * The on mouse pressed event handler */ - private EventHandler onMousePressedEventHandler = new EventHandler() { + private final EventHandler onMousePressedEventHandler = new EventHandler() { /** * Sets the current position of the mouse and the current position on the canvas @@ -75,7 +75,7 @@ public void handle(MouseEvent event) { /** * The on mouse dragged event handler */ - private EventHandler onMouseDraggedEventHandler = new EventHandler() { + private final EventHandler onMouseDraggedEventHandler = new EventHandler() { /** * Moves the canvas within @@ -100,7 +100,7 @@ public void handle(MouseEvent event) { /** * Mouse wheel handler: zoom to pivot point */ - private EventHandler onScrollEventHandler = new EventHandler() { + private final EventHandler onScrollEventHandler = new EventHandler() { /** * Zooms the canvas to the mouse pointer diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java index df7ea742..4958130c 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java @@ -7,10 +7,9 @@ import javafx.stage.Window; import org.apache.log4j.Logger; import org.inaetics.dronessimulator.common.protocol.GameFinishedMessage; -import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; -public class GameFinishedHandler implements MessageHandler { +public class GameFinishedHandler implements MessageHandler { private static final Logger logger = Logger.getLogger(GameFinishedHandler.class); private final Window mainWindow; @@ -19,9 +18,7 @@ public GameFinishedHandler(Window mainWindow) { } @Override - public void handleMessage(Message message) { - GameFinishedMessage gameFinishedMessage = (GameFinishedMessage) message; - + public void handleMessage(GameFinishedMessage gameFinishedMessage) { // Avoid throwing IllegalStateException by running from a non-JavaFX thread. Platform.runLater(() -> { Alert alert = new Alert(Alert.AlertType.INFORMATION); diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/KillMessageHandler.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/KillMessageHandler.java index 6a765d3c..ce856d0b 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/KillMessageHandler.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/KillMessageHandler.java @@ -1,7 +1,6 @@ package org.inaetics.dronessimulator.visualisation.messagehandlers; import org.inaetics.dronessimulator.common.protocol.KillMessage; -import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; import org.inaetics.dronessimulator.visualisation.BaseEntity; @@ -10,7 +9,7 @@ /** * The kill message handler class. Implements what to do when an entity (bullet, drone) is killed/removed. */ -public class KillMessageHandler implements MessageHandler { +public class KillMessageHandler implements MessageHandler { /** all the entities in the game */ private final ConcurrentMap entities; @@ -24,11 +23,10 @@ public KillMessageHandler(ConcurrentMap entities) { /** * Retrieve the entity from the message and then from the entitites map and then delete it - * @param message The received message. + * @param killMessage The received message. */ @Override - public void handleMessage(Message message) { - KillMessage killMessage = (KillMessage) message; + public void handleMessage(KillMessage killMessage) { BaseEntity baseEntity = entities.get(killMessage.getIdentifier()); if(baseEntity != null) { diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java index 140ee02c..616f70c8 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java @@ -4,7 +4,6 @@ import org.inaetics.dronessimulator.common.protocol.StateMessage; import org.inaetics.dronessimulator.common.vector.D3PolarCoordinate; import org.inaetics.dronessimulator.common.vector.D3Vector; -import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; import org.inaetics.dronessimulator.visualisation.BaseEntity; import org.inaetics.dronessimulator.visualisation.Bullet; @@ -17,7 +16,7 @@ /** * The state message handler class. Implements what to do when a new state is send of the entities (drone, bullet) */ -public class StateMessageHandler implements MessageHandler { +public class StateMessageHandler implements MessageHandler { /** Logger */ private static final Logger logger = Logger.getLogger(StateMessageHandler.class); /** UI updates */ @@ -77,12 +76,10 @@ private void createOrUpdateBullet(StateMessage stateMessage) { /** * Updates a drone or creates and/or updates a bullet based on the message - * @param message The received message. + * @param stateMessage The received message. */ @Override - public void handleMessage(Message message) { - StateMessage stateMessage = (StateMessage) message; - + public void handleMessage(StateMessage stateMessage) { switch (stateMessage.getType()) { case DRONE: updateDrone(stateMessage); @@ -91,7 +88,7 @@ public void handleMessage(Message message) { createOrUpdateBullet(stateMessage); break; default: - logger.error("Received state message with unknown entity type! " + message); + logger.error("Received state message with unknown entity type! " + stateMessage); } } } From a6d9a1e0f9c48502da898afbe6f8ab57c1db1e53 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 21 Dec 2017 10:53:47 +0100 Subject: [PATCH 25/27] Fix sonar issues --- .../ArchitectureManager.java | 45 ++++++++++--------- .../dronessimulator/common/Settings.java | 4 ++ .../common/protocol/FireBulletMessage.java | 1 + .../common/vector/D3PolarCoordinate.java | 15 +++---- .../discovery/api/tree/TreeNode.java | 6 +-- .../drone/tactic/Activator.java | 3 +- .../dronessimulator/drone/tactic/Tactic.java | 9 ++-- .../drone/tactic/example/SimpleTactic.java | 23 +++++----- .../utility/CalculateUtilityHelper.java | 7 +-- .../example/utility/TheoreticalTactic.java | 40 ++++++++--------- .../dronessimulator/physicsengine/Entity.java | 17 ++++--- .../physicsengine/EntityManager.java | 2 +- .../physicsengine/PhysicsEngine.java | 4 +- .../rabbitmq/publisher/PublisherRunner.java | 17 +++---- .../rabbitmq/subscriber/SubscriberRunner.java | 14 +++--- .../dronessimulator/visualisation/Bullet.java | 4 +- .../dronessimulator/visualisation/Game.java | 20 +++++---- 17 files changed, 119 insertions(+), 112 deletions(-) diff --git a/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java b/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java index 2ab6b38e..7a86757e 100644 --- a/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java +++ b/implementation/architecture-manager/src/main/java/org/inaetics/dronessimulator/architecturemanager/ArchitectureManager.java @@ -1,6 +1,6 @@ package org.inaetics.dronessimulator.architecturemanager; -import org.apache.log4j.Logger; +import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.architecture.SimulationAction; import org.inaetics.dronessimulator.common.architecture.SimulationState; import org.inaetics.dronessimulator.common.protocol.MessageTopic; @@ -22,6 +22,7 @@ * Currently this only consists of the current lifecycle state of the architecture * Uses Discovery and Subscriber to publish current state and receive requested state updates */ +@Log4j public class ArchitectureManager { /** * Reference to discovery bundle to publish state information @@ -32,11 +33,6 @@ public class ArchitectureManager { */ private volatile Subscriber m_subscriber; - /** - * The logger - */ - private final static Logger logger = Logger.getLogger(ArchitectureManager.class); - /** * The instance published in Discovery */ @@ -88,15 +84,15 @@ public ArchitectureManager(Discoverer discoverer, Subscriber subscriber) { */ public void start() { while (!m_subscriber.hasConnection()){ - logger.warn("Architecture Manager does not yet have a connection to the subscriber implementation... Retrying now!"); + log.warn("Architecture Manager does not yet have a connection to the subscriber implementation... Retrying now!"); try { m_subscriber.connect(); - logger.debug("Connection with the subscriber created"); + log.debug("Connection with the subscriber created"); } catch (IOException e) { - logger.error(e); + log.error(e); } } - logger.info("Starting Architecture Manager..."); + log.info("Starting Architecture Manager..."); try { // Register instance with discovery m_discoverer.register(this.instance); @@ -114,24 +110,29 @@ public void start() { this.previousAction = action; this.currentState = nextState; - logger.info("New transition: (" + this.previousState + ", " + this.previousAction + ", " + this.currentState + ")"); + log.info("New transition: (" + this.previousState + ", " + this.previousAction + ", " + this.currentState + ")"); - try { - instance = m_discoverer.updateProperties(instance, getCurrentProperties()); - } catch (IOException e) { - logger.fatal(e); - } + instance = safeUpdateProperties(instance, getCurrentProperties()); } else { - logger.error(String.format("Received an action which did not led to next state! Current state: %s. Action: %s", currentState, action)); + log.error(String.format("Received an action which did not led to next state! Current state: %s. Action: %s", currentState, action)); } }); } catch(IOException | DuplicateName e) { - logger.fatal(e); + log.fatal(e); } - logger.info("Started Architecture Manager!"); + log.info("Started Architecture Manager!"); + } + + private Instance safeUpdateProperties(final Instance instance, final Map properties) { + try { + return m_discoverer.updateProperties(instance, properties); + } catch (IOException e) { + log.fatal(e); + } + return instance; } /** @@ -139,13 +140,13 @@ public void start() { * Unregisters the current state in Discovery */ public void stop() { - logger.info("Stopping Architecture Manager..."); + log.info("Stopping Architecture Manager..."); try { m_discoverer.unregister(instance); } catch (IOException e) { - logger.error(e); + log.error(e); } - logger.info("Stopped Architecture Manager!"); + log.info("Stopped Architecture Manager!"); } /** diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java index 1953a56f..6cf2a4f1 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/Settings.java @@ -29,6 +29,10 @@ private static String v(String variableName, String defaultValue) { return value != null ? value : defaultValue; } + private Settings() { + throw new IllegalStateException("Utility class"); + } + public static double getTickTime(ChronoUnit temporalUnit) { switch (temporalUnit) { case NANOS: diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/FireBulletMessage.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/FireBulletMessage.java index 12efa807..605d6334 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/FireBulletMessage.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/protocol/FireBulletMessage.java @@ -18,6 +18,7 @@ public class FireBulletMessage extends CreateEntityMessage { */ private String firedById; + @Override public String toString() { return String.format("(FireBulletMessage %s fired by %s, %s)", this.getIdentifier(), this.getFiredById(), this.getDamage()); } diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/vector/D3PolarCoordinate.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/vector/D3PolarCoordinate.java index 5be29dd8..316d9ba2 100644 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/vector/D3PolarCoordinate.java +++ b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/vector/D3PolarCoordinate.java @@ -37,22 +37,19 @@ public D3PolarCoordinate() { * @param length_ The distance to the coordinate. */ public D3PolarCoordinate(double angle1_x_y_, double angle2_x_z_, double length_) { - double angle1_x_y = angle1_x_y_; - double angle2_x_z = angle2_x_z_; - double length = length_; // Change angles to keep the length always positive. - if(length < 0) { - angle1_x_y = angle1_x_y + Math.PI; - angle2_x_z = -1 * angle2_x_z; - length = -1 * length; + if (length_ < 0) { + angle1_x_y_ = angle1_x_y_ + Math.PI; + angle2_x_z_ = -1 * angle2_x_z_; + length_ = -1 * length_; } // Normalize the angles. - Tuple normalizedAngles = normalizeAngles(angle1_x_y, angle2_x_z); + Tuple normalizedAngles = normalizeAngles(angle1_x_y_, angle2_x_z_); this.angle1_x_y = normalizedAngles.getLeft(); this.angle2_x_z = normalizedAngles.getRight(); - this.length = length; + this.length = length_; } /** diff --git a/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/tree/TreeNode.java b/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/tree/TreeNode.java index 26e27e10..82c4e342 100644 --- a/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/tree/TreeNode.java +++ b/implementation/discovery/api/src/main/java/org/inaetics/dronessimulator/discovery/api/tree/TreeNode.java @@ -33,7 +33,7 @@ public abstract class TreeNode, P extends P * @param id The id of the node. */ public TreeNode(String id) { - this(id, (N) null, (P) null); + this(id, null, null); } /** @@ -155,9 +155,9 @@ protected synchronized void setParent(N parent) { } public synchronized String toString() { - String children = this.children.entrySet().stream().map((e) -> " " + e.getValue().toString().replace("\n", "\n ")).reduce("", (r, c) -> r + "\n" + c); + String childrenString = this.children.entrySet().stream().map((e) -> " " + e.getValue().toString().replace("\n", "\n ")).reduce("", (r, c) -> r + "\n" + c); - return "Node " + this.id + (this.values != null ? " " + this.values : "") + children; + return "Node " + this.id + (this.values != null ? " " + this.values : "") + childrenString; } /** diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java index 37fea2d5..fa5e4202 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java @@ -113,7 +113,8 @@ public List getDroneComponents(DependencyManager dm) { .setService(Gun.class) .setRequired(true) ); - }if (componentStrings.contains("radio")) { + } + if (componentStrings.contains("radio")) { components.add(dm.createServiceDependency() .setService(Radio.class) .setRequired(true) diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 60257735..606cc639 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -38,6 +38,8 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { private static final long TACTIC_TIMOUT = 1;//tck private final TimeoutTimer workTimoutTimer = new TimeoutTimer(TACTIC_TIMOUT * Settings.TICK_TIME); + private final AtomicBoolean initialized = new AtomicBoolean(false); + private final TimeoutTimer ticker = new TimeoutTimer(Settings.TICK_TIME); // drone components @Getter protected volatile Radar radar; @@ -65,13 +67,11 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { private volatile Subscriber m_subscriber; private Instance simulationInstance; private boolean registered = false; - private final AtomicBoolean initialized = new AtomicBoolean(false); /** * Discoverer bundle */ @SuppressWarnings("unused") //Assigned through OSGi private volatile Discoverer m_discoverer; - private final TimeoutTimer ticker = new TimeoutTimer(Settings.TICK_TIME); /** * Thread implementation @@ -152,7 +152,8 @@ private void registerSubscriber() { } @Override - public final void destroy() {} + public final void destroy() { + } private void configSimulation() { try { @@ -282,6 +283,8 @@ protected final boolean hasComponents(String... components) { case "radio": result &= radio != null; break; + default: + log.warn(component + " is not a valid component to check for."); } } return result; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java index 1e731083..820ee064 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/SimpleTactic.java @@ -92,10 +92,9 @@ private D3Vector accelerateAfterWall(D3Vector input_acceleration) { y = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); z = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); - if(gps.getPosition().getX() >= MAX_DEVIATION_POSTION){ + if (gps.getPosition().getX() >= MAX_DEVIATION_POSTION) { x = -Settings.MAX_DRONE_ACCELERATION; - } - else if(gps.getPosition().getX() <= 0){ + } else if (gps.getPosition().getX() <= 0) { x = Settings.MAX_DRONE_ACCELERATION; } } @@ -104,10 +103,9 @@ else if(gps.getPosition().getX() <= 0){ x = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); z = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); - if(gps.getPosition().getY() >= MAX_DEVIATION_POSTION){ + if (gps.getPosition().getY() >= MAX_DEVIATION_POSTION) { y = -Settings.MAX_DRONE_ACCELERATION; - } - else if(gps.getPosition().getY() <= 0){ + } else if (gps.getPosition().getY() <= 0) { y = Settings.MAX_DRONE_ACCELERATION; } } @@ -116,10 +114,9 @@ else if(gps.getPosition().getY() <= 0){ x = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); y = ThreadLocalRandom.current().nextDouble(-Settings.MAX_DRONE_ACCELERATION, Settings.MAX_DRONE_ACCELERATION); - if(gps.getPosition().getZ() >= MAX_Z_DEVIATION_POSTION){ + if (gps.getPosition().getZ() >= MAX_Z_DEVIATION_POSTION) { z = -Settings.MAX_DRONE_ACCELERATION; - } - else if(gps.getPosition().getZ() <= 0){ + } else if (gps.getPosition().getZ() <= 0) { z = Settings.MAX_DRONE_ACCELERATION; } } @@ -145,11 +142,11 @@ private void calculateAcceleration() { */ private void calculateGun() { Optional target = radar.getNearestTarget(); - if (target.isPresent()) { - if (target.get().distance_between(gps.getPosition()) <= gun.getMaxDistance()) { - gun.fireBullet(target.get().sub(gps.getPosition()).toPoolCoordinate()); + target.ifPresent(aim -> { + if (aim.distance_between(gps.getPosition()) <= gun.getMaxDistance()) { + gun.fireBullet(aim.sub(gps.getPosition()).toPoolCoordinate()); } - } + }); } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java index 3ddcc6e9..320b4b30 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/CalculateUtilityHelper.java @@ -140,21 +140,22 @@ static class CalculateUtilityParams { } private Collection getEnemies(Map>> teammembers, Collection> radarImage) { - List enemies = radarImage.parallelStream() + List enemiesList = radarImage.parallelStream() .map(Tuple::getRight) //Only get the positions .filter(ral -> teammembers.entrySet().parallelStream().map(tm -> tm.getValue().getB()) //Get positions of the teammembers .filter(t -> t.distance_between(ral) < Settings.MAX_DRONE_VELOCITY).count() == 0) //If there is a teammember close, it must be from that drone, so we only want the // ones that // do NOT have a teammember close .collect(Collectors.toList()); - log.debug("Number of expected enemies(" + radarImage.size() + "+" + teammembers.size() + "): " + (radarImage.size() - teammembers.size()) + ". Number of found enemies" + enemies.size()); - return enemies; + log.debug("Number of expected enemies(" + radarImage.size() + "+" + teammembers.size() + "): " + (radarImage.size() - teammembers.size()) + ". Number of found enemies" + enemiesList.size()); + return enemiesList; } D3Vector getDroneLocation() { return teammembers.get(droneId).getB(); } + @SuppressWarnings("SameParameterValue") boolean droneHasComponent(String component) { return teammembers.get(droneId).getC().contains(component); } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 234a4386..6a1c11b3 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -25,8 +25,6 @@ @NoArgsConstructor //An OSGi constructor public class TheoreticalTactic extends Tactic { public static final double TTL_DRONE = 3 * Settings.getTickTime(ChronoUnit.SECONDS); //seconds - private DroneType droneType; - private String idLeader; /** * This is a list of the teammembers based on the heartbeat messages *

@@ -35,21 +33,23 @@ public class TheoreticalTactic extends Tactic { * The right list of the tuple is a list of available components */ private final Map>> teammembers = new ConcurrentHashMap<>(); + private final TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec + private DroneType droneType; + private String idLeader; private Queue> radarImage = new ConcurrentLinkedQueue<>(); private ManagedThread handleBroadcastMessagesThread; private D3Vector myTargetMoveLocation; - private final TimeoutTimer lastRequestForLeader = new TimeoutTimer(1000); //1 sec private DroneType getType() { - DroneType droneType; + DroneType type; if (hasComponents("radar", "radio")) { - droneType = DroneType.RADAR; + type = DroneType.RADAR; } else if (hasComponents("gun", "radio")) { - droneType = DroneType.GUN; + type = DroneType.GUN; } else { - droneType = null; + type = null; } - return droneType; + return type; } @Override @@ -58,14 +58,14 @@ protected void initializeTactics() { handleBroadcastMessagesThread = new LambdaManagedThread(this::manageIncomingCommunication); handleBroadcastMessagesThread.startThread(); if (DroneType.GUN.equals(droneType)) { - //Send a message if you fire a bullet - gun.registerCallback((fireBulletMessage) -> { - DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FIRED_BULLET_MESSAGE); - shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); - shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); - shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); - radio.send(shotMessage.getMessage()); - }); + //Send a message if you fire a bullet + gun.registerCallback((fireBulletMessage) -> { + DataMessage shotMessage = new DataMessage(this, MyTacticMessage.MESSAGETYPES.FIRED_BULLET_MESSAGE); + shotMessage.getData().put("direction", String.valueOf(fireBulletMessage.getDirection().orElse(null))); + shotMessage.getData().put("velocity", String.valueOf(fireBulletMessage.getVelocity().orElse(null))); + shotMessage.getData().put("firedMoment", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)); + radio.send(shotMessage.getMessage()); + }); } log.debug("Tactic initialized for drone with type " + droneType); } @@ -101,11 +101,9 @@ protected void calculateTactics() { //Leader is not alive findLeader(); //Check if the new found leader is alive. This is done to avoid side-effects of a function - if (lastRequestForLeader.timeIsExceeded()) { - //If it could not find a new (alive) leader, just start shooting if possible to any direction. - if (DroneType.GUN.equals(droneType)) { - randomShooting(); - } + //If it could not find a new (alive) leader, just start shooting if possible to any direction. + if (lastRequestForLeader.timeIsExceeded() && DroneType.GUN.equals(droneType)) { + randomShooting(); } } else if (idLeader.equals(getIdentifier())) { sendInstructions(); diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java index 3d3002a6..9f615e1f 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java @@ -86,14 +86,8 @@ public Entity(int entityId, Size size, D3Vector position, D3Vector velocity, D3V super(entityId, size, position, velocity, acceleration, direction); } - /** - * Returns a deep copy of the given entity. - * - * @param entity The entity to copy. - * @return A copy of the entity. - */ - public static Entity deepcopy(Entity entity) { - return new Entity(entity.getEntityId(), entity.getSize(), entity.getPosition(), entity.getVelocity(), entity + public Entity(Entity entity) { + this(entity.getEntityId(), entity.getSize(), entity.getPosition(), entity.getVelocity(), entity .getAcceleration(), entity.getDirection()); } @@ -223,9 +217,14 @@ public EntityType getType() { return null; } + /** + * Returns a deep copy of the curent entity. + * + * @return A copy of the entity. + */ @Override public GameEntity deepCopy() { - return deepcopy(this); + return new Entity(this); } diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/EntityManager.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/EntityManager.java index 2d10b441..334c1cae 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/EntityManager.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/EntityManager.java @@ -156,7 +156,7 @@ public List copyState() { List result = new ArrayList<>(this.entities.size()); for(Map.Entry e : this.entities.entrySet()) { - result.add(Entity.deepcopy(e.getValue())); + result.add(new Entity(e.getValue())); } return result; diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java index 51419180..e6a0f6b3 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java @@ -156,7 +156,7 @@ private void startCollision(int e1Id, Entity entity1, int e2Id, Entity entity2) // If the collision wasn't happening yet if(observer != null && (startedE1WithE2 || startedE2WithE1)) { //This collision is new and has just started - observer.collisionStartHandler(Entity.deepcopy(entity1), Entity.deepcopy(entity2)); + observer.collisionStartHandler(new Entity(entity1), new Entity(entity2)); } } @@ -169,7 +169,7 @@ private void removeCollision(int e1Id, Entity entity1, int e2Id, Entity entity2) if(observer != null && (e1CollidedWithe2 || e2CollidedWithe1)) { //This collision has just ended - observer.collisionStopHandler(Entity.deepcopy(entity1), Entity.deepcopy(entity2)); + observer.collisionStopHandler(new Entity(entity1), new Entity(entity2)); } } diff --git a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java index bc430571..172189f6 100644 --- a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java +++ b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/PublisherRunner.java @@ -1,18 +1,19 @@ package org.inaetics.dronessimulator.pubsub.rabbitmq.publisher; import com.rabbitmq.client.ConnectionFactory; +import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.discovery.api.Discoverer; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.Topic; import org.inaetics.dronessimulator.pubsub.javaserializer.JavaSerializer; +import java.io.IOException; import java.util.ArrayList; -import static org.junit.Assert.assertTrue; - /** * Runner for the RabbitMQ publisher for use in tests. */ +@Log4j public class PublisherRunner implements Runnable { /** The time to wait between messages. */ public static final long SLEEP_TIME = 1000; @@ -42,20 +43,20 @@ public void run() { try { this.publisher.connect(); - System.out.printf("Publisher %s connected\n", topic.getName()); + log.info(String.format("Publisher %s connected", topic.getName())); for (Message message : this.testMessages) { - assertTrue("Publisher is not connected", publisher.isConnected()); + if (!publisher.isConnected()) log.warn("Publisher is not connected"); Thread.sleep(SLEEP_TIME); this.publisher.send(this.topic, message); - System.out.printf("Publisher %s sent message %s at %d\n", this.topic.getName(), message.toString(), System.currentTimeMillis()); + log.debug(String.format("Publisher %s sent message %s at %d", this.topic.getName(), message.toString(), System.currentTimeMillis())); } - System.out.printf("Publisher %s is done, disconnecting\n", topic.getName()); + log.info(String.format("Publisher %s is done, disconnecting", topic.getName())); this.publisher.disconnect(); - } catch (Exception e) { - e.printStackTrace(); + } catch (IOException | InterruptedException e) { + log.error(e); } } } diff --git a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java index a2161dc7..f03b7bb4 100644 --- a/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java +++ b/implementation/pubsub/rabbitmq/test/src/test/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/SubscriberRunner.java @@ -1,6 +1,7 @@ package org.inaetics.dronessimulator.pubsub.rabbitmq.subscriber; import com.rabbitmq.client.ConnectionFactory; +import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.discovery.api.Discoverer; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; @@ -10,11 +11,10 @@ import java.util.ArrayList; -import static org.junit.Assert.assertTrue; - /** * Runner for the RabbitMQ subscriber for use in tests. */ +@Log4j public class SubscriberRunner implements Runnable { /** The identifier of the subscriber queue. */ private final String identifier; @@ -51,16 +51,16 @@ public void run() { this.subscriber.connect(); this.subscriber.addTopic(this.topic); - System.out.printf("Subscriber %s is connected\n", identifier); - assertTrue("Publisher is not connected", subscriber.isConnected()); + log.info(String.format("Subscriber %s is connected", identifier)); + if (!subscriber.isConnected()) log.warn("Subscriber is not connected"); Thread.sleep(timeout); - System.out.printf("Subscriber %s is done, disconnecting\n", identifier); + log.info(String.format("Subscriber %s is done, disconnecting", identifier)); this.subscriber.disconnect(); } catch (Exception e) { - e.printStackTrace(); + log.error(e); } } @@ -74,7 +74,7 @@ public synchronized ArrayList getTestMessages() { class TestHandler implements MessageHandler { @Override public synchronized void handleMessage(Message message) { - System.out.printf("Subscriber %s received message %s at %d\n", identifier, message.toString(), System.currentTimeMillis()); + log.debug(String.format("Subscriber %s received message %s at %d\n", identifier, message.toString(), System.currentTimeMillis())); testMessages.add(message); } } diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Bullet.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Bullet.java index 116ec3b1..ba28cd34 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Bullet.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Bullet.java @@ -10,7 +10,7 @@ public class Bullet extends BaseEntity { /** Image of the bullet */ - private static final String image = "/bullet.png"; + private static final String IMAGE = "/bullet.png"; /** * Creates a new bullet @@ -18,7 +18,7 @@ public class Bullet extends BaseEntity { * @param uiUpdates - uiupdates */ public Bullet(BlockingQueue uiUpdates) { - super(uiUpdates, image); + super(uiUpdates, IMAGE); this.imageView.setFitHeight(Settings.BULLET_HEIGHT); this.imageView.setId("bullet"); } diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java index 72279a13..72e5ba0f 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java @@ -61,6 +61,10 @@ */ @Log4j public class Game extends Application { + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String URI = "uri"; + public static final String RABBIT_IDENTIFIER = "visualisation"; /** * All the entities in the game */ @@ -291,20 +295,20 @@ private void handleNodeEvent(NodeEvent e) { DiscoveryPath path = node.getPath(); if (path.equals(DiscoveryPath.config(Type.RABBITMQ, org.inaetics.dronessimulator.discovery.api.discoverynode.Group.BROKER, "default"))) { - if (node.getValue("username") != null) { - rabbitConfig.put("username", node.getValue("username")); + if (node.getValue(USERNAME) != null) { + rabbitConfig.put(USERNAME, node.getValue(USERNAME)); } - if (node.getValue("password") != null) { - rabbitConfig.put("password", node.getValue("password")); + if (node.getValue(PASSWORD) != null) { + rabbitConfig.put(PASSWORD, node.getValue(PASSWORD)); } - if (node.getValue("uri") != null) { - rabbitConfig.put("uri", node.getValue("uri")); + if (node.getValue(URI) != null) { + rabbitConfig.put(URI, node.getValue(URI)); } if (rabbitConfig.size() == 3) { - rabbitConnectionInfo = new RabbitConnectionInfo(rabbitConfig.get("username"), rabbitConfig.get("password"), rabbitConfig.get("uri")); + rabbitConnectionInfo = new RabbitConnectionInfo(rabbitConfig.get(USERNAME), rabbitConfig.get(PASSWORD), rabbitConfig.get(URI)); try { connectRabbit(); } catch (IOException e1) { @@ -333,7 +337,7 @@ private void connectRabbit() throws IOException { } // We can connect to localhost, since the visualization does not run within Docker - this.subscriber = new RabbitSubscriber(connectionFactory, "visualisation", new JavaSerializer(), discoverer); + this.subscriber = new RabbitSubscriber(connectionFactory, RABBIT_IDENTIFIER, new JavaSerializer(), discoverer); this.publisher = new RabbitPublisher(connectionFactory, new JavaSerializer(), discoverer); this.subscriber.connect(); From 66ff893c4facd9d4d6946be55c58bd7c8b565f51 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 21 Dec 2017 11:46:07 +0100 Subject: [PATCH 26/27] Fix sonar issues --- .../common/ThreadHelperMethods.java | 27 ---- .../drone/components/engine/Engine.java | 44 ++---- .../drone/components/gps/GPS.java | 1 - .../drone/tactic/TacticStarter.java | 65 --------- .../dronessimulator/physicsengine/Entity.java | 132 ------------------ .../physicsengine/PhysicsEngine.java | 1 + .../PhysicsEngineDriver.java | 3 +- .../ruleprocessors/RuleProcessors.java | 1 - .../gameengine/ruleprocessors/RuleSets.java | 3 + .../rules/deathmatch/CollisionRule.java | 6 +- .../pubsub/javaserializer/JavaSerializer.java | 43 ++---- .../rabbitmq/common/RabbitConnection.java | 5 +- .../rabbitmq/publisher/RabbitPublisher.java | 1 - .../rabbitmq/subscriber/RabbitSubscriber.java | 24 ++-- .../dronessimulator/visualisation/Drone.java | 1 + .../dronessimulator/visualisation/Game.java | 2 +- .../visualisation/controls/NodeGestures.java | 1 - .../messagehandlers/GameFinishedHandler.java | 8 -- .../messagehandlers/StateMessageHandler.java | 5 +- 19 files changed, 48 insertions(+), 325 deletions(-) delete mode 100644 implementation/common/src/main/java/org/inaetics/dronessimulator/common/ThreadHelperMethods.java delete mode 100644 implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TacticStarter.java diff --git a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/ThreadHelperMethods.java b/implementation/common/src/main/java/org/inaetics/dronessimulator/common/ThreadHelperMethods.java deleted file mode 100644 index f5a96eac..00000000 --- a/implementation/common/src/main/java/org/inaetics/dronessimulator/common/ThreadHelperMethods.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.inaetics.dronessimulator.common; - -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; -import java.util.Set; - -public class ThreadHelperMethods { - public static ThreadInfo[] getAllThreads(int a) { - ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); - long[] ids = mxBean.getAllThreadIds(); - return mxBean.getThreadInfo(ids); - } - - public static ThreadGroup getAllThreads(boolean b) { - ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); - ThreadGroup parentGroup; - while ((parentGroup = rootGroup.getParent()) != null) { - rootGroup = parentGroup; - } - return rootGroup; - } - - public static Set getAllThreads() { - return Thread.getAllStackTraces().keySet(); - } -} diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index a1312b35..a3885cbf 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -7,7 +7,6 @@ import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.protocol.MessageTopic; import org.inaetics.dronessimulator.common.protocol.MovementMessage; -import org.inaetics.dronessimulator.common.protocol.TargetMoveLocationMessage; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.drone.components.gps.GPS; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; @@ -115,35 +114,26 @@ public D3Vector stagnate_acceleration(D3Vector input) { * @param input_acceleration The new acceleration for the drone using this component */ public void changeAcceleration(D3Vector input_acceleration) { - log.debug("CHANGED ACCELERATION -> " + input_acceleration); - D3Vector acceleration = input_acceleration; acceleration = this.limit_acceleration(acceleration); - - D3Vector accelerationBetween = acceleration; - acceleration = this.limit_velocity(acceleration); -// acceleration = this.stagnate_acceleration(acceleration); - - log.debug("TESTACC | " + input_acceleration.length() + " | " + accelerationBetween.length() + " | " + acceleration.length() + " | " + m_gps.getVelocity().length() + " | " + m_gps.getVelocity().add(acceleration).length()); - if (Double.isNaN(acceleration.getX()) || Double.isNaN(acceleration.getY()) || Double.isNaN(acceleration.getZ())) { throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + "" + input_acceleration.toString() + ", Output acceleration: " + acceleration.toString()); } Boolean change = true; -// if (lastAcceleration != null) { -// double diffX = Math.abs(lastAcceleration.getX() - acceleration.getX()); -// double diffY = Math.abs(lastAcceleration.getY() - acceleration.getY()); -// double diffZ = Math.abs(lastAcceleration.getZ() - acceleration.getZ()); -// double diffTot = diffX + diffY + diffZ; -// if (diffTot < 3) { -// change = false; -// } -// } + if (lastAcceleration != null) { + double diffX = Math.abs(lastAcceleration.getX() - acceleration.getX()); + double diffY = Math.abs(lastAcceleration.getY() - acceleration.getY()); + double diffZ = Math.abs(lastAcceleration.getZ() - acceleration.getZ()); + double diffTot = diffX + diffY + diffZ; + if (diffTot < 3) { + change = false; + } + } if (change) { lastAcceleration = acceleration; @@ -180,8 +170,6 @@ public D3Vector changeVelocity(D3Vector input) { } - log.debug("CHANGEVELOCITY -> " + output + " | " + output.length() + " | " + m_gps.getAcceleration().length()); - MovementMessage msg = new MovementMessage(); msg.setVelocity(output); msg.setIdentifier(m_drone.getIdentifier()); @@ -195,20 +183,6 @@ public D3Vector changeVelocity(D3Vector input) { return output; } - @Deprecated - public void moveTo(D3Vector location) { - TargetMoveLocationMessage msg = new TargetMoveLocationMessage(); - msg.setTargetLocation(location); - log.info("Move to: " + location.toString()); - msg.setIdentifier(m_drone.getIdentifier()); - - try { - m_publisher.send(MessageTopic.MOVEMENTS, msg); - } catch (IOException e) { - log.fatal(e); - } - } - public final void registerCallback(EngineCallback callback) { callbacks.add(callback); } diff --git a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java index 7246a3a4..14e01c62 100644 --- a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java +++ b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java @@ -97,7 +97,6 @@ public void handleMessage(StateMessage message) { Optional optionalPreviousPosition = previousMessage.getPosition(); Optional optionalPreviousVelocity = previousMessage.getVelocity(); Optional optionalPreviousAcceleration = previousMessage.getAcceleration(); - Optional optionalPreviousDirection = previousMessage.getDirection(); if (deltaMessages <= 0) { //We cannot use two messages that were send at the same time since this will create a NaN. To diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TacticStarter.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TacticStarter.java deleted file mode 100644 index caf32b8f..00000000 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/TacticStarter.java +++ /dev/null @@ -1,65 +0,0 @@ -//package org.inaetics.dronessimulator.drone.tactic; -// -//import java.io.*; -//import org.osgi.framework.launch.*; -//import org.apache.felix.main.AutoProcessor; -// -///** -// * @see http://felix.apache.org/documentation/subprojects/apache-felix-framework/apache-felix-framework-launching-and-embedding.html -// */ -//public class TacticStarter -//{ -// private static Framework m_fwk = null; -// -// public static void main(String[] argv) throws Exception -// { -// // Print welcome banner. -// System.out.println("\nWelcome to My Launcher"); -// System.out.println("======================\n"); -// -// try -// { -// m_fwk = getFrameworkFactory().newFramework(null); -// m_fwk.init(); -// AutoProcessor.process(null, m_fwk.getBundleContext()); -// m_fwk.start(); -// m_fwk.waitForStop(0); -// System.exit(0); -// } -// catch (Exception ex) -// { -// System.err.println("Could not create framework: " + ex); -// ex.printStackTrace(); -// System.exit(-1); -// } -// } -// -// private static FrameworkFactory getFrameworkFactory() throws Exception -// { -// java.net.URL url = TacticStarter.class.getClassLoader().getResource( -// "META-INF/services/org.osgi.framework.launch.FrameworkFactory"); -// if (url != null) -// { -// BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); -// try -// { -// for (String s = br.readLine(); s != null; s = br.readLine()) -// { -// s = s.trim(); -// // Try to load first non-empty, non-commented line. -// if ((s.length() > 0) && (s.charAt(0) != '#')) -// { -// return (FrameworkFactory) Class.forName(s).newInstance(); -// } -// } -// } -// finally -// { -// if (br != null) br.close(); -// } -// } -// -// throw new Exception("Could not find framework factory."); -// } -//} \ No newline at end of file diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java index 9f615e1f..c7af77ad 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java @@ -4,16 +4,12 @@ import lombok.Setter; import lombok.ToString; import lombok.extern.log4j.Log4j; -import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.protocol.EntityType; import org.inaetics.dronessimulator.common.vector.D3PolarCoordinate; import org.inaetics.dronessimulator.common.vector.D3Vector; import org.inaetics.dronessimulator.gameengine.common.Size; import org.inaetics.dronessimulator.gameengine.common.state.GameEntity; -import static org.inaetics.dronessimulator.common.Settings.MAX_DRONE_ACCELERATION; -import static org.inaetics.dronessimulator.common.Settings.MAX_DRONE_VELOCITY; - /** * An entity represented in the simulated world. * x == width, y == depth, z == height @@ -207,11 +203,6 @@ public int hashCode() { return this.getEntityId(); } - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } - @Override public EntityType getType() { return null; @@ -240,129 +231,6 @@ public DroneEntity(int id, Size size, D3Vector position, D3Vector velocity, D3Ve this.targetPosition = targetPosition; } - public D3Vector getAcceleration() { - D3Vector nextAcceleration; - if (getTargetPosition() != null && !targetPosition.equals(new D3Vector()) && !targetPosition.equals(getPosition())) { - nextAcceleration = calculateAccelrationToGetToPosition(getTargetPosition(), getPosition(), getVelocity()); - } else { - nextAcceleration = super.getAcceleration(); - targetPosition = null; - } - return nextAcceleration; - } - - private D3Vector calculateAccelrationToGetToPosition(D3Vector targetLocation, D3Vector currentLocation, D3Vector - currentVelocity) { - log.info("Moving to " + targetLocation.toString() + " from " + currentLocation.toString()); - if (currentLocation.distance_between(targetLocation) < 1) { - if (currentVelocity.length() != 0) { - D3Vector move = limit_acceleration(currentVelocity.scale(-1)); - log.info("WE ARE CLOSE!" + move.toString()); - return move; - } - } else { - D3Vector targetAcceleration; - double distance = currentLocation.distance_between(targetLocation); - double decelDistance = (currentVelocity.length() * currentVelocity.length()) / (2 * Settings - .MAX_DRONE_ACCELERATION); - if (distance > decelDistance) //we are still far, continue accelerating (if possible) - { - targetAcceleration = maximize_acceleration(targetLocation.sub(currentLocation)); - } else //we are about to reach the target, let's start decelerating. - { - targetAcceleration = currentVelocity.normalize().scale(-(currentVelocity.length() * currentVelocity - .length()) / (2 * distance)); - } -// D3Vector move = location.sub(position.add(currentVelocity)); - log.info("WE ARE NOT CLOSE!" + targetAcceleration.toString()); - return targetAcceleration; - } - return new D3Vector(); - } - - /** - * Limit the acceleration - * - * @param input The acceleration to limit - * @return The limited acceleration - */ - public D3Vector limit_acceleration(D3Vector input) { - D3Vector output = input; - // Prevent that the acceleration exceeds te maximum acceleration - if (input.length() > MAX_DRONE_ACCELERATION) { - double correctionFactor = MAX_DRONE_ACCELERATION / input.length(); - output = input.scale(correctionFactor); - } - return output; - } - - /** - * Maximizes the acceleration in the same direction - * - * @param input The vector to scale to the maximal acceleration value - * @return The vector in the same direction as input but length == max acceleration value - */ - public D3Vector maximize_acceleration(D3Vector input) { - D3Vector output = input; - if (input.length() < MAX_DRONE_ACCELERATION && input.length() != 0) { - double correctionFactor = MAX_DRONE_ACCELERATION / input.length(); - output = input.scale(correctionFactor); - } - return output; - } - - - /** - * Limits the velocity when the maximum velocity is archieved. - * - * @param input acceleration as a D3Vector - * @return optimized acceleration as a D3Vector - */ - private D3Vector limit_velocity(D3Vector input) { - D3Vector output = input; - // Check velocity - if (getVelocity().length() >= MAX_DRONE_VELOCITY && getVelocity().add(input).length() >= getVelocity().length()) { - output = new D3Vector(); - } - return output; - } - - /** - * Stagnate the acceleration when the velocity is at 90% of the maximum velocity. - * - * @param input acceleration as a D3Vector - * @return optimized acceleration as a D3Vector - */ - public D3Vector stagnate_acceleration(D3Vector input) { - D3Vector output = input; - // Change acceleration if velocity is close to the maximum velocity - if (getVelocity().length() >= (MAX_DRONE_VELOCITY * 0.9)) { - double maxAcceleration = MAX_DRONE_VELOCITY - getVelocity().length(); - if (Math.abs(output.length()) > Math.abs(maxAcceleration)) { - output = output.scale(maxAcceleration / output.length() == 0 ? 1 : output.length()); - } - } - return output; - } - - /** - * Set the new desired acceleration - * - * @param input_acceleration The new acceleration for the drone using this component - */ - public void changeAcceleration(D3Vector input_acceleration) { - D3Vector acceleration = input_acceleration; - - acceleration = limit_acceleration(acceleration); - acceleration = limit_velocity(acceleration); - acceleration = stagnate_acceleration(acceleration); - if (Double.isNaN(acceleration.getX()) || Double.isNaN(acceleration.getY()) || Double.isNaN(acceleration.getZ())) { - throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + - "" + input_acceleration.toString() + ", Output acceleration: " + acceleration.toString()); - } - - setAcceleration(acceleration); - } } } diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java index e6a0f6b3..d80a1e79 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/PhysicsEngine.java @@ -270,6 +270,7 @@ public void resumeEngine() { @Override public void destroy() { + // Override destroy from thread to do nothing. Will be called as callback by Activator upon destroy of the bundle } @Override diff --git a/implementation/gameengine/physicsenginedriver/src/main/java/org/inaetics/dronessimulator/gameengine/physicsenginedriver/PhysicsEngineDriver.java b/implementation/gameengine/physicsenginedriver/src/main/java/org/inaetics/dronessimulator/gameengine/physicsenginedriver/PhysicsEngineDriver.java index ca35e43a..c610ae10 100644 --- a/implementation/gameengine/physicsenginedriver/src/main/java/org/inaetics/dronessimulator/gameengine/physicsenginedriver/PhysicsEngineDriver.java +++ b/implementation/gameengine/physicsenginedriver/src/main/java/org/inaetics/dronessimulator/gameengine/physicsenginedriver/PhysicsEngineDriver.java @@ -170,8 +170,7 @@ private static Entity gameEntityToPhysicsEntity(GameEntity g) { if (g.getType().equals(EntityType.DRONE)) { size = new Size(10, 10, 10); - return new Entity.DroneEntity(g.getEntityId(), size, g.getPosition(), g.getVelocity(), g.getAcceleration - (), g.getDirection(), ((Drone) g).getTargetLocation()); + return new Entity.DroneEntity(g.getEntityId(), size, g.getPosition(), g.getVelocity(), g.getAcceleration(), g.getDirection(), ((Drone) g).getTargetLocation()); } else if (g.getType().equals(EntityType.BULLET)) { size = new Size(1, 1, 1); } else { diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java index 4aedf640..dda982a1 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleProcessors.java @@ -138,7 +138,6 @@ public void quit() { } @Override - @Deprecated public void destroy() { // Override destroy from thread to do nothing. Will be called as callback by Activator upon destroy of the bundle } diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java index 2248fc4f..58bdcac5 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/RuleSets.java @@ -14,6 +14,9 @@ import java.util.List; public class RuleSets { + private RuleSets() { + throw new IllegalStateException("Utility class"); + } public static List getRulesForGameMode(GameMode gameMode, Publisher publisher, IdentifierMapper idMapper) { List result = new LinkedList<>(); //General rules that are applicable in any game mode diff --git a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/deathmatch/CollisionRule.java b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/deathmatch/CollisionRule.java index 83e00939..6284ee6c 100644 --- a/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/deathmatch/CollisionRule.java +++ b/implementation/gameengine/ruleprocessors/src/main/java/org/inaetics/dronessimulator/gameengine/ruleprocessors/rules/deathmatch/CollisionRule.java @@ -1,7 +1,7 @@ package org.inaetics.dronessimulator.gameengine.ruleprocessors.rules.deathmatch; -import org.apache.log4j.Logger; +import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.common.protocol.EntityType; import org.inaetics.dronessimulator.gameengine.common.gameevent.CollisionStartEvent; import org.inaetics.dronessimulator.gameengine.common.gameevent.DamageEvent; @@ -22,6 +22,7 @@ * Bullet with drone damages the drone and kills the bullet * Drones colliding damages both drones */ +@Log4j public class CollisionRule extends Rule { /** How much damage a collision between drones causes */ private static final int COLLISION_DAMAGE = Drone.DRONE_MAX_HEALTH; @@ -60,7 +61,6 @@ public List process(GameEngineEvent msg) { * @return The events due to the collision */ private List handleCollision(GameEntity e1, GameEntity e2) { - //TODO refactor and make this more efficient for more possible entities. List results = new ArrayList<>(); EntityType e1Type = e1.getType(); @@ -93,7 +93,7 @@ private List handleCollision(GameEntity e1, GameEntity e2) { results.add(new DamageEvent(drone, bullet.getDmg())); } } else { - Logger.getLogger(CollisionRule.class).error("Found a collision with unknown rules. Collision between: {} & {} ", e1Type, e2Type); + log.error("Found a collision with unknown rules. Collision between: {} & {} ", e1Type, e2Type); } return results; diff --git a/implementation/pubsub/javaserializer/src/main/java/org/inaetics/dronessimulator/pubsub/javaserializer/JavaSerializer.java b/implementation/pubsub/javaserializer/src/main/java/org/inaetics/dronessimulator/pubsub/javaserializer/JavaSerializer.java index 88bd2c44..78bb9283 100644 --- a/implementation/pubsub/javaserializer/src/main/java/org/inaetics/dronessimulator/pubsub/javaserializer/JavaSerializer.java +++ b/implementation/pubsub/javaserializer/src/main/java/org/inaetics/dronessimulator/pubsub/javaserializer/JavaSerializer.java @@ -1,6 +1,6 @@ package org.inaetics.dronessimulator.pubsub.javaserializer; -import org.apache.log4j.Logger; +import lombok.extern.log4j.Log4j; import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.serializer.Serializer; @@ -9,54 +9,37 @@ /** * Serializer implementation using the default Java serialization. */ +@Log4j public class JavaSerializer implements Serializer { - /** The logger */ - private static final Logger logger = Logger.getLogger(JavaSerializer.class); - /** * Serializes the given message using the Java object serialization. + * * @param message The message to serialize. * @return The Java serialized message as byte array. * @throws IOException Serialization error in the in-memory output stream. */ public byte[] serialize(Message message) throws IOException { - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(byteStream); - - out.writeObject(message); - -// logger.debug("Message {} serialized to bytes", message.toString()); - - out.close(); - byteStream.close(); - - return byteStream.toByteArray(); + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteStream)) { + out.writeObject(message); + return byteStream.toByteArray(); + } } /** * Deserializes the given byte stream into a message using the Java object deserialization. + * * @param bytes The Java serialized byte array. * @return The message built from the byte array. - * @throws IOException Deserialization error in the in-memory input stream. + * @throws IOException Deserialization error in the in-memory input stream. * @throws ClassNotFoundException The given byte array does not deserialize into an instance of a known class. */ public Message deserialize(byte[] bytes) throws IOException, ClassNotFoundException { - ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); - ObjectInputStream in = new ObjectInputStream(byteStream); - - Message message = null; - - try { - message = (Message) in.readObject(); -// logger.debug("Deserialized message {} from bytes", message.toString()); + try (ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes); ObjectInputStream in = new ObjectInputStream(byteStream)) { + return (Message) in.readObject(); } catch (ClassCastException e) { // This is not a valid message so we can drop it. - logger.warn("Invalid message offered for deserialization, message dropped", e); + log.warn("Invalid message offered for deserialization, message dropped", e); } - - in.close(); - byteStream.close(); - - return message; + return null; } } diff --git a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java index 9b88bc25..a8409d98 100644 --- a/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java +++ b/implementation/pubsub/rabbitmq/common/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/common/RabbitConnection.java @@ -10,7 +10,9 @@ import java.io.IOException; import java.net.ConnectException; -import java.util.*; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashSet; import java.util.concurrent.TimeoutException; /** @@ -228,7 +230,6 @@ protected void declareTopic(Topic topic) throws IOException { } if (!declaredTopics.contains(topic)) { - Map args = new HashMap<>(); channel.exchangeDeclare(topic.getName(), BuiltinExchangeType.TOPIC, false); declaredTopics.add(topic); getLogger().debug("RabbitMQ exchange {} declared", topic.getName()); diff --git a/implementation/pubsub/rabbitmq/publisher/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/RabbitPublisher.java b/implementation/pubsub/rabbitmq/publisher/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/RabbitPublisher.java index 8ff14dc4..3067a112 100644 --- a/implementation/pubsub/rabbitmq/publisher/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/RabbitPublisher.java +++ b/implementation/pubsub/rabbitmq/publisher/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/publisher/RabbitPublisher.java @@ -63,7 +63,6 @@ public void send(Topic topic, Message message) { // Drop null messages and when a serializer is absent if (message != null && serializer != null) { -// logger.debug("Preparing to send message {} to topic {}", message.toString(), topic.getName()); byte[] serializedMessage = serializer.serialize(message); this.channel.basicPublish(topic.getName(), "", new AMQP.BasicProperties.Builder().deliveryMode(1).build(), serializedMessage); logger.debug("Sent message {} to topic {}", message.toString(), topic.getName()); diff --git a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java index 5e835bcf..0225ceae 100644 --- a/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java +++ b/implementation/pubsub/rabbitmq/subscriber/src/main/java/org/inaetics/dronessimulator/pubsub/rabbitmq/subscriber/RabbitSubscriber.java @@ -3,7 +3,6 @@ import com.rabbitmq.client.ConnectionFactory; import lombok.Getter; import org.apache.log4j.Logger; -import org.inaetics.dronessimulator.common.Settings; import org.inaetics.dronessimulator.common.protocol.CompressedProtocolMessage; import org.inaetics.dronessimulator.discovery.api.Discoverer; import org.inaetics.dronessimulator.pubsub.api.Message; @@ -137,17 +136,17 @@ public void removeTopic(Topic topic) throws IOException { @Override public void addHandler(Class messageClass, MessageHandler handler) { // Create new set for this message class if needed - Collection> handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); - handlers.add(handler); + Collection> classHandlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); + classHandlers.add(handler); logger.debug("Handler {} set for message class {}", handler, messageClass); } @Override public void addHandlerIfNotExists(Class messageClass, MessageHandler handler) { // Create new set for this message class if needed - Collection> handlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); - if (handlers.stream().filter(h -> h.getClass().equals(handler.getClass())).count() == 0) { - handlers.add(handler); + Collection> classHandlers = RabbitSubscriber.handlers.computeIfAbsent(messageClass, k -> new HashSet<>()); + if (classHandlers.stream().filter(h -> h.getClass().equals(handler.getClass())).count() == 0) { + classHandlers.add(handler); logger.debug("Handler {} set for message class {}", handler, messageClass); } } @@ -159,11 +158,11 @@ public void addHandlerIfNotExists(Class messageClass, Message */ @Override public void removeHandler(Class messageClass, MessageHandler handler) { - Collection> handlers = RabbitSubscriber.handlers.get(messageClass); + Collection> classHandlers = RabbitSubscriber.handlers.get(messageClass); // Remove the handler for the class if any handler set is defined - if (handlers != null) { - handlers.remove(handler); + if (classHandlers != null) { + classHandlers.remove(handler); logger.debug("Handler {} removed for message class {}", handler, messageClass); } } @@ -184,11 +183,11 @@ public void receive(Message message) { } // apparently not a compressed message, lets continue - Collection> handlers = RabbitSubscriber.handlers.get(message.getClass()); + Collection> classHandlers = RabbitSubscriber.handlers.get(message.getClass()); // Pass the message to every defined handler - if (handlers != null) { - for (MessageHandler handler : handlers) { + if (classHandlers != null) { + for (MessageHandler handler : classHandlers) { handler.handleMessage(message); } } else { @@ -212,7 +211,6 @@ public void connect() throws IOException { // Define queue Map args = new HashMap<>(); -// args.put("x-message-ttl", Settings.TICK_TIME); this.channel.queueDeclare(this.identifier, false, false, true, args); logger.debug("RabbitMQ queue {} declared", this.identifier); diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java index 4ab63f03..eb7b96fd 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Drone.java @@ -42,6 +42,7 @@ public abstract class Drone extends BaseEntity { /** * Updates the height text and calls the parent method */ + @Override void updateUI() { super.updateUI(); heightText.setText("HP: " + currentHP + "/100 Location: " + position.toString(2)); diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java index 72e5ba0f..bc006e7c 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/Game.java @@ -347,7 +347,7 @@ private void connectRabbit() throws IOException { this.subscriber.addHandler(KillMessage.class, new KillMessageHandler(this.entities)); this.subscriber.addHandler(StateMessage.class, new StateMessageHandler(uiUpdates, this.entities)); - this.subscriber.addHandlerIfNotExists(GameFinishedMessage.class, new GameFinishedHandler(primaryStage)); + this.subscriber.addHandlerIfNotExists(GameFinishedMessage.class, new GameFinishedHandler()); this.subscriber.addTopic(MessageTopic.STATEUPDATES); } diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java index 471f3d2b..500c5114 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/controls/NodeGestures.java @@ -37,7 +37,6 @@ public NodeGestures(PannableCanvas canvas) { nodeDragContext.mouseAnchorY = event.getSceneY(); Node node = (Node) event.getSource(); - System.out.format("%10f,%10f%n", node.getTranslateX(), node.getTranslateY()); nodeDragContext.translateAnchorX = node.getTranslateX(); nodeDragContext.translateAnchorY = node.getTranslateY(); diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java index 4958130c..8fba2471 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/GameFinishedHandler.java @@ -4,18 +4,10 @@ import javafx.scene.control.Alert; import javafx.scene.layout.Region; import javafx.stage.StageStyle; -import javafx.stage.Window; -import org.apache.log4j.Logger; import org.inaetics.dronessimulator.common.protocol.GameFinishedMessage; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; public class GameFinishedHandler implements MessageHandler { - private static final Logger logger = Logger.getLogger(GameFinishedHandler.class); - private final Window mainWindow; - - public GameFinishedHandler(Window mainWindow) { - this.mainWindow = mainWindow; - } @Override public void handleMessage(GameFinishedMessage gameFinishedMessage) { diff --git a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java index 616f70c8..a5c2b23d 100644 --- a/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java +++ b/implementation/visualisation/src/main/java/org/inaetics/dronessimulator/visualisation/messagehandlers/StateMessageHandler.java @@ -37,10 +37,9 @@ public StateMessageHandler(BlockingQueue uiUpdates, ConcurrentMap createBullet(stateMessage.getIdentifier())); + BaseEntity currentBullet = entities.computeIfAbsent(stateMessage.getIdentifier(), k -> createBullet()); stateMessage.getPosition().ifPresent(currentBullet::setPosition); stateMessage.getDirection().ifPresent(currentBullet::setDirection); From 819d56724d4abcf413c76122de3db70839db407e Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 21 Dec 2017 15:56:28 +0100 Subject: [PATCH 27/27] Add documentation --- .../drone/components/engine/pom.xml | 4 +- .../drone/components/engine/Engine.java | 69 ++++----- .../drone/components/engine/EngineTest.java | 29 ++-- implementation/drone/components/gps/pom.xml | 5 +- .../drone/components/gps/GPS.java | 132 +++++++++++------- implementation/drone/components/gun/pom.xml | 4 +- .../drone/components/gun/Gun.java | 39 +++--- implementation/drone/components/radar/pom.xml | 4 +- .../drone/components/radar/Activator.java | 4 +- .../drone/components/radar/Radar.java | 64 ++++----- implementation/drone/components/radio/pom.xml | 4 +- .../drone/components/radio/Radio.java | 32 ++--- implementation/drone/drone-init/pom.xml | 4 +- .../drone/droneinit/DroneInit.java | 26 ++-- implementation/drone/tactic/pom.xml | 4 +- .../drone/tactic/Activator.java | 34 ++--- .../dronessimulator/drone/tactic/Tactic.java | 92 ++++-------- .../example/basic/BasicTacticHeartbeat.java | 2 +- .../example/utility/TheoreticalTactic.java | 8 +- .../tactic/example/utility/package-info.java | 1 - .../drone/components/gps/GPSTest.java | 4 +- .../drone/tactic/TacticTesterHelper.java | 4 +- .../dronessimulator/physicsengine/Entity.java | 2 +- 23 files changed, 261 insertions(+), 310 deletions(-) diff --git a/implementation/drone/components/engine/pom.xml b/implementation/drone/components/engine/pom.xml index 475a0e71..df4d9fda 100644 --- a/implementation/drone/components/engine/pom.xml +++ b/implementation/drone/components/engine/pom.xml @@ -1,6 +1,6 @@ - components diff --git a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java index a3885cbf..823ea331 100644 --- a/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java +++ b/implementation/drone/components/engine/src/main/java/org/inaetics/dronessimulator/drone/components/engine/Engine.java @@ -22,20 +22,14 @@ @Log4j @NoArgsConstructor //This is the constructor for OSGi @AllArgsConstructor //This is a constructor for test purposes. -public class Engine { - /** - * The Publisher bundle - */ +public final class Engine { + private final Set callbacks = new HashSet<>(); + /** The Publisher to use for sending messages */ private volatile Publisher m_publisher; - - /** - * The Drone Init bundle - */ - private volatile DroneInit m_drone; private volatile GPS m_gps; - - private Set callbacks = new HashSet<>(); - + /** The drone instance that can be used to get information about the current drone */ + private volatile DroneInit m_drone; + /** The last known acceleration, this might be NULL. */ @Getter private D3Vector lastAcceleration; @@ -45,7 +39,7 @@ public class Engine { * @param input The acceleration to limit * @return The limited acceleration */ - public D3Vector limit_acceleration(D3Vector input) { + public static D3Vector limit_acceleration(D3Vector input) { D3Vector output = input; // Prevent that the acceleration exceeds te maximum acceleration if (input.length() > Settings.MAX_DRONE_ACCELERATION) { @@ -61,7 +55,7 @@ public D3Vector limit_acceleration(D3Vector input) { * @param input The vector to scale to the maximal acceleration value * @return The vector in the same direction as input but length == max acceleration value */ - public D3Vector maximize_acceleration(D3Vector input) { + public static D3Vector maximize_acceleration(D3Vector input) { D3Vector output = input; if (input.length() < Settings.MAX_DRONE_ACCELERATION && input.length() != 0) { double correctionFactor = Settings.MAX_DRONE_ACCELERATION / input.length(); @@ -70,22 +64,24 @@ public D3Vector maximize_acceleration(D3Vector input) { return output; } - /** - * Limits the velocity when the maximum velocity is archieved. + * Limits the velocity when the maximum velocity is achieved. * - * @param input acceleration as a D3Vector - * @return optimized acceleration as a D3Vector + * Note that there are four cases: + * - The current velocity is below the max and the new velocity is also below the max -> do nothing + * - The current velocity is below the max and the new velocity is above the max -> limit + * - The current velocity is above the max and the new velocity is also above the max -> limit + * - The current velocity is above the max and the new velocity is below the max -> do nothing + * + * @param input velocity as a D3Vector + * @return optimized velocity as a D3Vector */ - public D3Vector limit_velocity(D3Vector input) { + private D3Vector limit_velocity(D3Vector input) { D3Vector output = input; - if (m_gps.getVelocity().length() > Settings.MAX_DRONE_VELOCITY && m_gps.getVelocity().add(input).length() > Settings.MAX_DRONE_VELOCITY) { - - double correctionFactor = Settings.MAX_DRONE_VELOCITY / input.length(); - D3Vector outputCorrection = input.scale(correctionFactor); - output = outputCorrection; - } + double correctionFactor = Settings.MAX_DRONE_VELOCITY / input.length(); + output = input.scale(correctionFactor); + } return output; } @@ -116,15 +112,15 @@ public D3Vector stagnate_acceleration(D3Vector input) { public void changeAcceleration(D3Vector input_acceleration) { D3Vector acceleration = input_acceleration; - acceleration = this.limit_acceleration(acceleration); + acceleration = limit_acceleration(acceleration); acceleration = this.limit_velocity(acceleration); if (Double.isNaN(acceleration.getX()) || Double.isNaN(acceleration.getY()) || Double.isNaN(acceleration.getZ())) { - throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + - "" + input_acceleration.toString() + ", Output acceleration: " + acceleration.toString()); + throw new IllegalArgumentException("Acceleration is not a number. Input acceleration: " + input_acceleration.toString() + ", Output acceleration: " + + acceleration.toString()); } - Boolean change = true; + boolean change = true; if (lastAcceleration != null) { double diffX = Math.abs(lastAcceleration.getX() - acceleration.getX()); double diffY = Math.abs(lastAcceleration.getY() - acceleration.getY()); @@ -143,17 +139,17 @@ public void changeAcceleration(D3Vector input_acceleration) { try { m_publisher.send(MessageTopic.MOVEMENTS, msg); + //Run all callbacks + callbacks.forEach(callback -> callback.run(msg)); } catch (IOException e) { log.fatal(e); } - - //Run all callbacks - callbacks.forEach(callback -> callback.run(msg)); } } /** - * Send the new desired velocity to the game-engine + * Send the new desired velocity to the game-engine. This method will respect the max acceleration and the max velocity that is defined in the Settings. + * * @param input The new velocity for the drone using this component */ public D3Vector changeVelocity(D3Vector input) { @@ -176,6 +172,8 @@ public D3Vector changeVelocity(D3Vector input) { try { m_publisher.send(MessageTopic.MOVEMENTS, msg); + //Run all callbacks + callbacks.forEach(callback -> callback.run(msg)); } catch (IOException e) { log.fatal(e); } @@ -183,6 +181,11 @@ public D3Vector changeVelocity(D3Vector input) { return output; } + /** + * Submit a callback-function that is called after each movement update is send. The MovementMessage is a parameter for this callback. + * + * @param callback the function to be called + */ public final void registerCallback(EngineCallback callback) { callbacks.add(callback); } diff --git a/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/EngineTest.java b/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/EngineTest.java index 1fc05668..bf9ddffa 100644 --- a/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/EngineTest.java +++ b/implementation/drone/components/engine/src/test/java/org/inaetics/dronessimulator/drone/components/engine/EngineTest.java @@ -8,12 +8,12 @@ import org.junit.*; import org.junit.contrib.java.lang.system.EnvironmentVariables; -import java.util.HashSet; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class EngineTest { + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); private Engine engine; private MockPublisher publisher; private DroneInit drone; @@ -21,9 +21,6 @@ public class EngineTest { private D3Vector current_velocity; private D3Vector current_acceleration; - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); - @Before public void setup() { publisher = new MockPublisher(); @@ -34,7 +31,7 @@ public void setup() { current_acceleration = new D3Vector(1, 1, 1); when(gps.getVelocity()).thenReturn(current_velocity); when(gps.getAcceleration()).thenReturn(current_acceleration); - engine = new Engine(publisher, drone, gps, new HashSet<>(), null); + engine = new Engine(publisher, gps, drone, null); environmentVariables.set("MAX_DRONE_ACCELERATION", "10"); environmentVariables.set("MAX_DRONE_VELOCITY", "20"); } @@ -42,29 +39,29 @@ public void setup() { @Test public void limit_acceleration() throws Exception { //nul-vectors should be untouched - Assert.assertEquals(new D3Vector(0, 0, 0), engine.limit_acceleration(new D3Vector(0, 0, 0))); + Assert.assertEquals(new D3Vector(0, 0, 0), Engine.limit_acceleration(new D3Vector(0, 0, 0))); //Permitted acceleration should be passed - Assert.assertEquals(new D3Vector(5, 5, 5), engine.limit_acceleration(new D3Vector(5, 5, 5))); - Assert.assertEquals(new D3Vector(-5, -5, -5), engine.limit_acceleration(new D3Vector(-5, -5, -5))); - Assert.assertEquals(new D3Vector(-5, 5, -5), engine.limit_acceleration(new D3Vector(-5, 5, -5))); - Assert.assertEquals(new D3Vector(-5, 5, 5), engine.limit_acceleration(new D3Vector(-5, 5, 5))); + Assert.assertEquals(new D3Vector(5, 5, 5), Engine.limit_acceleration(new D3Vector(5, 5, 5))); + Assert.assertEquals(new D3Vector(-5, -5, -5), Engine.limit_acceleration(new D3Vector(-5, -5, -5))); + Assert.assertEquals(new D3Vector(-5, 5, -5), Engine.limit_acceleration(new D3Vector(-5, 5, -5))); + Assert.assertEquals(new D3Vector(-5, 5, 5), Engine.limit_acceleration(new D3Vector(-5, 5, 5))); //Excessive acceleration should be limited - Assert.assertEquals(new D3Vector(10, 0, 0), engine.limit_acceleration(new D3Vector(1000, 0, 0))); - Assert.assertEquals(new D3Vector(-10, 0, 0), engine.limit_acceleration(new D3Vector(-1000, 0, 0))); + Assert.assertEquals(new D3Vector(10, 0, 0), Engine.limit_acceleration(new D3Vector(1000, 0, 0))); + Assert.assertEquals(new D3Vector(-10, 0, 0), Engine.limit_acceleration(new D3Vector(-1000, 0, 0))); } @Test public void maximize_acceleration() throws Exception { //nul-vectors should be untouched - Assert.assertEquals(new D3Vector(0, 0, 0), engine.maximize_acceleration(new D3Vector(0, 0, 0))); + Assert.assertEquals(new D3Vector(0, 0, 0), Engine.maximize_acceleration(new D3Vector(0, 0, 0))); //Less than max accelaration should be upgraded to the max - Assert.assertEquals(new D3Vector(5.77350269189625764509148780501957, 5.77350269189625764509148780501957, 5.77350269189625764509148780501957), engine.maximize_acceleration(new D3Vector(5, 5, 5))); + Assert.assertEquals(new D3Vector(5.77350269189625764509148780501957, 5.77350269189625764509148780501957, 5.77350269189625764509148780501957), Engine.maximize_acceleration(new D3Vector(5, 5, 5))); //More than max acceleration should be kept the same - Assert.assertEquals(new D3Vector(15, 15, 15), engine.maximize_acceleration(new D3Vector(15, 15, 15))); + Assert.assertEquals(new D3Vector(15, 15, 15), Engine.maximize_acceleration(new D3Vector(15, 15, 15))); } diff --git a/implementation/drone/components/gps/pom.xml b/implementation/drone/components/gps/pom.xml index 074305c0..efd594ac 100644 --- a/implementation/drone/components/gps/pom.xml +++ b/implementation/drone/components/gps/pom.xml @@ -1,6 +1,6 @@ - components @@ -32,7 +32,6 @@ - diff --git a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java index 14e01c62..9f5f7700 100644 --- a/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java +++ b/implementation/drone/components/gps/src/java/org/inaetics/dronessimulator/drone/components/gps/GPS.java @@ -28,17 +28,11 @@ @NoArgsConstructor //OSGi constructor @AllArgsConstructor //Testing constructor public class GPS implements MessageHandler { - /** - * Reference to the Subscriber bundle - */ + private final Set callbacks = new HashSet<>(); + /** The Subscriber to use for receiving messages */ private volatile Subscriber subscriber; - /** - * Reference to the Drone Init bundle - */ + /** The drone instance that can be used to get information about the current drone */ private volatile DroneInit drone; - - private Set callbacks = new HashSet<>(); - private StateMessage previousMessage; /** @@ -68,7 +62,7 @@ public class GPS implements MessageHandler { /** - * FELIX CALLBACKS + * Start the GPS (called from Apache Felix). This initializes to what messages the subscriber should listen. */ public void start() { try { @@ -79,9 +73,6 @@ public void start() { this.subscriber.addHandler(StateMessage.class, this); } - /** - * -- MESSAGEHANDLER - */ public void handleMessage(StateMessage message) { if (message != null && message.getIdentifier().equals(this.drone.getIdentifier())) { //Prepare some variables @@ -92,51 +83,13 @@ public void handleMessage(StateMessage message) { Optional optionalDirection = message.getDirection(); //Check if the message is recent - if (previousMessage != null && deltaNow > Settings.TICK_TIME) { + if (previousMessage != null && deltaNow > Settings.getTickTime(ChronoUnit.MILLIS)) { double deltaMessages = ChronoUnit.MILLIS.between(previousMessage.getTimestamp(), message.getTimestamp()); - Optional optionalPreviousPosition = previousMessage.getPosition(); - Optional optionalPreviousVelocity = previousMessage.getVelocity(); - Optional optionalPreviousAcceleration = previousMessage.getAcceleration(); - if (deltaMessages <= 0) { - //We cannot use two messages that were send at the same time since this will create a NaN. To - // avoid getting this multiple times, we do not update the last message. + //We cannot use two messages that were send at the same time since this will create a NaN. To avoid getting this multiple times, we do not update the last message. return; } - - //Use the previous message to make a better guess of the location the drone probably is. - if (optionalAccelration.isPresent() && optionalPreviousAcceleration.isPresent()) { - D3Vector deltaAcceleration; - if (optionalAccelration.get().equals(D3Vector.ZERO)) { - deltaAcceleration = D3Vector.ZERO; - } else { - deltaAcceleration = optionalAccelration.get().normalize().scale(optionalAccelration.get().sub(optionalPreviousAcceleration.get()).length() / deltaMessages); - } - D3Vector estimatedAcceleration = optionalAccelration.get().add(deltaAcceleration.scale(deltaNow / 1000)); - setAcceleration(estimatedAcceleration); - - if (optionalVelocity.isPresent() && optionalPreviousVelocity.isPresent()) { - D3Vector deltaVelocity; - if (optionalAccelration.get().equals(D3Vector.ZERO)) { - deltaVelocity = D3Vector.ZERO; - } else { - deltaVelocity = optionalVelocity.get().normalize().scale(optionalVelocity.get().sub(optionalPreviousVelocity.get()).length() / deltaMessages); - } - D3Vector estimatedVelocity = optionalVelocity.get().add(deltaVelocity.scale(deltaNow / 1000)).add(estimatedAcceleration.scale(Settings.getTickTime(ChronoUnit.SECONDS))); - setVelocity(estimatedVelocity); - - if (optionalPosition.isPresent() && optionalPreviousPosition.isPresent()) { - D3Vector deltaPosition; - if (optionalAccelration.get().equals(D3Vector.ZERO)) { - deltaPosition = D3Vector.ZERO; - } else { - deltaPosition = optionalPosition.get().normalize().scale(optionalPosition.get().sub(optionalPreviousPosition.get()).length() / deltaMessages); - } - D3Vector estimatedPosition = optionalPosition.get().add(deltaPosition.scale(deltaNow / 1000)).add(estimatedVelocity.scale(Settings.getTickTime(ChronoUnit.SECONDS))); - setPosition(estimatedPosition); - } - } - } + interpolateMessages(deltaNow, deltaMessages, message); } else { optionalPosition.ifPresent(this::setPosition); optionalAccelration.ifPresent(this::setAcceleration); @@ -149,6 +102,77 @@ public void handleMessage(StateMessage message) { } } + /** + * Interpolate the position, velocity and acceleration from the previous message and the current message. The estimated values are also set to the corresponding + * fields. This method makes use of the other interpolate methods to achieve this. + * + * @param deltaNow the difference between the moment the message was send and the current time. + * @param deltaMessages the difference between the moment the previous message was send and the current message was send. + * @param message The current message + */ + private void interpolateMessages(double deltaNow, double deltaMessages, StateMessage message) { + Optional optionalPosition = message.getPosition(); + Optional optionalVelocity = message.getVelocity(); + Optional optionalAccelration = message.getAcceleration(); + Optional optionalPreviousPosition = previousMessage.getPosition(); + Optional optionalPreviousVelocity = previousMessage.getVelocity(); + Optional optionalPreviousAcceleration = previousMessage.getAcceleration(); + + //Use the previous message to make a better guess of the location the drone probably is. + if (optionalAccelration.isPresent() && optionalPreviousAcceleration.isPresent()) { + D3Vector estimatedAcceleration = interpolateAcceleration(deltaNow, deltaMessages, optionalAccelration.get(), optionalPreviousAcceleration.get()); + + if (optionalVelocity.isPresent() && optionalPreviousVelocity.isPresent()) { + D3Vector estimatedVelocity = interpolateVelocity(deltaNow, deltaMessages, optionalVelocity.get(), optionalAccelration.get(), optionalPreviousVelocity.get(), + estimatedAcceleration); + + if (optionalPosition.isPresent() && optionalPreviousPosition.isPresent()) { + interpolatePosition(deltaNow, deltaMessages, optionalPosition.get(), optionalAccelration.get(), optionalPreviousPosition.get(), estimatedVelocity); + } + } + } + } + + private void interpolatePosition(double deltaNow, double deltaMessages, D3Vector optionalPosition, D3Vector optionalAccelration, D3Vector optionalPreviousPosition, D3Vector estimatedVelocity) { + D3Vector deltaPosition; + if (optionalAccelration.equals(D3Vector.ZERO)) { + deltaPosition = D3Vector.ZERO; + } else { + deltaPosition = optionalPosition.normalize().scale(optionalPosition.sub(optionalPreviousPosition).length() / deltaMessages); + } + D3Vector estimatedPosition = optionalPosition.add(deltaPosition.scale(deltaNow / 1000)).add(estimatedVelocity.scale(Settings.getTickTime(ChronoUnit.SECONDS))); + setPosition(estimatedPosition); + } + + private D3Vector interpolateVelocity(double deltaNow, double deltaMessages, D3Vector optionalVelocity, D3Vector optionalAccelration, D3Vector optionalPreviousVelocity, D3Vector estimatedAcceleration) { + D3Vector deltaVelocity; + if (optionalAccelration.equals(D3Vector.ZERO)) { + deltaVelocity = D3Vector.ZERO; + } else { + deltaVelocity = optionalVelocity.normalize().scale(optionalVelocity.sub(optionalPreviousVelocity).length() / deltaMessages); + } + D3Vector estimatedVelocity = optionalVelocity.add(deltaVelocity.scale(deltaNow / 1000)).add(estimatedAcceleration.scale(Settings.getTickTime(ChronoUnit.SECONDS))); + setVelocity(estimatedVelocity); + return estimatedVelocity; + } + + private D3Vector interpolateAcceleration(double deltaNow, double deltaMessages, D3Vector acceleration, D3Vector previousAcceleration) { + D3Vector deltaAcceleration; + if (acceleration.equals(D3Vector.ZERO)) { + deltaAcceleration = D3Vector.ZERO; + } else { + deltaAcceleration = acceleration.normalize().scale(acceleration.sub(previousAcceleration).length() / deltaMessages); + } + D3Vector estimatedAcceleration = acceleration.add(deltaAcceleration.scale(deltaNow / 1000)); + setAcceleration(estimatedAcceleration); + return estimatedAcceleration; + } + + /** + * Submit a callback-function that is called after each state update is received. The StateMessage is a parameter for this callback. + * + * @param callback the function to be called + */ public final void registerCallback(GPSCallback callback) { callbacks.add(callback); } diff --git a/implementation/drone/components/gun/pom.xml b/implementation/drone/components/gun/pom.xml index ea6b98ed..48c34f38 100644 --- a/implementation/drone/components/gun/pom.xml +++ b/implementation/drone/components/gun/pom.xml @@ -1,6 +1,6 @@ - components diff --git a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java index bbfb2395..f1608b5e 100644 --- a/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java +++ b/implementation/drone/components/gun/src/java/org/inaetics/dronessimulator/drone/components/gun/Gun.java @@ -37,17 +37,12 @@ public class Gun { * Maximum time added to {@link BASE_SHOT_TIME_BETWEEN} */ private static final int MAX_OFFSET_SHOT_TIME = 1000; - /** - * Reference to the Publisher bundle - */ + private final Set callbacks = new HashSet<>(); + /** The Publisher to use for sending messages */ private volatile Publisher publisher; - /** - * Reference to the Drone Init bundle - */ + /** The drone instance that can be used to get information about the current drone */ private volatile DroneInit drone; - /** - * Reference to the GPS bundle - */ + /** The GPS that can be used to get the current position, velocity and acceleration */ private volatile GPS gps; /** * Last time the gun has fired @@ -58,32 +53,29 @@ public class Gun { */ private long nextShotAtMs = lastShotAtMs; - private final Set callbacks = new HashSet<>(); - - // -- GETTERS - /** * Gives the fire range of the gun. + * * @return the distance in m */ - public double getMaxDistance(){ + public double getMaxDistance() { return MAX_DISTANCE; } /** * Gives the number of MilliSeconds scince the last shot is fired. - * @return */ - public long msSinceLastShot(){ + public long msSinceLastShot() { return System.currentTimeMillis() - this.lastShotAtMs; } - // -- FUNCTIONS /** - * Fires a bullet in de given direction. + * Fires a bullet in de given direction. If you know the position where the bullet should end, you can use the following code to call this method: + * {@code gun.fireBullet(targetLocation.sub(gps.getPosition()).toPoolCoordinate());} + * * @param direction in which the bullet must be fired. */ - public void fireBullet(D3PolarCoordinate direction){ + public void fireBullet(D3PolarCoordinate direction) { long currentTimeMs = System.currentTimeMillis(); if (currentTimeMs >= nextShotAtMs) { @@ -97,11 +89,11 @@ public void fireBullet(D3PolarCoordinate direction){ msg.setPosition(gps.getPosition()); msg.setAcceleration(new D3Vector()); - try{ + try { publisher.send(MessageTopic.MOVEMENTS, msg); lastShotAtMs = currentTimeMs; nextShotAtMs = lastShotAtMs + BASE_SHOT_TIME_BETWEEN + new Random().nextInt(MAX_OFFSET_SHOT_TIME); - } catch(IOException e){ + } catch (IOException e) { log.fatal(e); } //Run all the callbacks @@ -111,6 +103,11 @@ public void fireBullet(D3PolarCoordinate direction){ } } + /** + * Submit a callback-function that is called after each bullet is fired. The FireBulletMessage is a parameter for this callback. + * + * @param callback the function to be called + */ public final void registerCallback(GunCallback callback) { callbacks.add(callback); } diff --git a/implementation/drone/components/radar/pom.xml b/implementation/drone/components/radar/pom.xml index 9de025dc..cf131827 100644 --- a/implementation/drone/components/radar/pom.xml +++ b/implementation/drone/components/radar/pom.xml @@ -1,6 +1,6 @@ - components diff --git a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Activator.java b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Activator.java index 44b251ac..5257350f 100644 --- a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Activator.java +++ b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Activator.java @@ -28,8 +28,8 @@ public void init(BundleContext bundleContext, DependencyManager dependencyManage .setRequired(true) ) .add(createServiceDependency() - .setService(ArchitectureEventController.class) - .setRequired(true) + .setService(ArchitectureEventController.class) + .setRequired(true) ) ); } diff --git a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java index 4d493b78..c6efb214 100644 --- a/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java +++ b/implementation/drone/components/radar/src/java/org/inaetics/dronessimulator/drone/components/radar/Radar.java @@ -35,7 +35,16 @@ @Log4j @NoArgsConstructor //OSGi constructor @AllArgsConstructor //Testing constructor -public class Radar implements MessageHandler { +public class Radar implements MessageHandler { + /** + * The range of this radar + */ + private static final int RADAR_RANGE = 500; + /** + * Map of all last known entities and their positions (the first string is the id of the entity, the tuple's string is the team name if applicable and the D3Vector is + * the location) + */ + private final Map allEntities = new ConcurrentHashMap<>(); /** * Reference to Architecture Event Controller bundle */ @@ -49,27 +58,15 @@ public class Radar implements MessageHandler { */ private volatile DroneInit m_drone; private volatile Discoverer m_discoverer; - /** * Last known position of this drone */ @Getter @Setter private volatile D3Vector position; - /** - * Map of all last known entities and their positions (the first string is the id of the entity, the tuple's string is the team name if applicable and the D3Vector is the location) - */ - private final Map allEntities = new ConcurrentHashMap<>(); - /** - * The range of this radar - */ - private static final int RADAR_RANGE = 500; /** - * FELIX CALLBACKS - */ - /** - * Adds handlers for discovery, architectureEventController and subscribes on stateUpdates in subscriber. + * Start the Radar (called from Apache Felix). This adds handlers for discovery, architectureEventController and subscribes on stateUpdates in subscriber. */ public void start() { List> removedNodeHandlers = new ArrayList<>(); @@ -92,25 +89,20 @@ public void start() { this.m_subscriber.addHandler(StateMessage.class, this); this.m_subscriber.addHandler(KillMessage.class, this); - m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> allEntities.clear()); + m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, (fromState, action, toState) -> allEntities.clear()); } - /* - * -- GETTERS - */ - /** * Retrieves all last known entities which are in range of this radar * - * @return The entities in range + * @return The locations of the entities in range */ public List getRadar() { List results; if (position != null) { results = allEntities.values() - .stream() + .parallelStream() .filter(drone -> position.distance_between(drone) <= RADAR_RANGE) .collect(Collectors.toList()); } else { @@ -123,51 +115,47 @@ public List getRadar() { /** * Retrieves the nearest target in range * - * @return The nearest entity in range. Note that this could be a teammember + * @return The nearest entity in range. Note that this could be a team member. */ public Optional getNearestTarget() { return getRadar() - .stream() + .parallelStream() .sorted(Comparator.comparingDouble(e -> e.distance_between(position))) .findFirst(); } - //-- MESSAGEHANDLERS - /** - * Handles a recieved message and calls the messagehandlers. + * Handles a recieved message and calls the appropriate message handler. * * @param message The received message. */ public void handleMessage(Message message) { if (message instanceof StateMessage) { - handleStateMessage((StateMessage) message); + handleMessage((StateMessage) message); } else if (message instanceof KillMessage) { - handleKillMessage((KillMessage) message); + handleMessage((KillMessage) message); } } /** - * Handles a stateMessage + * Handles a stateMessage by changing the internal state based on this message. * * @param stateMessage the received stateMessage */ - private void handleStateMessage(StateMessage stateMessage){ - if (stateMessage.getIdentifier().equals(this.m_drone.getIdentifier())){ + private void handleMessage(StateMessage stateMessage) { + if (stateMessage.getIdentifier().equals(this.m_drone.getIdentifier())) { stateMessage.getPosition().ifPresent(this::setPosition); - } else { - if (stateMessage.getType().equals(EntityType.DRONE)) { - stateMessage.getPosition().ifPresent(pos -> this.allEntities.put(stateMessage.getIdentifier(), pos)); - } + } else if (stateMessage.getType().equals(EntityType.DRONE)) { + stateMessage.getPosition().ifPresent(pos -> this.allEntities.put(stateMessage.getIdentifier(), pos)); } } /** - * Handles a killMessage + * Handles a killMessage by removing the killed entity from the internal state representation. * * @param killMessage the received killMessage */ - private void handleKillMessage(KillMessage killMessage) { + private void handleMessage(KillMessage killMessage) { if (killMessage.getEntityType().equals(EntityType.DRONE)) { this.allEntities.remove(killMessage.getIdentifier()); } diff --git a/implementation/drone/components/radio/pom.xml b/implementation/drone/components/radio/pom.xml index bd9174b2..324dfff3 100644 --- a/implementation/drone/components/radio/pom.xml +++ b/implementation/drone/components/radio/pom.xml @@ -1,6 +1,6 @@ - components diff --git a/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java b/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java index 1dfc61df..71dcd573 100644 --- a/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java +++ b/implementation/drone/components/radio/src/main/java/org/inaetics/dronessimulator/drone/components/radio/Radio.java @@ -36,7 +36,7 @@ public class Radio implements MessageHandler { private Topic topic; /** - * FELIX CALLBACKS + * Start the Radio (called from Apache Felix). This initializes to what messages the subscriber should listen. */ public void start() { topic = new TeamTopic(m_drone.getTeamname()); @@ -50,31 +50,25 @@ public void start() { } /** - * Sends a text to other drones through the radio - * @param text the text to send. + * Send a message to all drones that are subscribed to the topic "TeamTopic(teamname)". Note that the radio should also be subscribed to the type of messages that + * you send (See: {@link Radio#start()} for the message types that the Radio is subscribed to). + * + * @param msg the message to send + * @return false if there was an error, true otherwise. */ - - public void sendText(String text){ - - TextMessage msg = new TextMessage(); - msg.setText(text); - try{ - m_publisher.send(topic, msg); - } catch(IOException e){ - log.fatal(e); - } - } - - public void send(Message msg) { - try{ + public boolean send(Message msg) { + try { m_publisher.send(topic, msg); - } catch(IOException e){ + return true; + } catch (IOException e) { log.fatal(e); + return false; } } /** * Retrieves the queue with received messages + * * @return queue with messages */ public final ConcurrentLinkedQueue getMessages() { @@ -82,7 +76,7 @@ public final ConcurrentLinkedQueue getMessages() { } /** - * -- MESSAGEHANDLER + * Receive the messages that the radio is subscribed to and add them to the internal queue. */ public void handleMessage(Message message) { received_queue.add(message); diff --git a/implementation/drone/drone-init/pom.xml b/implementation/drone/drone-init/pom.xml index 05d4a109..4c0eee90 100644 --- a/implementation/drone/drone-init/pom.xml +++ b/implementation/drone/drone-init/pom.xml @@ -1,6 +1,6 @@ - drone diff --git a/implementation/drone/drone-init/src/java/org/inaetics/dronessimulator/drone/droneinit/DroneInit.java b/implementation/drone/drone-init/src/java/org/inaetics/dronessimulator/drone/droneinit/DroneInit.java index d19978d2..61f65618 100644 --- a/implementation/drone/drone-init/src/java/org/inaetics/dronessimulator/drone/droneinit/DroneInit.java +++ b/implementation/drone/drone-init/src/java/org/inaetics/dronessimulator/drone/droneinit/DroneInit.java @@ -20,15 +20,14 @@ */ @Log4j public class DroneInit { - /** - * The identifier of this drone - */ - @Getter @Setter + @Getter + @Setter private String identifier; /** - * Reference to the Discoverer bundle + * The client for the discoverer where the */ + @SuppressWarnings("unused")//This is initialized by Apache Felix private volatile Discoverer m_discoverer; /** @@ -40,9 +39,6 @@ public DroneInit() { this.initIdentifier(); } - /** - * FELIX CALLBACKS - */ /** * On startup register Drone in Discovery. */ @@ -52,7 +48,7 @@ public void start() throws IOException { } /** - * On startup unregister Drone in Discovery. + * On stop unregister Drone in Discovery. */ public void stop() throws IOException { log.info("Stopping the drone now!"); @@ -84,19 +80,15 @@ public String getTeamname() { return teamname; } - - - /** - * Unregister the drone service in Discovery - */ private void unregisterDroneService() throws IOException { this.m_discoverer.unregister(registered_instance); } /** - * Initializes the identifier for this drone - * It checks the following environment variables - * in order: DRONENAME, COMPUTERNAME, HOSTNAME + * Initializes the identifier for this drone. It checks the following environment variables in order: + * - DRONENAME + * - COMPUTERNAME + * - HOSTNAME * If none are found, it generates a random UUID */ public void initIdentifier() { diff --git a/implementation/drone/tactic/pom.xml b/implementation/drone/tactic/pom.xml index b61083e9..887db8b0 100644 --- a/implementation/drone/tactic/pom.xml +++ b/implementation/drone/tactic/pom.xml @@ -1,6 +1,6 @@ - drone diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java index 376aa4e1..770946c8 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Activator.java @@ -14,7 +14,6 @@ import org.inaetics.dronessimulator.drone.components.radio.Radio; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; import org.inaetics.dronessimulator.drone.tactic.example.SimpleTactic; -import org.inaetics.dronessimulator.drone.tactic.example.utility.TheoreticalTactic; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; import org.osgi.framework.BundleContext; @@ -25,41 +24,42 @@ public class Activator extends DependencyActivatorBase { private static final Logger logger = Logger.getLogger(Activator.class); + @Override public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception { Tactic tactic = createNewTactic(); Component component = createComponent() - .setInterface(Tactic.class.getName(), null) - .setImplementation(tactic) - .setCallbacks("init", "startTactic", "stopTactic", "destroy"); + .setInterface(Tactic.class.getName(), null) + .setImplementation(tactic) + .setCallbacks("init", "startTactic", "stopTactic", "destroy"); for (ServiceDependency dep : getDroneComponents(dependencyManager)) { component.add(dep); } component.add( - createServiceDependency() - .setService(ArchitectureEventController.class) - .setRequired(true) + createServiceDependency() + .setService(ArchitectureEventController.class) + .setRequired(true) ); component.add( - createServiceDependency() - .setService(Subscriber.class) - .setRequired(true) + createServiceDependency() + .setService(Subscriber.class) + .setRequired(true) ); component.add( - createServiceDependency() - .setService(Discoverer.class) - .setRequired(true) + createServiceDependency() + .setService(Discoverer.class) + .setRequired(true) ); component.add( - createServiceDependency() - .setService(DroneInit.class) - .setRequired(true) + createServiceDependency() + .setService(DroneInit.class) + .setRequired(true) ); dependencyManager.add(component); @@ -84,7 +84,7 @@ public List getDroneComponents(DependencyManager dm) { envComponents = "radio,gps"; //Default components } envComponents += ",engine"; //A drone always needs an engine, so add that always to the componentlist - logger.info("Create drone with the following components: "+envComponents); + logger.info("Create drone with the following components: " + envComponents); List componentStrings = Arrays.stream(envComponents.split(",")).map(String::trim).filter(c -> !c.isEmpty()).collect(Collectors.toList()); // Inject dependencies based on the defined components diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java index 606cc639..90a8b20d 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/Tactic.java @@ -21,7 +21,6 @@ import org.inaetics.dronessimulator.drone.components.radar.Radar; import org.inaetics.dronessimulator.drone.components.radio.Radio; import org.inaetics.dronessimulator.drone.droneinit.DroneInit; -import org.inaetics.dronessimulator.pubsub.api.Message; import org.inaetics.dronessimulator.pubsub.api.MessageHandler; import org.inaetics.dronessimulator.pubsub.api.subscriber.Subscriber; @@ -35,7 +34,7 @@ * The abstract tactic each drone tactic should extend */ @Log4j -public abstract class Tactic extends ManagedThread implements MessageHandler { +public abstract class Tactic extends ManagedThread implements MessageHandler { private static final long TACTIC_TIMOUT = 1;//tck private final TimeoutTimer workTimoutTimer = new TimeoutTimer(TACTIC_TIMOUT * Settings.TICK_TIME); private final AtomicBoolean initialized = new AtomicBoolean(false); @@ -49,20 +48,16 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { protected volatile Engine engine; @Getter protected volatile Gun gun; - /** - * Drone Init bundle - */ - protected volatile DroneInit m_drone; @Getter protected volatile Radio radio; + /** The drone instance that can be used to get information about the current drone */ + protected volatile DroneInit m_drone; /** - * Architecture Event controller bundle + * Architecture Event controller bundle that is used to listen to state updates. */ @SuppressWarnings("unused") //Assigned through OSGi private volatile ArchitectureEventController m_architectureEventController; - /** - * Subscriber bundle - */ + /** The Subscriber to use for receiving messages */ @SuppressWarnings("unused") //Assigned through OSGi private volatile Subscriber m_subscriber; private Instance simulationInstance; @@ -75,6 +70,8 @@ public abstract class Tactic extends ManagedThread implements MessageHandler { /** * Thread implementation + *

+ * Work calls the calulateTactics everytime the ticker is exceeded. The ticker runs on {@link Settings#TICK_TIME} ms. */ @Override protected final void work() throws InterruptedException { @@ -93,42 +90,19 @@ protected final void work() throws InterruptedException { } /** - * Registers the handlers for the architectureEventController on startup. And registers the subscriber. Starts the - * tactic. + * Registers the handlers for the architectureEventController on startup. And registers the subscriber. Starts the tactic. This is called by Apache Felix. */ public final void startTactic() { - m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.configSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.CONFIG, SimulationAction.START, SimulationState.RUNNING, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.startSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.PAUSE, SimulationState.PAUSED, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.pauseSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.PAUSED, SimulationAction.RESUME, SimulationState.RUNNING, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.resumeSimulation() - ); - - - m_architectureEventController.addHandler(SimulationState.CONFIG, SimulationAction.STOP, SimulationState.INIT, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.stopSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.STOP, SimulationState.INIT, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.stopSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.PAUSED, SimulationAction.STOP, SimulationState.INIT, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.stopSimulation() - ); - - m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.GAMEOVER, SimulationState.DONE, - (SimulationState fromState, SimulationAction action, SimulationState toState) -> this.stopSimulation() - ); + //@formatter:off + m_architectureEventController.addHandler(SimulationState.INIT, SimulationAction.CONFIG, SimulationState.CONFIG, (f,a,t) -> this.configSimulation()); + m_architectureEventController.addHandler(SimulationState.CONFIG, SimulationAction.START, SimulationState.RUNNING, (f,a,t) -> this.startSimulation()); + m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.PAUSE, SimulationState.PAUSED, (f,a,t) -> this.pauseSimulation()); + m_architectureEventController.addHandler(SimulationState.PAUSED, SimulationAction.RESUME, SimulationState.RUNNING, (f,a,t) -> this.resumeSimulation()); + m_architectureEventController.addHandler(SimulationState.CONFIG, SimulationAction.STOP, SimulationState.INIT, (f,a,t) -> this.stopSimulation()); + m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.STOP, SimulationState.INIT, (f,a,t) -> this.stopSimulation()); + m_architectureEventController.addHandler(SimulationState.PAUSED, SimulationAction.STOP, SimulationState.INIT, (f,a,t) -> this.stopSimulation()); + m_architectureEventController.addHandler(SimulationState.RUNNING, SimulationAction.GAMEOVER, SimulationState.DONE, (f,a,t) -> this.stopSimulation()); + //@formatter:on simulationInstance = new TacticInstance(m_drone.getIdentifier()); @@ -137,6 +111,9 @@ public final void startTactic() { super.start(); } + /** + * Stop the thread when Apache Felix calls this method. + */ public final void stopTactic() { this.stopThread(); unconfigSimulation(); @@ -153,6 +130,7 @@ private void registerSubscriber() { @Override public final void destroy() { + //Do nothing on shutdown } private void configSimulation() { @@ -221,31 +199,15 @@ private void stopSimulation() { initialized.set(false); } - //Optionally stop started threads - //TODO - log.info("Stopped drone!"); } - //-- MESSAGEHANDLERS - - /** - * Handles a recieved message and calls the messagehandlers. - * - * @param message The received message. - */ - public final void handleMessage(Message message) { - if (message instanceof KillMessage) { - handleKillMessage((KillMessage) message); - } - } - /** - * Handles a killMessage + * Handles a killMessage by stopping the tactic and exiting the process * * @param killMessage the received killMessage */ - private void handleKillMessage(KillMessage killMessage) { + public void handleMessage(KillMessage killMessage) { if (killMessage.getIdentifier().equals(m_drone.getIdentifier())) { log.info("Found kill message! Quitting for now... Last known movements: \n" + "\tposition: " + gps.getPosition().toString() + "\n" + @@ -296,7 +258,6 @@ protected final void validateRequiredComponents(String... requiredComponents) th } } - /** * Checks if the components are non-null. * @@ -323,7 +284,7 @@ public final Set getAvailableComponents() { } /** - * -- Abstract metods + * Method which is called when the tactic is started. */ protected abstract void initializeTactics(); @@ -342,8 +303,7 @@ public final Set getAvailableComponents() { public class MissingComponentsException extends Exception { public MissingComponentsException(String... requiredComponents) { - super("One of the following components is missing that was required: " + Arrays.toString - (requiredComponents)); + super("One of the following components is missing that was required: " + Arrays.toString(requiredComponents)); } } } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java index 0fe448e3..66534629 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/basic/BasicTacticHeartbeat.java @@ -6,7 +6,7 @@ import static org.inaetics.dronessimulator.drone.tactic.example.basic.ProtocolTags.HEARTBEAT_GUN; import static org.inaetics.dronessimulator.drone.tactic.example.basic.ProtocolTags.HEARTBEAT_RADAR; -public class BasicTacticHeartbeat implements Runnable{ +public class BasicTacticHeartbeat implements Runnable { private BasicTactic tactic; private BasicTacticCommunication comm; diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java index 24cb53ce..12f7a6a9 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/TheoreticalTactic.java @@ -175,8 +175,8 @@ private void manageIncomingCommunication() { ) && droneType.equals(DroneType.RADAR) && idLeader == null) { //Become a leader yourself and prepare setLeader(getIdentifier()); - radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.IS_LEADER_MESSAGE).getMessage()); - log.info("Drone " + getIdentifier() + " is the leader of team " + m_drone.getTeamname()); + if (radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.IS_LEADER_MESSAGE).getMessage())) + log.info("Drone " + getIdentifier() + " is the leader of team " + m_drone.getTeamname()); } else if (MyTacticMessage.checkType(newMessage, MyTacticMessage.MESSAGETYPES.IS_LEADER_MESSAGE)) { setLeader(newMessage.get("id")); @@ -223,8 +223,8 @@ private void moveToLocation(D3Vector targetLocation) { private void findLeader() { if (idLeader == null) { log.debug("Searching for leader"); - radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SEARCH_LEADER_MESSAGE).getMessage()); - lastRequestForLeader.reset(); + if (radio.send(new DataMessage(this, MyTacticMessage.MESSAGETYPES.SEARCH_LEADER_MESSAGE).getMessage())) + lastRequestForLeader.reset(); } } diff --git a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java index 305aa686..532fd926 100644 --- a/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java +++ b/implementation/drone/tactic/src/main/java/org/inaetics/dronessimulator/drone/tactic/example/utility/package-info.java @@ -1,4 +1,3 @@ - /** * This package contains an example tactic that uses a utility function. In the package messages there are the messages that are send between the drones. Note that * drones are only able to receive TacticMessages. This contains a Map with String key-value pairs. The CalculateUtilityHelper does the actual utility calculations so diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/components/gps/GPSTest.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/components/gps/GPSTest.java index 8548ba4a..b29af520 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/components/gps/GPSTest.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/components/gps/GPSTest.java @@ -13,7 +13,6 @@ import java.time.LocalTime; import java.time.temporal.ChronoUnit; -import java.util.HashSet; import static org.inaetics.dronessimulator.test.concurrent.D3VectorMatcher.closeTo; import static org.mockito.Mockito.mock; @@ -27,8 +26,7 @@ public class GPSTest { public void setUp() throws Exception { drone = new DroneInit(); drone.setIdentifier("1"); - gps = new GPS(mock(Subscriber.class), drone, new HashSet<>(), null, D3Vector.UNIT, D3Vector.UNIT, D3Vector - .UNIT, D3PolarCoordinate.UNIT); + gps = new GPS(mock(Subscriber.class), drone, null, D3Vector.UNIT, D3Vector.UNIT, D3Vector.UNIT, D3PolarCoordinate.UNIT); gps.start(); } diff --git a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java index d130ebc4..f843847f 100644 --- a/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java +++ b/implementation/drone/tactic/src/test/java/org/inaetics/dronessimulator/drone/tactic/TacticTesterHelper.java @@ -31,13 +31,13 @@ public static T getTactic(Class tacticClass, Publisher pub T tactic = tacticClass.newInstance(); List componentList = Arrays.asList(components); if (componentList.contains("gps") || components.length == 0) { - tactic.gps = new GPS(subscriber, droneInit, new HashSet<>(), null, D3Vector.UNIT, D3Vector.UNIT, D3Vector + tactic.gps = new GPS(subscriber, droneInit, null, D3Vector.UNIT, D3Vector.UNIT, D3Vector .UNIT, D3PolarCoordinate.UNIT); tactic.gps.start(); } if (componentList.contains("engine") || components.length == 0) { - tactic.engine = new Engine(publisher, droneInit, tactic.gps, new HashSet<>(), null); + tactic.engine = new Engine(publisher, tactic.gps, droneInit, null); } if (componentList.contains("radio") || components.length == 0) { diff --git a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java index c7af77ad..39526763 100644 --- a/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java +++ b/implementation/gameengine/physicsengine/src/main/java/org/inaetics/dronessimulator/physicsengine/Entity.java @@ -18,7 +18,7 @@ @Getter @Setter @ToString -public class Entity extends GameEntity implements Cloneable { +public class Entity extends GameEntity { /** * Creates an entity. *