Merge branch '1124_Mark_Sequence_From_RaceXML' into 'develop'

Loading mark sequence from RaceXML

# Loading mark sequence from RaceXML

## Change Log
1. Added MarkOrder class
* 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
* NEW: Added RacePosition class to hold players position in the race
* NEW: Fixed issue where the duplicates weren't stored in the mark order

## Testing
* Unit tests in models/MarkOrderTest.java

## Acceptance Criteria
* Use the mark sequence in the raceXML
    * Met by change log item (1)

* Store relevant mark details with each participating boat (Last mark, next mark)
    * Method in MarkOrder to get next mark, however the last mark and next mark will need to be stored by whoever implements the second task.

See merge request !52
This commit is contained in:
William Muir
2017-08-05 15:18:26 +12:00
7 changed files with 340 additions and 165 deletions
@@ -4,12 +4,9 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import seng302.gameServer.server.messages.BoatActionType;
import seng302.model.Player;
import seng302.model.Yacht;
import seng302.gameServer.server.messages.BoatActionType;
/**
* A Static class to hold information about the current state of the game (model)
@@ -30,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<String> observablePlayers = FXCollections.observableArrayList();
private static Map<Player, String> playerStringMap = new HashMap<>();
/*
Ideally I would like to make this class an object instantiated by the server and given to
@@ -60,7 +55,6 @@ public class GameState implements Runnable {
yachts = new HashMap<>();
new Thread(this).start();
}
public static String getHostIpAddress() {
@@ -71,20 +65,14 @@ public class GameState implements Runnable {
return players;
}
public static ObservableList<String> 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);
}
@@ -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
@@ -1,137 +0,0 @@
package seng302.gameServer.server.simulator;
import java.util.List;
import java.util.Observable;
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<Corner> course;
private List<Boat> 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<Boat> getBoats(){
return boats;
}
public void setRaceStarted(boolean raceStarted) {
isRaceStarted = raceStarted;
}
}