From 6a6ed3ed44d531511b2551b2d2da7287643ecd64 Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Thu, 18 May 2017 13:32:24 +1200 Subject: [PATCH 1/4] Server sends mark locations to test - Added a timer to send boat location messages containing the mark locations to test the receiver #story[891] --- .../java/seng302/server/ServerThread.java | 50 +++++++++++++++++++ .../seng302/server/simulator/Simulator.java | 17 +++++++ 2 files changed, 67 insertions(+) diff --git a/src/main/java/seng302/server/ServerThread.java b/src/main/java/seng302/server/ServerThread.java index 094845ab..4270b633 100644 --- a/src/main/java/seng302/server/ServerThread.java +++ b/src/main/java/seng302/server/ServerThread.java @@ -1,5 +1,7 @@ package seng302.server; +import seng302.server.simulator.mark.CompoundMark; +import seng302.server.simulator.mark.Mark; import seng302.server.messages.*; import seng302.server.simulator.Boat; import seng302.server.simulator.Simulator; @@ -257,6 +259,53 @@ public class ServerThread implements Runnable, Observer { //Delays the new course xml data for 25 seconds so the boats are able to pass the starting line } + /** + * Starts sending boat location messages containing the mark positions + * Marks are flipped by 90 degrees from their original position + */ + private void startUpdatingMarkPositions(){ + Timer t = new Timer(); + t.schedule(new TimerTask() { + + /** + * Send the mark location message + * @param m The mark to send + * @param offset How far to move the marks from their original position + */ + private void sendMark(Mark m, Double offset){ + Message markLocation = new BoatLocationMessage(m.getSourceID(), server.getSequenceNumber(), + m.getLat()-offset, m.getLng()+offset*2, 0, 0); + + try { + server.send(markLocation); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void run() { + for (CompoundMark m : raceSimulator.getMarks()){ + if (m == null){ + continue; + } + + Mark mark1 = m.getMark1(); + Mark mark2 = m.getMark2(); + + if (mark1 != null){ + sendMark(mark1, 0.0002); + } + + if (mark2 != null){ + sendMark(mark2, 0.0005); + } + + } + } + }, 21000, 1000); + } + public void run() { try{ server = new StreamingServerSocket(PORT_NUMBER); @@ -275,6 +324,7 @@ public class ServerThread implements Runnable, Observer { startSendingRaceStartStatusMessages(); startSendingRaceStatusMessages(); sendPostStartCourseXml(); + startUpdatingMarkPositions(); } /** diff --git a/src/main/java/seng302/server/simulator/Simulator.java b/src/main/java/seng302/server/simulator/Simulator.java index 72d2717a..c279fea5 100644 --- a/src/main/java/seng302/server/simulator/Simulator.java +++ b/src/main/java/seng302/server/simulator/Simulator.java @@ -1,12 +1,15 @@ package seng302.server.simulator; +import seng302.server.simulator.mark.CompoundMark; import seng302.server.simulator.mark.Corner; import seng302.server.simulator.mark.Mark; import seng302.server.simulator.mark.Position; import seng302.server.simulator.parsers.RaceParser; +import java.util.HashSet; import java.util.List; import java.util.Observable; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; public class Simulator extends Observable implements Runnable { @@ -138,4 +141,18 @@ public class Simulator extends Observable implements Runnable { public void setRaceStarted(boolean raceStarted) { isRaceStarted = raceStarted; } + + /** + * @return A list of marks in the race + */ + public Set getMarks(){ + Set marks = new HashSet<>(); + + for (Corner c : course){ + marks.add(c.getCompoundMark()); + marks.add(c.getCompoundMark()); + } + + return marks; + } } From 7885b3fae21e34e79718b2769094294edd7d7bc4 Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Wed, 2 Aug 2017 22:03:10 +1200 Subject: [PATCH 2/4] Loading course mark order from RaceXML - Mark order is read from the generated RaceXML and stored - Added .getNextMark() to get the next mark in the race - Added .equals() and .hashCode() for Marks Tags: #story[1124] (Task 1) --- .../java/seng302/gameServer/GameState.java | 10 +- .../java/seng302/models/map/CanvasMap.java | 2 - src/main/java/seng302/models/mark/Mark.java | 38 ++++++ .../java/seng302/models/mark/MarkOrder.java | 116 ++++++++++++++++++ .../java/seng302/server/messages/Message.java | 2 - .../java/seng302/models/MarkOrderTest.java | 95 ++++++++++++++ 6 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 src/main/java/seng302/models/mark/MarkOrder.java create mode 100644 src/test/java/seng302/models/MarkOrderTest.java diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 16eb8050..918cd210 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -1,13 +1,14 @@ package seng302.gameServer; +import seng302.client.ClientPacketParser; +import seng302.models.Player; +import seng302.models.Yacht; +import seng302.server.messages.BoatActionType; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import seng302.client.ClientPacketParser; -import seng302.models.Player; -import seng302.models.Yacht; -import seng302.server.messages.BoatActionType; /** * A Static class to hold information about the current state of the game (model) @@ -43,7 +44,6 @@ public class GameState implements Runnable { yachts = new HashMap<>(); new Thread(this).start(); - } public static String getHostIpAddress() { diff --git a/src/main/java/seng302/models/map/CanvasMap.java b/src/main/java/seng302/models/map/CanvasMap.java index e6a00cfa..de6403c7 100644 --- a/src/main/java/seng302/models/map/CanvasMap.java +++ b/src/main/java/seng302/models/map/CanvasMap.java @@ -7,8 +7,6 @@ import seng302.utilities.GeoPoint; import javax.net.ssl.HttpsURLConnection; import java.net.URL; -import java.lang.Math; - /** * CanvasMap retrieves a map image with given geo boundary from Google Map server. * By passing a rectangle like geo boundary, it returns a map image with the diff --git a/src/main/java/seng302/models/mark/Mark.java b/src/main/java/seng302/models/mark/Mark.java index 027bf6d3..362c77fd 100644 --- a/src/main/java/seng302/models/mark/Mark.java +++ b/src/main/java/seng302/models/mark/Mark.java @@ -145,4 +145,42 @@ public abstract class Mark { public int getCompoundMarkID() { return compoundMarkID; } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (!(other instanceof Mark)){ + return false; + } + + Mark otherMark = (Mark) other; + + if (otherMark.getLatitude() != getLatitude() || otherMark.getLongitude() != getLongitude()) { + return false; + } + + if (otherMark.getCompoundMarkID() != getCompoundMarkID()){ + return false; + } + + if (otherMark.getId() != getId()){ + return false; + } + + if (!otherMark.getName().equals(name)){ + return false; + } + + return true; + } + + @Override + public int hashCode() { + return getName().hashCode() + getMarkType().hashCode() + + Integer.hashCode(getCompoundMarkID()) + Double.hashCode(getLatitude()) + + Double.hashCode(getLongitude()); + } } diff --git a/src/main/java/seng302/models/mark/MarkOrder.java b/src/main/java/seng302/models/mark/MarkOrder.java new file mode 100644 index 00000000..bf6f5f40 --- /dev/null +++ b/src/main/java/seng302/models/mark/MarkOrder.java @@ -0,0 +1,116 @@ +package seng302.models.mark; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import seng302.models.stream.XMLParser; +import seng302.models.xml.Race; +import seng302.models.xml.XMLGenerator; +import seng302.server.messages.XMLMessageSubType; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; +import java.util.Collections; +import java.util.List; + +/** + * Class to hold the order of the marks in the race. + */ +public class MarkOrder { + private List raceMarkOrder; + private Logger logger = LoggerFactory.getLogger(MarkOrder.class); + + public MarkOrder(){ + loadRaceProperties(); + } + + /** + * @return An ordered list of marks in the race + * OR null if the mark order could not be loaded + */ + public List getMarkOrder(){ + if (raceMarkOrder == null){ + logger.warn("Race order accessed but not instantiated"); + return null; + } + + return Collections.unmodifiableList(raceMarkOrder); + } + + /** + * Returns the mark in the race after the previous mark + * @param previous The previous mark + * @return the next mark + * OR null if there is no next mark + */ + public Mark getNextMark(Mark previous){ + for (int i = 0; i < raceMarkOrder.size(); i++){ + Mark mark = raceMarkOrder.get(i); + + if (i + 1 >= raceMarkOrder.size()){ + return null; + } + + if (mark.equals(previous)){ + return raceMarkOrder.get(i+1); + } + } + + return null; + } + + /** + * Loads the race order from an XML string + * @param xml An AC35 RaceXML + * @return An ordered list of marks in the race + */ + private List loadRaceOrderFromXML(String xml){ + XMLParser xmlParser = new XMLParser(); + XMLParser.RaceXMLObject raceXMLObject; + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + Document doc; + + try { + db = dbf.newDocumentBuilder(); + doc = db.parse(new InputSource(new StringReader(xml))); + } catch (ParserConfigurationException | IOException | SAXException e) { + logger.error("Failed to read generated race XML"); + return null; + } + + xmlParser.constructXML(doc , XMLMessageSubType.RACE.getType()); + raceXMLObject = xmlParser.getRaceXML(); + + if (raceXMLObject != null){ + logger.debug("Loaded RaceXML for mark order"); + return raceXMLObject.getNonDupCompoundMarks(); + } + + return null; + } + + /** + * Load the raceXML and mark order + */ + private void loadRaceProperties(){ + XMLGenerator generator = new XMLGenerator(); + + generator.setRace(new Race()); + + String raceXML = generator.getRaceAsXml(); + + if (raceXML == null){ + logger.error("Failed to generate raceXML (for race properties)"); + return; + } + + raceMarkOrder = loadRaceOrderFromXML(raceXML); + } +} diff --git a/src/main/java/seng302/server/messages/Message.java b/src/main/java/seng302/server/messages/Message.java index 398628ab..10afb8e5 100644 --- a/src/main/java/seng302/server/messages/Message.java +++ b/src/main/java/seng302/server/messages/Message.java @@ -1,7 +1,5 @@ package seng302.server.messages; -import java.io.IOException; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java new file mode 100644 index 00000000..97db018c --- /dev/null +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -0,0 +1,95 @@ +package seng302.models; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import seng302.models.mark.Mark; +import seng302.models.mark.MarkOrder; +import seng302.models.mark.SingleMark; + +import static junit.framework.TestCase.*; +import static org.junit.Assert.assertNotEquals; + +public class MarkOrderTest { + private static MarkOrder markOrder; + + @BeforeClass + public static void setup(){ + markOrder = new MarkOrder(); + } + + /** + * Test to ensure marks are loaded from XML + */ + @Test + public void testMarkOrderLoadedFromXML(){ + assertTrue(markOrder != null); + } + + /** + * Test if .equals() method on returns true on two marks that are equal + */ + @Test + public void testMarkEqualsTrue(){ + Mark mark1 = new SingleMark("asd", 1.1, 2.2, 1, 2); + Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); + + assertEquals(mark1, mark2); + } + + /** + * Test if .equals() method on returns false on two marks that are NOT equal + */ + @Test + public void testMarkNotEquals(){ + Mark mark1 = new SingleMark("asf", 1.1, 2.2, 2, 2); + Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); + + assertNotEquals(mark1, mark2); + } + + /** + * Test if .getNextMark() returns null if it is called with the final mark in the race + */ + @Test + public void testNextMarkAtEnd(){ + // There are no marks in the XML, therefore this can't be tested + if (markOrder.getMarkOrder().size() == 0){ + return; + } + + Mark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); + + assertEquals(null, markOrder.getNextMark(lastMark)); + } + + /** + * Test if .getNextMark() method on returns null if the mark does not exist in the race + */ + @Test + public void testNextMarkNotExists(){ + Mark someMark = new SingleMark("0-0-0-0-0-0-0", 0.0, 0.1, 2, 1); + + assertEquals(null, markOrder.getNextMark(someMark)); + } + + /** + * Test if .getNextMark() method returns the next mark in the race + */ + @Test + public void testNextMark(){ + // There are not enough marks for this to be tested + if (markOrder.getMarkOrder().size() < 2){ + return; + } + + Mark firstMark = markOrder.getMarkOrder().get(0); + + assertEquals(markOrder.getMarkOrder().get(1), markOrder.getNextMark(firstMark)); + } + + @AfterClass + public static void tearDown(){ + markOrder = null; + } +} From f9e6df46c145afddbc0a222586613f5ae768be16 Mon Sep 17 00:00:00 2001 From: Calum Date: Thu, 3 Aug 2017 13:23:43 +1200 Subject: [PATCH 3/4] Fixed issues caused by merge. #bug --- src/main/java/seng302/App.java | 6 +- .../java/seng302/gameServer/GameState.java | 20 +-- .../server/messages/BoatLocationMessage.java | 39 +++-- .../server/simulator/Simulator.java | 159 ------------------ .../java/seng302/model/mark/CompoundMark.java | 42 +++++ .../{models => model}/mark/MarkOrder.java | 52 +++--- .../seng302/visualiser/map/CanvasMap.java | 3 +- src/main/resources/server_config/race.xml | 2 +- .../java/seng302/models/MarkOrderTest.java | 59 ++++--- 9 files changed, 130 insertions(+), 252 deletions(-) delete mode 100644 src/main/java/seng302/gameServer/server/simulator/Simulator.java rename src/main/java/seng302/{models => model}/mark/MarkOrder.java (76%) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index ab3ec79a..3b914350 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -7,7 +7,11 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import seng302.model.PolarTable; diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index 8ab071c3..b49283c3 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -4,15 +4,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import seng302.client.ClientPacketParser; -import seng302.models.Player; -import seng302.models.Yacht; -import seng302.server.messages.BoatActionType; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import seng302.gameServer.server.messages.BoatActionType; +import seng302.model.Player; +import seng302.model.Yacht; /** * A Static class to hold information about the current state of the game (model) @@ -33,8 +27,6 @@ public class GameState implements Runnable { private static GameStages currentStage; private static long startTime; - // TODO: 26/07/17 cir27 - Super hackish fix until something more permanent can be made. - private static ObservableList observablePlayers = FXCollections.observableArrayList(); private static Map playerStringMap = new HashMap<>(); /* Ideally I would like to make this class an object instantiated by the server and given to @@ -73,20 +65,14 @@ public class GameState implements Runnable { return players; } - public static ObservableList getObservablePlayers () { - return observablePlayers; - } - public static void addPlayer(Player player) { players.add(player); String playerText = player.getYacht().getSourceId() + " " + player.getYacht().getBoatName() + " " + player.getYacht().getCountry(); - Platform.runLater(() -> observablePlayers.add(playerText)); //Had to add this to handle javaFX window using array playerStringMap.put(player, playerText); } public static void removePlayer(Player player) { players.remove(player); - observablePlayers.remove(playerStringMap.get(player)); playerStringMap.remove(player); } diff --git a/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java b/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java index 6e85f4e1..72548c12 100644 --- a/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java +++ b/src/main/java/seng302/gameServer/server/messages/BoatLocationMessage.java @@ -1,6 +1,7 @@ package seng302.gameServer.server.messages; public class BoatLocationMessage extends Message { + private final int MESSAGE_SIZE = 56; private long messageVersionNumber; @@ -28,6 +29,7 @@ public class BoatLocationMessage extends Message { /** * Describes the location, altitude and sensor data from the boat. + * * @param sourceId ID of the boat * @param sequenceNum Sequence number of the message * @param latitude The boats latitude @@ -35,7 +37,8 @@ public class BoatLocationMessage extends Message { * @param heading The boats heading * @param boatSpeed The boats speed */ - public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, double heading, long boatSpeed){ + public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, + double heading, long boatSpeed) { messageVersionNumber = 1; time = System.currentTimeMillis(); this.sourceId = sourceId; @@ -49,7 +52,7 @@ public class BoatLocationMessage extends Message { this.roll = 0; this.boatSpeed = boatSpeed; this.COG = 2; - this.SOG = boatSpeed ; + this.SOG = boatSpeed; this.apparentWindSpeed = 0; this.apparentWindAngle = 0; this.trueWindSpeed = 0; @@ -63,7 +66,7 @@ public class BoatLocationMessage extends Message { allocateBuffer(); writeHeaderToBuffer(); - long headingToSend = (long)((heading/360.0) * 65535.0); + long headingToSend = (long) ((heading / 360.0) * 65535.0); putByte((byte) messageVersionNumber); putInt(time, 6); @@ -94,56 +97,62 @@ public class BoatLocationMessage extends Message { /** * Convert binary latitude or longitude to floating point number + * * @param binaryPackedLatLon Binary packed lat OR lon * @return Floating point lat/lon */ - public static double binaryPackedToLatLon(long binaryPackedLatLon){ - return (double)binaryPackedLatLon * 180.0 / 2147483648.0; + public static double binaryPackedToLatLon(long binaryPackedLatLon) { + return (double) binaryPackedLatLon * 180.0 / 2147483648.0; } /** * Convert binary packed heading to floating point number + * * @param binaryPackedHeading Binary packed heading * @return heading as a decimal */ - public static double binaryPackedHeadingToDouble(long binaryPackedHeading){ - return (double)binaryPackedHeading * 360.0 / 65536.0; + public static double binaryPackedHeadingToDouble(long binaryPackedHeading) { + return (double) binaryPackedHeading * 360.0 / 65536.0; } /** * Convert binary packed wind angle to floating point number + * * @param binaryPackedWindAngle Binary packed wind angle * @return wind angle as a decimal */ - public static double binaryPackedWindAngleToDouble(long binaryPackedWindAngle){ - return (double)binaryPackedWindAngle*180.0/32768.0; + public static double binaryPackedWindAngleToDouble(long binaryPackedWindAngle) { + return (double) binaryPackedWindAngle * 180.0 / 32768.0; } /** * Convert a latitude or longitude to a binary packed long + * * @param latLon A floating point latitude/longitude * @return A binary packed lat/lon */ - public static long latLonToBinaryPackedLong(double latLon){ - return (long)((536870912 * latLon) / 45); + public static long latLonToBinaryPackedLong(double latLon) { + return (long) ((536870912 * latLon) / 45); } /** * Convert a heading to a binary packed long + * * @param heading A floating point heading * @return A binary packed heading */ - public static long headingToBinaryPackedLong(double heading){ - return (long)((8192*heading)/45); + public static long headingToBinaryPackedLong(double heading) { + return (long) ((8192 * heading) / 45); } /** * Convert a wind angle to a binary packed long + * * @param windAngle Floating point wind angle * @return A binary packed wind angle */ - public static long windAngleToBinaryPackedLong(double windAngle){ - return (long)((8192*windAngle)/45); + public static long windAngleToBinaryPackedLong(double windAngle) { + return (long) ((8192 * windAngle) / 45); } @Override diff --git a/src/main/java/seng302/gameServer/server/simulator/Simulator.java b/src/main/java/seng302/gameServer/server/simulator/Simulator.java deleted file mode 100644 index 9f638beb..00000000 --- a/src/main/java/seng302/gameServer/server/simulator/Simulator.java +++ /dev/null @@ -1,159 +0,0 @@ -package seng302.server.simulator; - -import seng302.server.simulator.mark.Corner; -import seng302.server.simulator.mark.Mark; -import seng302.server.simulator.parsers.RaceParser; -import seng302.utilities.GeoPoint; -import seng302.utilities.GeoUtility; - -import java.util.HashSet; -import java.util.List; -import java.util.Observable; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; -import seng302.model.mark.Mark; -import seng302.gameServer.server.simulator.parsers.RaceParser; -import seng302.model.GeoPoint; -import seng302.utilities.GeoUtility; - -public class Simulator extends Observable implements Runnable { - - private List course; - private List boats; - private long lapse; - private boolean isRaceStarted; - - /** - * Creates a simulator instance with given time lapse. - * @param lapse time duration in millisecond. - */ - public Simulator(long lapse) { - RaceParser rp = new RaceParser("/server_config/race.xml"); - course = rp.getCourse(); - boats = rp.getBoats(); - this.lapse = lapse; - isRaceStarted = false; - - setLegs(); - - // set start line's coordinate to boats - Double startLat = course.get(0).getCompoundMark().getSubMark(1).getLat(); - Double startLng = course.get(0).getCompoundMark().getSubMark(1).getLng(); - for (Boat boat : boats) { - boat.setLat(startLat); - boat.setLng(startLng); - boat.setLastPassedCorner(course.get(0)); - boat.setHeadingCorner(course.get(1)); - boat.setSpeed(ThreadLocalRandom.current().nextInt(40000, 60000 + 1)); - } - } - - @Override - public void run() { - - int numOfFinishedBoats = 0; - - while (numOfFinishedBoats < boats.size()) { - - // if race has started, then boat should start to move. - if (isRaceStarted) { - for (Boat boat : boats) { - numOfFinishedBoats += moveBoat(boat, lapse); - } - } - - setChanged(); - notifyObservers(boats); - - try { - Thread.sleep(lapse); - } catch (InterruptedException e) { - System.out.println("[Simulator] interrupted exception "); - } - } - } - - /** - * Moves a boat with given time duration. - * @param boat the boat to be moved - * @param duration the moving duration in milliseconds - * @return 1 if the boat has reached the final line, otherwise return 0 - */ - private int moveBoat(Boat boat, double duration) { - if (boat.getHeadingCorner() != null) { - - boat.move(boat.getLastPassedCorner().getBearingToNextCorner(), duration); - - GeoPoint boatPos = new GeoPoint(boat.getLat(), boat.getLng()); - GeoPoint lastMarkPos = boat.getLastPassedCorner().getCompoundMark().getSubMark(1); - - double distanceFromLastMark = GeoUtility.getDistance(boatPos, lastMarkPos); - // if a boat passes its heading mark - while (distanceFromLastMark >= boat.getLastPassedCorner().getDistanceToNextCorner()) { - double compensateDistance = distanceFromLastMark - boat.getLastPassedCorner().getDistanceToNextCorner(); - boat.setLastPassedCorner(boat.getHeadingCorner()); - boat.setHeadingCorner(boat.getLastPassedCorner().getNextCorner()); - - // heading corner == null means boat has reached the final mark - if (boat.getHeadingCorner() == null) { - boat.setFinished(true); - return 1; - } - - // move compensate distance for the mark just passed - GeoPoint pos = GeoUtility.getGeoCoordinate( - boat.getLastPassedCorner().getCompoundMark().getSubMark(1), - boat.getLastPassedCorner().getBearingToNextCorner(), - compensateDistance); - boat.setLat(pos.getLat()); - boat.setLng(pos.getLng()); - distanceFromLastMark = GeoUtility.getDistance(new GeoPoint(boat.getLat(), boat.getLng()), - boat.getLastPassedCorner().getCompoundMark().getSubMark(1)); - } - } - return 0; - } - - /** - * Link all the corners in the course list so that every corner knows its next - * corner, as well as the distance and bearing to its next corner. However, - * the last corner's heading is null, which means it is the final line. - */ - private void setLegs() { - // get the bearing from one mark to the next heading mark - for (int i = 0; i < course.size() - 1; i++) { - - Mark mark1 = course.get(i).getCompoundMark().getSubMark(1); - Mark mark2 = course.get(i + 1).getCompoundMark().getSubMark(1); - course.get(i).setDistanceToNextCorner(GeoUtility.getDistance(mark1, mark2)); - - course.get(i).setNextCorner(course.get(i + 1)); - - course.get(i).setBearingToNextCorner( - GeoUtility.getBearing(course.get(i).getCompoundMark().getSubMark(1), - course.get(i + 1).getCompoundMark().getSubMark(1))); - } - } - - public List getBoats(){ - return boats; - } - - public void setRaceStarted(boolean raceStarted) { - isRaceStarted = raceStarted; - } - - /** - * @return A list of marks in the race - */ - public Set getMarks(){ - Set marks = new HashSet<>(); - - for (Corner c : course){ - marks.add(c.getCompoundMark()); - marks.add(c.getCompoundMark()); - } - - return marks; - } -} diff --git a/src/main/java/seng302/model/mark/CompoundMark.java b/src/main/java/seng302/model/mark/CompoundMark.java index b1989845..af4cd8a8 100644 --- a/src/main/java/seng302/model/mark/CompoundMark.java +++ b/src/main/java/seng302/model/mark/CompoundMark.java @@ -86,4 +86,46 @@ public class CompoundMark { public List getMarks () { return marks; } + + +// @Override +// public boolean equals(Object other) { +// if (other == null) { +// return false; +// } +// +// if (!(other instanceof Mark)){ +// return false; +// } +// +// Mark otherMark = (Mark) other; +// +// if (otherMark.getLat() != getLat() || otherMark.getLongitude() != getLongitude()) { +// return false; +// } +// +// if (otherMark.getCompoundMarkID() != getCompoundMarkID()){ +// return false; +// } +// +// if (otherMark.getId() != getId()){ +// return false; +// } +// +// if (!otherMark.getName().equals(name)){ +// return false; +// } +// +// return true; +// } + + @Override + public int hashCode() { + int hash = 0; + for (Mark mark : marks) { + hash += Double.hashCode(mark.getSourceID()) + Double.hashCode(mark.getLat()) + + Double.hashCode(mark.getLng()) + mark.getName().hashCode(); + } + return hash + getName().hashCode() + Integer.hashCode(getId()); + } } diff --git a/src/main/java/seng302/models/mark/MarkOrder.java b/src/main/java/seng302/model/mark/MarkOrder.java similarity index 76% rename from src/main/java/seng302/models/mark/MarkOrder.java rename to src/main/java/seng302/model/mark/MarkOrder.java index bf6f5f40..d3270c8f 100644 --- a/src/main/java/seng302/models/mark/MarkOrder.java +++ b/src/main/java/seng302/model/mark/MarkOrder.java @@ -1,28 +1,29 @@ -package seng302.models.mark; +package seng302.model.mark; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import seng302.models.stream.XMLParser; -import seng302.models.xml.Race; -import seng302.models.xml.XMLGenerator; -import seng302.server.messages.XMLMessageSubType; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; -import java.util.Collections; -import java.util.List; +import seng302.model.stream.xml.generator.Race; +import seng302.model.stream.xml.parser.RaceXMLData; +import seng302.utilities.XMLGenerator; +import seng302.utilities.XMLParser; /** * Class to hold the order of the marks in the race. */ public class MarkOrder { - private List raceMarkOrder; + private List raceMarkOrder; private Logger logger = LoggerFactory.getLogger(MarkOrder.class); public MarkOrder(){ @@ -33,7 +34,7 @@ public class MarkOrder { * @return An ordered list of marks in the race * OR null if the mark order could not be loaded */ - public List getMarkOrder(){ + public List getMarkOrder(){ if (raceMarkOrder == null){ logger.warn("Race order accessed but not instantiated"); return null; @@ -48,9 +49,9 @@ public class MarkOrder { * @return the next mark * OR null if there is no next mark */ - public Mark getNextMark(Mark previous){ + public CompoundMark getNextMark(CompoundMark previous){ for (int i = 0; i < raceMarkOrder.size(); i++){ - Mark mark = raceMarkOrder.get(i); + CompoundMark mark = raceMarkOrder.get(i); if (i + 1 >= raceMarkOrder.size()){ return null; @@ -60,7 +61,6 @@ public class MarkOrder { return raceMarkOrder.get(i+1); } } - return null; } @@ -69,9 +69,7 @@ public class MarkOrder { * @param xml An AC35 RaceXML * @return An ordered list of marks in the race */ - private List loadRaceOrderFromXML(String xml){ - XMLParser xmlParser = new XMLParser(); - XMLParser.RaceXMLObject raceXMLObject; + private List loadRaceOrderFromXML(String xml){ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; @@ -84,13 +82,14 @@ public class MarkOrder { logger.error("Failed to read generated race XML"); return null; } + + RaceXMLData data = XMLParser.parseRace(doc); - xmlParser.constructXML(doc , XMLMessageSubType.RACE.getType()); - raceXMLObject = xmlParser.getRaceXML(); - - if (raceXMLObject != null){ + if (data != null){ logger.debug("Loaded RaceXML for mark order"); - return raceXMLObject.getNonDupCompoundMarks(); + List course = new ArrayList<>(data.getCompoundMarks().values()); + course.sort(Comparator.comparingInt(CompoundMark::getId)); + return course; } return null; @@ -110,7 +109,6 @@ public class MarkOrder { logger.error("Failed to generate raceXML (for race properties)"); return; } - raceMarkOrder = loadRaceOrderFromXML(raceXML); } } diff --git a/src/main/java/seng302/visualiser/map/CanvasMap.java b/src/main/java/seng302/visualiser/map/CanvasMap.java index 140a0b50..e79805e4 100644 --- a/src/main/java/seng302/visualiser/map/CanvasMap.java +++ b/src/main/java/seng302/visualiser/map/CanvasMap.java @@ -4,8 +4,7 @@ import java.net.URL; import javafx.geometry.Point2D; import javafx.scene.image.Image; import javax.net.ssl.HttpsURLConnection; -import java.net.URL; -import java.lang.Math; +import seng302.model.GeoPoint; /** * CanvasMap retrieves a map image with given geo boundary from Google Map server. diff --git a/src/main/resources/server_config/race.xml b/src/main/resources/server_config/race.xml index 5798aeb7..b2379fca 100644 --- a/src/main/resources/server_config/race.xml +++ b/src/main/resources/server_config/race.xml @@ -28,7 +28,7 @@ - + diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java index 97db018c..ad9b700e 100644 --- a/src/test/java/seng302/models/MarkOrderTest.java +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -1,14 +1,13 @@ package seng302.models; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; + import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import seng302.models.mark.Mark; -import seng302.models.mark.MarkOrder; -import seng302.models.mark.SingleMark; - -import static junit.framework.TestCase.*; -import static org.junit.Assert.assertNotEquals; +import seng302.model.mark.CompoundMark; +import seng302.model.mark.MarkOrder; public class MarkOrderTest { private static MarkOrder markOrder; @@ -26,27 +25,27 @@ public class MarkOrderTest { assertTrue(markOrder != null); } - /** - * Test if .equals() method on returns true on two marks that are equal - */ - @Test - public void testMarkEqualsTrue(){ - Mark mark1 = new SingleMark("asd", 1.1, 2.2, 1, 2); - Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); - - assertEquals(mark1, mark2); - } - - /** - * Test if .equals() method on returns false on two marks that are NOT equal - */ - @Test - public void testMarkNotEquals(){ - Mark mark1 = new SingleMark("asf", 1.1, 2.2, 2, 2); - Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); - - assertNotEquals(mark1, mark2); - } +// /** +// * Test if .equals() method on returns true on two marks that are equal +// */ +// @Test +// public void testMarkEqualsTrue(){ +// M mark1 = new SingleMark("asd", 1.1, 2.2, 1, 2); +// Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); +// +// assertEquals(mark1, mark2); +// } +// +// /** +// * Test if .equals() method on returns false on two marks that are NOT equal +// */ +// @Test +// public void testMarkNotEquals(){ +// Mark mark1 = new SingleMark("asf", 1.1, 2.2, 2, 2); +// Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); +// +// assertNotEquals(mark1, mark2); +// } /** * Test if .getNextMark() returns null if it is called with the final mark in the race @@ -58,7 +57,7 @@ public class MarkOrderTest { return; } - Mark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); + CompoundMark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); assertEquals(null, markOrder.getNextMark(lastMark)); } @@ -68,7 +67,7 @@ public class MarkOrderTest { */ @Test public void testNextMarkNotExists(){ - Mark someMark = new SingleMark("0-0-0-0-0-0-0", 0.0, 0.1, 2, 1); + CompoundMark someMark = new CompoundMark(1, "something"); assertEquals(null, markOrder.getNextMark(someMark)); } @@ -83,7 +82,7 @@ public class MarkOrderTest { return; } - Mark firstMark = markOrder.getMarkOrder().get(0); + CompoundMark firstMark = markOrder.getMarkOrder().get(0); assertEquals(markOrder.getMarkOrder().get(1), markOrder.getNextMark(firstMark)); } From 281ce2d842442a89b081e0b5fa4d6d43e067a732 Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Fri, 4 Aug 2017 13:20:50 +1200 Subject: [PATCH 4/4] Loading course mark order from RaceXML - Re-engineered code to work with the new marks - Fixed bug where race order wasn't correct (added RacePosition class to fix) - Rewrote tests to work with new RacePosition class Tags: #story[1124] (Task 1) --- .../java/seng302/model/mark/MarkOrder.java | 75 ++++++++++++------- .../java/seng302/model/mark/RacePosition.java | 55 ++++++++++++++ .../java/seng302/models/MarkOrderTest.java | 67 +++++++---------- 3 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 src/main/java/seng302/model/mark/RacePosition.java diff --git a/src/main/java/seng302/model/mark/MarkOrder.java b/src/main/java/seng302/model/mark/MarkOrder.java index d3270c8f..ca2b4356 100644 --- a/src/main/java/seng302/model/mark/MarkOrder.java +++ b/src/main/java/seng302/model/mark/MarkOrder.java @@ -1,14 +1,5 @@ package seng302.model.mark; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -19,11 +10,21 @@ import seng302.model.stream.xml.parser.RaceXMLData; import seng302.utilities.XMLGenerator; import seng302.utilities.XMLParser; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + /** * Class to hold the order of the marks in the race. */ public class MarkOrder { - private List raceMarkOrder; + private List raceMarkOrder; private Logger logger = LoggerFactory.getLogger(MarkOrder.class); public MarkOrder(){ @@ -34,7 +35,7 @@ public class MarkOrder { * @return An ordered list of marks in the race * OR null if the mark order could not be loaded */ - public List getMarkOrder(){ + public List getMarkOrder(){ if (raceMarkOrder == null){ logger.warn("Race order accessed but not instantiated"); return null; @@ -45,23 +46,25 @@ public class MarkOrder { /** * Returns the mark in the race after the previous mark - * @param previous The previous mark - * @return the next mark - * OR null if there is no next mark + * @param position The current race position + * @return the next race position + * OR null if there is no position */ - public CompoundMark getNextMark(CompoundMark previous){ - for (int i = 0; i < raceMarkOrder.size(); i++){ - CompoundMark mark = raceMarkOrder.get(i); + public RacePosition getNextPosition(RacePosition position){ + Mark previousMark = position.getNextMark(); + Mark nextMark; - if (i + 1 >= raceMarkOrder.size()){ - return null; - } + if (position.getPositionIndex() + 1 >= raceMarkOrder.size() - 1){ + RacePosition nextRacePosition = new RacePosition(raceMarkOrder.size() - 1, null, previousMark); + nextRacePosition.setFinishingLeg(); - if (mark.equals(previous)){ - return raceMarkOrder.get(i+1); - } + return nextRacePosition; } - return null; + + Integer nextPositionIndex = position.getPositionIndex() + 1; + RacePosition nextRacePosition = new RacePosition(nextPositionIndex, raceMarkOrder.get(nextPositionIndex), previousMark); + + return nextRacePosition; } /** @@ -69,7 +72,7 @@ public class MarkOrder { * @param xml An AC35 RaceXML * @return An ordered list of marks in the race */ - private List loadRaceOrderFromXML(String xml){ + private List loadRaceOrderFromXML(String xml){ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; @@ -87,14 +90,32 @@ public class MarkOrder { if (data != null){ logger.debug("Loaded RaceXML for mark order"); - List course = new ArrayList<>(data.getCompoundMarks().values()); - course.sort(Comparator.comparingInt(CompoundMark::getId)); + List corners = data.getMarkSequence(); + Map marks = data.getCompoundMarks(); + List course = new ArrayList<>(); + + for (Corner corner : corners){ + CompoundMark compoundMark = marks.get(corner.getCompoundMarkID()); + course.add(compoundMark.getMarks().get(0)); + } + return course; } return null; } + /** + * @return The first position in the race + */ + public RacePosition getFirstPosition(){ + if (raceMarkOrder.size() > 0){ + return new RacePosition(-1, raceMarkOrder.get(0), null); + } + + return null; + } + /** * Load the raceXML and mark order */ diff --git a/src/main/java/seng302/model/mark/RacePosition.java b/src/main/java/seng302/model/mark/RacePosition.java new file mode 100644 index 00000000..fc160b10 --- /dev/null +++ b/src/main/java/seng302/model/mark/RacePosition.java @@ -0,0 +1,55 @@ +package seng302.model.mark; + +/** + * Represents a boats position between two marks + */ +public class RacePosition { + private Integer positionIndex; + private Mark nextMark; + private Mark previousMark; + private Boolean isFinishingLeg; + + public RacePosition(Integer positionIndex, Mark nextMark, Mark previousMark){ + this.positionIndex = positionIndex; + this.nextMark = nextMark; + this.previousMark = previousMark; + isFinishingLeg = false; + } + + /** + * @return The position of the boat (0...number of marks in race - 1) + */ + public Integer getPositionIndex(){ + return positionIndex; + } + + /** + * @return The mark the boat is heading to + * will return NULL if this is the finishing legg + */ + public Mark getNextMark(){ + return nextMark; + } + + /** + * @return The mark the boat is heading away from, + * Will return NULL if this is the starting leg + */ + public Mark getPreviousMark(){ + return previousMark; + } + + /** + * Sets a flag that this is the last leg in the race + */ + public void setFinishingLeg(){ + isFinishingLeg = true; + } + + /** + * @return true if this is the last leg in the race + */ + public boolean getIsFinishingLeg() { + return isFinishingLeg; + } +} diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java index ad9b700e..8db06ec8 100644 --- a/src/test/java/seng302/models/MarkOrderTest.java +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -1,13 +1,14 @@ package seng302.models; -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertTrue; - import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import seng302.model.mark.CompoundMark; +import seng302.model.mark.Mark; import seng302.model.mark.MarkOrder; +import seng302.model.mark.RacePosition; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; public class MarkOrderTest { private static MarkOrder markOrder; @@ -25,28 +26,6 @@ public class MarkOrderTest { assertTrue(markOrder != null); } -// /** -// * Test if .equals() method on returns true on two marks that are equal -// */ -// @Test -// public void testMarkEqualsTrue(){ -// M mark1 = new SingleMark("asd", 1.1, 2.2, 1, 2); -// Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); -// -// assertEquals(mark1, mark2); -// } -// -// /** -// * Test if .equals() method on returns false on two marks that are NOT equal -// */ -// @Test -// public void testMarkNotEquals(){ -// Mark mark1 = new SingleMark("asf", 1.1, 2.2, 2, 2); -// Mark mark2 = new SingleMark("asd", 1.1, 2.2, 1, 2); -// -// assertNotEquals(mark1, mark2); -// } - /** * Test if .getNextMark() returns null if it is called with the final mark in the race */ @@ -57,19 +36,12 @@ public class MarkOrderTest { return; } - CompoundMark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); + Mark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); + Integer lastIndex = markOrder.getMarkOrder().size() - 1; - assertEquals(null, markOrder.getNextMark(lastMark)); - } + RacePosition lastRacePosition = new RacePosition(lastIndex, lastMark, null); - /** - * Test if .getNextMark() method on returns null if the mark does not exist in the race - */ - @Test - public void testNextMarkNotExists(){ - CompoundMark someMark = new CompoundMark(1, "something"); - - assertEquals(null, markOrder.getNextMark(someMark)); + assertEquals(null, markOrder.getNextPosition(lastRacePosition).getNextMark()); } /** @@ -82,9 +54,26 @@ public class MarkOrderTest { return; } - CompoundMark firstMark = markOrder.getMarkOrder().get(0); + RacePosition firstRacePos = new RacePosition(0, markOrder.getMarkOrder().get(0), null); - assertEquals(markOrder.getMarkOrder().get(1), markOrder.getNextMark(firstMark)); + assertEquals(markOrder.getMarkOrder().get(1).getName(), markOrder.getNextPosition(firstRacePos).getNextMark().getName()); + } + + /** + * Test if a whole race can be completed + */ + @Test + public void testMarkSequence(){ + RacePosition current = markOrder.getFirstPosition(); + + while (!current.getIsFinishingLeg()){ + + current = markOrder.getNextPosition(current); + + if (current.getIsFinishingLeg()){ + assertEquals(null, current.getNextMark()); + } + } } @AfterClass