Compare commits
158 Commits
sprint_4.1
...
sprint_5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 25d8c8f9c4 | |||
| 1d9dd76356 | |||
| c2c34705d5 | |||
| 96ed5e445e | |||
| 870dc07fd2 | |||
| 34704bd93d | |||
| 201c88a253 | |||
| 6c4da58d9d | |||
| b56fa5cba3 | |||
| 9cedbeb6f6 | |||
| 4c6d107102 | |||
| 7917a2584b | |||
| f99f8a0d7c | |||
| 7db716f51c | |||
| 592e5a088f | |||
| 2d6850950c | |||
| ef6821a0cd | |||
| 6e9535d78f | |||
| 72a45f5984 | |||
| de600fa062 | |||
| 87e3b4f246 | |||
| 7b47d72ef0 | |||
| 7392bdb80d | |||
| af9f1417f1 | |||
| 8fc00fd750 | |||
| 4e5b67abfa | |||
| 84e8ac89fc | |||
| 12d081a1af | |||
| 5e6b402bf5 | |||
| 2bfa6cb038 | |||
| d99055901f | |||
| c77a48f589 | |||
| 0135426dfe | |||
| 37c745c139 | |||
| 7880039801 | |||
| a56dac1e87 | |||
| 4ae422b47b | |||
| c8a96dcce9 | |||
| 4f2dca7ecf | |||
| e569574c01 | |||
| f544734b4d | |||
| 80b439470b | |||
| 539197cef5 | |||
| 1a867be387 | |||
| 3785cd705f | |||
| 5d7a438080 | |||
| 2f12f3e34f | |||
| 52bfa3ad34 | |||
| d1d659b698 | |||
| cdb9337aed | |||
| 8b0af5bb62 | |||
| 83232a935e | |||
| a30a1aa7c7 | |||
| 07cebb6c5b | |||
| 45bf65a3d3 | |||
| 60f5a99b0c | |||
| 526c12127f | |||
| 1daac842f2 | |||
| 3e4a6f0f2e | |||
| c1e937049e | |||
| 8f8d5c7384 | |||
| 027c7a1480 | |||
| df2efa3329 | |||
| 9d754c8819 | |||
| e11ceed28c | |||
| 8b8b6e4afa | |||
| ed2a22b573 | |||
| 41851ee925 | |||
| ffc61942a9 | |||
| 2e4382bff6 | |||
| f542dbb61e | |||
| 2869d139a3 | |||
| 3ec930491f | |||
| a0005064ac | |||
| 33fae9d69a | |||
| 913e5fee7b | |||
| 3992073303 | |||
| 797a99f632 | |||
| e891ed8a64 | |||
| e8c2cf809b | |||
| ec761893c7 | |||
| 5df7efda03 | |||
| 2fff73c075 | |||
| d37cbd263e | |||
| 12c2f31af9 | |||
| 49c0c029c3 | |||
| da7a34fc55 | |||
| 322ff740e2 | |||
| b1575e57df | |||
| 82b219cdba | |||
| e317de7562 | |||
| 1e80d76acd | |||
| 176d65e0b2 | |||
| 0c08f5a03c | |||
| e257602b78 | |||
| 8f00f3a80c | |||
| 63d24c001f | |||
| 67668fe1fc | |||
| dbbb41e12f | |||
| 45053ba507 | |||
| b301ce5d27 | |||
| f02bd3b3f8 | |||
| e83eaa38e1 | |||
| 102b5f3ca1 | |||
| 63958a6717 | |||
| 4b8ac32ca9 | |||
| 00b29a1890 | |||
| c7e5f93bc4 | |||
| e4d87c91a2 | |||
| f84091e54e | |||
| e03e8825b2 | |||
| 355f8543f5 | |||
| 77e7db79cc | |||
| 2809d0d832 | |||
| 5b908ec355 | |||
| c480fca72a | |||
| c19f66a6a4 | |||
| 1e6fd1af09 | |||
| 55db2c9961 | |||
| 6ec8b0c3c5 | |||
| 78557a4536 | |||
| 8090cd7985 | |||
| 5ce34bed92 | |||
| 035841f221 | |||
| ef61a687d6 | |||
| fcb1e5e593 | |||
| 752863a0d3 | |||
| 1a3e330eb4 | |||
| 5f9da6b40a | |||
| fe76ef9cdc | |||
| acbaa838ec | |||
| f4134d83b5 | |||
| 24cc10e1cd | |||
| 20b79b40f2 | |||
| 72e2776b7e | |||
| 49e4c92da6 | |||
| ba761e4951 | |||
| c1aa1d8eae | |||
| 4231c3ccd8 | |||
| 65223ceaaf | |||
| ca22615c08 | |||
| 559a9f38c0 | |||
| b73e4c89db | |||
| f163dfdd11 | |||
| a1eda8d91d | |||
| 5ed02a1fe1 | |||
| 66d4a4b958 | |||
| 0cd2867ac0 | |||
| 4d29354797 | |||
| 3085125f3e | |||
| 5e26ad7c36 | |||
| 765ea06c3b | |||
| 14a7305a2d | |||
| e7060d4b6f | |||
| 641039720e | |||
| c449da2916 | |||
| d22d758757 | |||
| acbde5aad8 |
@@ -36,6 +36,13 @@
|
|||||||
<version>2.7.13</version>
|
<version>2.7.13</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.freemarker</groupId>
|
||||||
|
<artifactId>freemarker</artifactId>
|
||||||
|
<version>2.3.26-incubating</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -4,79 +4,37 @@ import javafx.application.Application;
|
|||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.client.ClientState;
|
||||||
import seng302.models.PolarTable;
|
import seng302.models.PolarTable;
|
||||||
import seng302.models.stream.StreamParser;
|
|
||||||
import seng302.models.stream.StreamReceiver;
|
|
||||||
import seng302.server.ServerThread;
|
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) throws Exception {
|
||||||
PolarTable.parsePolarFile(getClass().getResource("/config/acc_polars.csv").getFile());
|
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||||
|
|
||||||
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
||||||
primaryStage.setTitle("RaceVision");
|
primaryStage.setTitle("RaceVision");
|
||||||
primaryStage.setScene(new Scene(root, 1530, 960));
|
primaryStage.setScene(new Scene(root, 1530, 960));
|
||||||
primaryStage.setMaxWidth(1530);
|
primaryStage.setMaxWidth(1530);
|
||||||
primaryStage.setMaxHeight(960);
|
primaryStage.setMaxHeight(960);
|
||||||
|
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("/PP.png")));
|
||||||
// primaryStage.setMaximized(true);
|
// primaryStage.setMaximized(true);
|
||||||
|
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
primaryStage.setOnCloseRequest(e -> {
|
primaryStage.setOnCloseRequest(e -> {
|
||||||
StreamParser.appClose();
|
ClientPacketParser.appClose();
|
||||||
StreamReceiver.noMoreBytes();
|
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ClientState.primaryStage = primaryStage;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
StreamReceiver sr = null;
|
|
||||||
|
|
||||||
new ServerThread("Racevision Test Server");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 1 && args[0].equals("-standalone")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 3 && args[0].equals("-server")) {
|
|
||||||
|
|
||||||
sr = new StreamReceiver(args[1], Integer.valueOf(args[2]), "RaceStream");
|
|
||||||
|
|
||||||
} else if (args.length == 2 && args[0].equals("-server")) {
|
|
||||||
switch (args[1]) {
|
|
||||||
case "internal":
|
|
||||||
sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
|
||||||
break;
|
|
||||||
case "staffserver":
|
|
||||||
sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
|
||||||
break;
|
|
||||||
case "official":
|
|
||||||
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Change the StreamReceiver in this else block to change the default data source.
|
|
||||||
else{
|
|
||||||
sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
|
||||||
}
|
|
||||||
|
|
||||||
sr.start();
|
|
||||||
StreamParser streamParser = new StreamParser("StreamParser");
|
|
||||||
streamParser.start();
|
|
||||||
|
|
||||||
launch(args);
|
launch(args);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
package seng302;
|
|
||||||
|
|
||||||
import javafx.geometry.Point2D;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class for performing geometric calculations on the canvas
|
|
||||||
* Created by wmu16 on 24/05/17.
|
|
||||||
*/
|
|
||||||
public final class GeometryUtils {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the line function on two points of a line and a test point to test which side of the line that point is
|
|
||||||
* on. If the return value is
|
|
||||||
* return 1, then the point is on one side of the line,
|
|
||||||
* return -1 then the point is on the other side of the line
|
|
||||||
* return 0 then the point is exactly on the line.
|
|
||||||
* @param linePoint1 One point of the line
|
|
||||||
* @param linePoint2 Second point of the line
|
|
||||||
* @param testPoint The point to test with this line
|
|
||||||
* @return A return value indicating which side of the line the point is on
|
|
||||||
*/
|
|
||||||
public static Integer lineFunction(Point2D linePoint1, Point2D linePoint2, Point2D testPoint) {
|
|
||||||
|
|
||||||
Double x = testPoint.getX();
|
|
||||||
Double y = testPoint.getY();
|
|
||||||
Double x1 = linePoint1.getX();
|
|
||||||
Double y1 = linePoint1.getY();
|
|
||||||
Double x2 = linePoint2.getX();
|
|
||||||
Double y2 = linePoint2.getY();
|
|
||||||
|
|
||||||
Double result = (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1); //Line function
|
|
||||||
|
|
||||||
if (result > 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (result < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a point and a vector (angle and vector length) Will create a new point, that vector away from the origin
|
|
||||||
* point
|
|
||||||
* @param originPoint The point with which to use as the base for our vector addition
|
|
||||||
* @param angleInDeg (DEGREES) The angle at which our new point is being created (in degrees!)
|
|
||||||
* @param vectorLength The length out on this angle from the origin point to create the new point
|
|
||||||
* @return a Point2D
|
|
||||||
*/
|
|
||||||
public static Point2D makeArbitraryVectorPoint(Point2D originPoint, Double angleInDeg, Double vectorLength) {
|
|
||||||
|
|
||||||
Double endPointX = originPoint.getX() + vectorLength * Math.cos(Math.toRadians(angleInDeg));
|
|
||||||
Double endPointY = originPoint.getY() + vectorLength * Math.sin(Math.toRadians(angleInDeg));
|
|
||||||
|
|
||||||
return new Point2D(endPointX, endPointY);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package seng302.models.stream;
|
package seng302.client;
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -11,7 +11,6 @@ import java.util.Comparator;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
@@ -23,6 +22,7 @@ import org.xml.sax.InputSource;
|
|||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import seng302.models.Yacht;
|
import seng302.models.Yacht;
|
||||||
import seng302.models.mark.Mark;
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.models.stream.XMLParser;
|
||||||
import seng302.models.stream.packets.BoatPositionPacket;
|
import seng302.models.stream.packets.BoatPositionPacket;
|
||||||
import seng302.models.stream.packets.StreamPacket;
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
|
||||||
@@ -32,15 +32,13 @@ import seng302.models.stream.packets.StreamPacket;
|
|||||||
* that are threadsafe so the visualiser can always access the latest speed and position available
|
* that are threadsafe so the visualiser can always access the latest speed and position available
|
||||||
* Created by kre39 on 23/04/17.
|
* Created by kre39 on 23/04/17.
|
||||||
*/
|
*/
|
||||||
public class StreamParser extends Thread {
|
public class ClientPacketParser {
|
||||||
|
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>();
|
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>();
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>();
|
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>();
|
||||||
private String threadName;
|
|
||||||
private Thread t;
|
|
||||||
private static boolean newRaceXmlReceived = false;
|
private static boolean newRaceXmlReceived = false;
|
||||||
private static boolean raceStarted = false;
|
private static boolean raceStarted = false;
|
||||||
private static XMLParser xmlObject;
|
private static XMLParser xmlObject = new XMLParser();
|
||||||
private static boolean raceFinished = false;
|
private static boolean raceFinished = false;
|
||||||
private static boolean streamStatus = false;
|
private static boolean streamStatus = false;
|
||||||
private static long timeSinceStart = -1;
|
private static long timeSinceStart = -1;
|
||||||
@@ -51,52 +49,15 @@ public class StreamParser extends Thread {
|
|||||||
private static Long currentTimeLong;
|
private static Long currentTimeLong;
|
||||||
private static String currentTimeString;
|
private static String currentTimeString;
|
||||||
private static boolean appRunning;
|
private static boolean appRunning;
|
||||||
|
private static Map<Integer, Yacht> clientStateBoats = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
//CONVERSION CONSTANTS
|
//CONVERSION CONSTANTS
|
||||||
private static final Double MS_TO_KNOTS = 1.94384;
|
public static final Double MS_TO_KNOTS = 1.94384;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to initialise the thread name and stream parser object so a thread can be executed
|
* Used to initialise the thread name and stream parser object so a thread can be executed
|
||||||
*
|
|
||||||
* @param threadName name of the thread
|
|
||||||
*/
|
*/
|
||||||
public StreamParser(String threadName) {
|
public ClientPacketParser() {
|
||||||
this.threadName = threadName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to within threading so when the stream parser thread runs, it will keep looking for a
|
|
||||||
* packet to process until it is unable to find anymore packets
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
appRunning = true;
|
|
||||||
try {
|
|
||||||
streamStatus = true;
|
|
||||||
xmlObject = new XMLParser();
|
|
||||||
while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) {
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
while (appRunning) {
|
|
||||||
StreamPacket packet = StreamReceiver.packetBuffer.take();
|
|
||||||
parsePacket(packet);
|
|
||||||
Thread.sleep(1);
|
|
||||||
while (StreamReceiver.packetBuffer.peek() == null) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to start the stream parser thread when multithreading
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
if (t == null) {
|
|
||||||
t = new Thread(this, threadName);
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +66,7 @@ public class StreamParser extends Thread {
|
|||||||
*
|
*
|
||||||
* @param packet the packet to be looked at and processed
|
* @param packet the packet to be looked at and processed
|
||||||
*/
|
*/
|
||||||
private static void parsePacket(StreamPacket packet) {
|
public static void parsePacket(StreamPacket packet) {
|
||||||
try {
|
try {
|
||||||
switch (packet.getType()) {
|
switch (packet.getType()) {
|
||||||
case HEARTBEAT:
|
case HEARTBEAT:
|
||||||
@@ -118,7 +79,6 @@ public class StreamParser extends Thread {
|
|||||||
extractDisplayMessage(packet);
|
extractDisplayMessage(packet);
|
||||||
break;
|
break;
|
||||||
case XML_MESSAGE:
|
case XML_MESSAGE:
|
||||||
newRaceXmlReceived = true;
|
|
||||||
extractXmlMessage(packet);
|
extractXmlMessage(packet);
|
||||||
break;
|
break;
|
||||||
case RACE_START_STATUS:
|
case RACE_START_STATUS:
|
||||||
@@ -145,12 +105,9 @@ public class StreamParser extends Thread {
|
|||||||
case AVG_WIND:
|
case AVG_WIND:
|
||||||
extractAvgWind(packet);
|
extractAvgWind(packet);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
System.out.println("Error parsing packet");
|
System.out.println("Error parsing packet");
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,8 +169,10 @@ public class StreamParser extends Thread {
|
|||||||
if (raceStatus == 4 || raceStatus == 8) {
|
if (raceStatus == 4 || raceStatus == 8) {
|
||||||
raceFinished = true;
|
raceFinished = true;
|
||||||
raceStarted = false;
|
raceStarted = false;
|
||||||
|
ClientState.setRaceStarted(false);
|
||||||
} else if (!raceStarted) {
|
} else if (!raceStarted) {
|
||||||
raceStarted = true;
|
raceStarted = true;
|
||||||
|
ClientState.setRaceStarted(true);
|
||||||
raceFinished = false;
|
raceFinished = false;
|
||||||
}
|
}
|
||||||
timeSinceStart = timeTillStart;
|
timeSinceStart = timeTillStart;
|
||||||
@@ -228,54 +187,60 @@ public class StreamParser extends Thread {
|
|||||||
for (int i = 0; i < noBoats; i++) {
|
for (int i = 0; i < noBoats; i++) {
|
||||||
long boatStatusSourceID = bytesToLong(
|
long boatStatusSourceID = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
||||||
Yacht boat = boats.get((int) boatStatusSourceID);
|
int boatStatus = (int) payload[28 + (i * 20)];
|
||||||
boat.setBoatStatus((int) payload[28 + (i * 20)]);
|
int boatLegNumber = (int) payload[29 + (i * 20)];
|
||||||
setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
|
int boatPenaltyAwarded = (int) payload[30 + (i * 20)];
|
||||||
boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
int boatPenaltyServed = (int) payload[31 + (i * 20)];
|
||||||
boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
long estTimeAtNextMark = bytesToLong(
|
||||||
Long estTimeAtNextMark = bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
||||||
boat.setEstimateTimeAtNextMark(estTimeAtNextMark);
|
long estTimeAtFinish = bytesToLong(
|
||||||
Long estTimeAtFinish = bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
||||||
|
|
||||||
|
Yacht boat = boats.get((int) boatStatusSourceID);
|
||||||
|
boat.setBoatStatus((boatStatus));
|
||||||
|
setBoatLegPosition(boat, boatLegNumber);
|
||||||
|
boat.setPenaltiesAwarded(boatPenaltyAwarded);
|
||||||
|
boat.setPenaltiesServed(boatPenaltyServed);
|
||||||
|
boat.setEstimateTimeAtNextMark(estTimeAtNextMark);
|
||||||
boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
||||||
// boatsPos.put(estTimeAtFinish, boat);
|
|
||||||
// String boatStatus = "SourceID: " + boatStatusSourceID;
|
// Update Client State boats when receive race status packet.
|
||||||
// boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)];
|
// Potentially could replace boats in ClientPacketParser.
|
||||||
// boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)];
|
Yacht clientBoat = ClientState.getBoats().get((int) boatStatusSourceID);
|
||||||
// boatStatus += "\nPenaltiesAwarded: " + (int)payload[29 + (i * 20)];
|
clientBoat.setBoatStatus((boatStatus));
|
||||||
// boatStatus += "\nPenaltiesServed: " + (int)payload[30 + (i * 20)];
|
setBoatLegPosition(clientBoat, boatLegNumber);
|
||||||
// boatStatus += "\nEstTimeAtNextMark: " + bytesToLong(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20)));
|
clientBoat.setPenaltiesAwarded(boatPenaltyAwarded);
|
||||||
// boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20)));
|
clientBoat.setPenaltiesServed(boatPenaltyServed);
|
||||||
// boatStatuses.add(boatStatus);
|
clientBoat.setEstimateTimeAtNextMark(estTimeAtNextMark);
|
||||||
|
clientBoat.setEstimateTimeAtFinish(estTimeAtFinish);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 is race started.
|
||||||
|
// ClientState race started flag will be set to true if race started, else set false.
|
||||||
|
if (raceStatus == 3) {
|
||||||
|
ClientState.setRaceStarted(true);
|
||||||
|
} else {
|
||||||
|
ClientState.setRaceStarted(false);
|
||||||
}
|
}
|
||||||
// if (isRaceStarted()) {
|
|
||||||
// int pos = 1;
|
|
||||||
// for (Yacht yacht : boatsPos.values()) {
|
|
||||||
// yacht.setPosition(String.valueOf(pos));
|
|
||||||
// pos++;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// for (Yacht yacht : boatsPos.values()) {
|
|
||||||
// yacht.setPosition("-");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
||||||
Integer placing = 1;
|
Integer placing = 1;
|
||||||
if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
|
|
||||||
|
if (/* TODO implement when we are getting this data /TODO leg != updatingBoat.getLegNumber() && */(raceStarted || raceFinished)) {
|
||||||
for (Yacht boat : boats.values()) {
|
for (Yacht boat : boats.values()) {
|
||||||
|
placing = boat.getSourceId();
|
||||||
|
/* See above to-do
|
||||||
if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
||||||
placing += 1;
|
placing += 1;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
updatingBoat.setPosition(placing.toString());
|
updatingBoat.setPosition(placing.toString());
|
||||||
updatingBoat.setLegNumber(leg);
|
updatingBoat.setLegNumber(leg);
|
||||||
boatsPos.putIfAbsent(placing, updatingBoat);
|
boatsPos.putIfAbsent(placing, updatingBoat);
|
||||||
boatsPos.replace(placing, updatingBoat);
|
boatsPos.replace(placing, updatingBoat);
|
||||||
} else if(updatingBoat.getLegNumber() == null){
|
} else if(updatingBoat.getLegNumber() == null){
|
||||||
updatingBoat.setPosition("1");
|
updatingBoat.setPosition("-");
|
||||||
updatingBoat.setLegNumber(leg);
|
updatingBoat.setLegNumber(leg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,9 +271,8 @@ public class StreamParser extends Thread {
|
|||||||
* @param packet Packet parsed in to use the payload
|
* @param packet Packet parsed in to use the payload
|
||||||
*/
|
*/
|
||||||
private static void extractXmlMessage(StreamPacket packet) {
|
private static void extractXmlMessage(StreamPacket packet) {
|
||||||
|
xmlObject = new XMLParser();
|
||||||
byte[] payload = packet.getPayload();
|
byte[] payload = packet.getPayload();
|
||||||
|
|
||||||
int messageType = payload[9];
|
int messageType = payload[9];
|
||||||
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
|
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
|
||||||
String xmlMessage = new String(
|
String xmlMessage = new String(
|
||||||
@@ -322,15 +286,19 @@ public class StreamParser extends Thread {
|
|||||||
db = dbf.newDocumentBuilder();
|
db = dbf.newDocumentBuilder();
|
||||||
doc = db.parse(new InputSource(new StringReader(xmlMessage)));
|
doc = db.parse(new InputSource(new StringReader(xmlMessage)));
|
||||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[ClientPacketParser] ParserConfigurationException | IOException | SAXException");
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlObject.constructXML(doc, messageType);
|
xmlObject.constructXML(doc, messageType);
|
||||||
|
|
||||||
if (messageType == 7) { //7 is the boat XML
|
if (messageType == 7) { //7 is the boat XML
|
||||||
boats = xmlObject.getBoatXML().getCompetingBoats();
|
boats = xmlObject.getBoatXML().getCompetingBoats();
|
||||||
|
// Set/Update the ClientState boats after receiving new boat xml.
|
||||||
|
// Flag boatsUpdated in ClientState to true.
|
||||||
|
ClientState.setBoats(xmlObject.getBoatXML().getCompetingBoats());
|
||||||
|
ClientState.setBoatsUpdated(true);
|
||||||
}
|
}
|
||||||
if (messageType == 6) { //6 is race info xml
|
if (messageType == 6) { //6 is race info xml
|
||||||
|
|
||||||
newRaceXmlReceived = true;
|
newRaceXmlReceived = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,6 +360,7 @@ public class StreamParser extends Thread {
|
|||||||
int messageType = payload[1];
|
int messageType = payload[1];
|
||||||
int length = payload[2];
|
int length = payload[2];
|
||||||
String message = new String(Arrays.copyOfRange(payload, 3, 3 + length));
|
String message = new String(Arrays.copyOfRange(payload, 3, 3 + length));
|
||||||
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -412,13 +381,14 @@ public class StreamParser extends Thread {
|
|||||||
//Converts the double to a usable lat/lon
|
//Converts the double to a usable lat/lon
|
||||||
double lat = ((180d * (double) rawLat) / Math.pow(2, 31));
|
double lat = ((180d * (double) rawLat) / Math.pow(2, 31));
|
||||||
double lon = ((180d * (double) rawLon) / Math.pow(2, 31));
|
double lon = ((180d * (double) rawLon) / Math.pow(2, 31));
|
||||||
|
// System.out.println("[CLIENT] Lat: " + lat + " Lon: " + lon);
|
||||||
long heading = bytesToLong(Arrays.copyOfRange(payload, 28, 30));
|
long heading = bytesToLong(Arrays.copyOfRange(payload, 28, 30));
|
||||||
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
|
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
|
||||||
|
|
||||||
//type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat
|
//type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat
|
||||||
if (deviceType == 1){
|
if (deviceType == 1){
|
||||||
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
|
Yacht boat = boats.get((int) boatId);
|
||||||
heading, groundSpeed);
|
boat.setVelocity(groundSpeed);
|
||||||
|
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed);
|
||||||
|
|
||||||
//add a new priority que to the boatLocations HashMap
|
//add a new priority que to the boatLocations HashMap
|
||||||
if (!boatLocations.containsKey(boatId)) {
|
if (!boatLocations.containsKey(boatId)) {
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package seng302.client;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the client to store static variables, which other threads and classes
|
||||||
|
* observer so that they can update their status accordingly.
|
||||||
|
*/
|
||||||
|
public class ClientState {
|
||||||
|
|
||||||
|
private static String hostIp = "";
|
||||||
|
private static Boolean isHost = false;
|
||||||
|
private static Boolean raceStarted = false;
|
||||||
|
private static Boolean connectedToHost = false;
|
||||||
|
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
|
||||||
|
private static Boolean boatsUpdated = true;
|
||||||
|
private static String clientSourceId = "";
|
||||||
|
public static Stage primaryStage;
|
||||||
|
|
||||||
|
public static String getHostIp() {
|
||||||
|
return hostIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setHostIp(String hostIp) {
|
||||||
|
ClientState.hostIp = hostIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isHost() {
|
||||||
|
return isHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setHost(Boolean isHost) {
|
||||||
|
ClientState.isHost = isHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isRaceStarted() {
|
||||||
|
return raceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRaceStarted(Boolean raceStarted) {
|
||||||
|
ClientState.raceStarted = raceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isConnectedToHost() {
|
||||||
|
return connectedToHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setConnectedToHost(Boolean connectedToHost) {
|
||||||
|
ClientState.connectedToHost = connectedToHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Yacht> getBoats() {
|
||||||
|
return boats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean isBoatsUpdated() {
|
||||||
|
return boatsUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBoatsUpdated(Boolean boatsUpdated) {
|
||||||
|
ClientState.boatsUpdated = boatsUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientSourceId() {
|
||||||
|
return clientSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setClientSourceId(String clientSourceId) {
|
||||||
|
ClientState.clientSourceId = clientSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBoats(Map<Integer, Yacht> boats) {
|
||||||
|
ClientState.boats = boats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package seng302.client;
|
||||||
|
|
||||||
|
import java.util.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by LobbyController to run a separate thread-loop
|
||||||
|
* updates the controller when change is detected.
|
||||||
|
*/
|
||||||
|
public class ClientStateQueryingRunnable extends Observable implements Runnable {
|
||||||
|
|
||||||
|
private Boolean terminate = false;
|
||||||
|
|
||||||
|
public ClientStateQueryingRunnable() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies observers(the lobby controller) that "game started" if ClientState
|
||||||
|
* raceStarted flag is true and terminates itself. Also, it notifies observers
|
||||||
|
* to add/remove players if ClientState boatsUpdated flag is true, then resets
|
||||||
|
* the flag to false;
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while(!terminate) {
|
||||||
|
// Sleeping the thread so it will respond to the if statement below
|
||||||
|
// if you know a better fix, pls tell me :) -ryan
|
||||||
|
try {
|
||||||
|
Thread.sleep(0);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClientState.isRaceStarted() && ClientState.isConnectedToHost()) {
|
||||||
|
setChanged();
|
||||||
|
notifyObservers("game started");
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClientState.isBoatsUpdated()) {
|
||||||
|
setChanged();
|
||||||
|
notifyObservers("update players");
|
||||||
|
ClientState.setBoatsUpdated(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to terminate the thread.
|
||||||
|
*
|
||||||
|
* Currently called by the main while loop when game started is detected.
|
||||||
|
*/
|
||||||
|
public void terminate() {
|
||||||
|
terminate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package seng302.client;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Alert.AlertType;
|
||||||
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
import seng302.server.messages.BoatActionMessage;
|
||||||
|
import seng302.server.messages.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing a single connection to a Server for the purposes of sending and receiving on
|
||||||
|
* its own thread.
|
||||||
|
*/
|
||||||
|
public class ClientToServerThread implements Runnable {
|
||||||
|
|
||||||
|
private static final int LOG_LEVEL = 1;
|
||||||
|
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
private Integer ourID;
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
private InputStream is;
|
||||||
|
private OutputStream os;
|
||||||
|
|
||||||
|
private Boolean updateClient = true;
|
||||||
|
private ByteArrayOutputStream crcBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for ClientToServerThread which takes in ipAddress and portNumber and attempts to
|
||||||
|
* connect to the specified ipAddress and port.
|
||||||
|
*
|
||||||
|
* Upon successful socket connection, threeWayHandshake will be preformed and the instance will
|
||||||
|
* be put on a thread and run immediately.
|
||||||
|
*
|
||||||
|
* @param ipAddress a string of ip address to be connected to
|
||||||
|
* @param portNumber an integer port number
|
||||||
|
* @throws Exception SocketConnection if fail to connect to ip address and port number
|
||||||
|
* combination
|
||||||
|
*/
|
||||||
|
public ClientToServerThread(String ipAddress, Integer portNumber) throws Exception {
|
||||||
|
socket = new Socket(ipAddress, portNumber);
|
||||||
|
is = socket.getInputStream();
|
||||||
|
os = socket.getOutputStream();
|
||||||
|
|
||||||
|
Integer allocatedID = threeWayHandshake();
|
||||||
|
if (allocatedID != null) {
|
||||||
|
ourID = allocatedID;
|
||||||
|
clientLog("Successful handshake. Allocated ID: " + ourID, 0);
|
||||||
|
ClientState.setClientSourceId(String.valueOf(ourID));
|
||||||
|
} else {
|
||||||
|
clientLog("Unsuccessful handshake", 1);
|
||||||
|
closeSocket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = new Thread(this);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints out log messages and the time happened.
|
||||||
|
* Only perform task if log level is below LOG_LEVEL variable.
|
||||||
|
*
|
||||||
|
* @param message a string of message to be printed out
|
||||||
|
* @param logLevel an int for log level
|
||||||
|
*/
|
||||||
|
static void clientLog(String message, int logLevel) {
|
||||||
|
if (logLevel <= LOG_LEVEL) {
|
||||||
|
System.out.println(
|
||||||
|
"[CLIENT " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the thread loop. It exits the loop if ClientState connected to host
|
||||||
|
* variable is false.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
int sync1;
|
||||||
|
int sync2;
|
||||||
|
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
||||||
|
while (ClientState.isConnectedToHost()) {
|
||||||
|
try {
|
||||||
|
crcBuffer = new ByteArrayOutputStream();
|
||||||
|
sync1 = readByte();
|
||||||
|
sync2 = readByte();
|
||||||
|
//checking if it is the start of the packet
|
||||||
|
if (sync1 == 0x47 && sync2 == 0x83) {
|
||||||
|
int type = readByte();
|
||||||
|
//No. of milliseconds since Jan 1st 1970
|
||||||
|
long timeStamp = Message.bytesToLong(getBytes(6));
|
||||||
|
skipBytes(4);
|
||||||
|
long payloadLength = Message.bytesToLong(getBytes(2));
|
||||||
|
byte[] payload = getBytes((int) payloadLength);
|
||||||
|
Checksum checksum = new CRC32();
|
||||||
|
checksum.update(crcBuffer.toByteArray(), 0, crcBuffer.size());
|
||||||
|
long computedCrc = checksum.getValue();
|
||||||
|
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||||
|
if (computedCrc == packetCrc) {
|
||||||
|
ClientPacketParser
|
||||||
|
.parsePacket(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||||
|
} else {
|
||||||
|
clientLog("Packet has been dropped", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
closeSocket();
|
||||||
|
Platform.runLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Alert alert = new Alert(AlertType.ERROR);
|
||||||
|
alert.setHeaderText("Host has disconnected");
|
||||||
|
alert.setContentText("Cannot find Server");
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clientLog("Disconnected from server", 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeSocket();
|
||||||
|
clientLog("Disconnected from server", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for an allocated sourceID and returns it to the server
|
||||||
|
*
|
||||||
|
* @return the sourceID allocated to us by the server
|
||||||
|
*/
|
||||||
|
private Integer threeWayHandshake() {
|
||||||
|
Integer ourSourceID = null;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
ourSourceID = is.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
clientLog("Three way handshake failed", 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (ourSourceID != null) {
|
||||||
|
try {
|
||||||
|
os.write(ourSourceID);
|
||||||
|
return ourSourceID;
|
||||||
|
} catch (IOException e) {
|
||||||
|
clientLog("Three way handshake failed", 1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the post-start race course information
|
||||||
|
*/
|
||||||
|
public void sendBoatActionMessage(BoatActionMessage boatActionMessage) {
|
||||||
|
try {
|
||||||
|
os.write(boatActionMessage.getBuffer());
|
||||||
|
} catch (IOException e) {
|
||||||
|
clientLog("Could not write to server", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void closeSocket() {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
clientLog("Failed to close the socket", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int readByte() throws Exception {
|
||||||
|
int currentByte = -1;
|
||||||
|
try {
|
||||||
|
currentByte = is.read();
|
||||||
|
crcBuffer.write(currentByte);
|
||||||
|
} catch (IOException e) {
|
||||||
|
clientLog("Read byte failed", 1);
|
||||||
|
}
|
||||||
|
if (currentByte == -1) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getBytes(int n) throws Exception {
|
||||||
|
byte[] bytes = new byte[n];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
bytes[i] = (byte) readByte();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipBytes(long n) throws Exception {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
readByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread getThread() {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,44 @@
|
|||||||
package seng302.controllers;
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.canvas.Canvas;
|
||||||
import javafx.scene.canvas.GraphicsContext;
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.shape.Polygon;
|
||||||
import seng302.models.BoatGroup;
|
import javafx.scene.text.Text;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.client.ClientState;
|
||||||
|
import seng302.fxObjects.BoatGroup;
|
||||||
import seng302.models.Colors;
|
import seng302.models.Colors;
|
||||||
import seng302.models.Yacht;
|
import seng302.models.Yacht;
|
||||||
|
import seng302.models.mark.GateMark;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.fxObjects.MarkGroup;
|
||||||
|
import seng302.models.mark.MarkType;
|
||||||
|
import seng302.models.mark.SingleMark;
|
||||||
import seng302.models.map.Boundary;
|
import seng302.models.map.Boundary;
|
||||||
import seng302.models.map.CanvasMap;
|
import seng302.models.map.CanvasMap;
|
||||||
import seng302.models.mark.*;
|
|
||||||
import seng302.models.stream.StreamParser;
|
|
||||||
import seng302.models.stream.XMLParser;
|
import seng302.models.stream.XMLParser;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Limit;
|
import seng302.models.stream.XMLParser.RaceXMLObject.Limit;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
||||||
import seng302.models.stream.packets.BoatPositionPacket;
|
import seng302.models.stream.packets.BoatPositionPacket;
|
||||||
import seng302.server.simulator.GeoUtility;
|
import seng302.utilities.GeoPoint;
|
||||||
import seng302.server.simulator.mark.Position;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by ptg19 on 15/03/17.
|
* Created by ptg19 on 15/03/17.
|
||||||
@@ -49,8 +58,8 @@ public class CanvasController {
|
|||||||
private final int BUFFER_SIZE = 50;
|
private final int BUFFER_SIZE = 50;
|
||||||
private final int PANEL_WIDTH = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
private final int PANEL_WIDTH = 1260; // it should be 1280 but, minors 40 to cancel the bias.
|
||||||
private final int PANEL_HEIGHT = 960;
|
private final int PANEL_HEIGHT = 960;
|
||||||
private final int CANVAS_WIDTH = 720;
|
private final int CANVAS_WIDTH = 1100;
|
||||||
private final int CANVAS_HEIGHT = 720;
|
private final int CANVAS_HEIGHT = 920;
|
||||||
private boolean horizontalInversion = false;
|
private boolean horizontalInversion = false;
|
||||||
|
|
||||||
private double distanceScaleFactor;
|
private double distanceScaleFactor;
|
||||||
@@ -66,6 +75,8 @@ public class CanvasController {
|
|||||||
|
|
||||||
private List<MarkGroup> markGroups = new ArrayList<>();
|
private List<MarkGroup> markGroups = new ArrayList<>();
|
||||||
private List<BoatGroup> boatGroups = new ArrayList<>();
|
private List<BoatGroup> boatGroups = new ArrayList<>();
|
||||||
|
private Text FPSDisplay = new Text();
|
||||||
|
private Polygon raceBorder = new Polygon();
|
||||||
|
|
||||||
//FRAME RATE
|
//FRAME RATE
|
||||||
private Double frameRate = 60.0;
|
private Double frameRate = 60.0;
|
||||||
@@ -100,6 +111,7 @@ public class CanvasController {
|
|||||||
// Bind canvas size to stack pane size.
|
// Bind canvas size to stack pane size.
|
||||||
canvas.widthProperty().bind(new SimpleDoubleProperty(CANVAS_WIDTH));
|
canvas.widthProperty().bind(new SimpleDoubleProperty(CANVAS_WIDTH));
|
||||||
canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT));
|
canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeCanvas() {
|
public void initializeCanvas() {
|
||||||
@@ -108,18 +120,24 @@ public class CanvasController {
|
|||||||
gc.setGlobalAlpha(0.5);
|
gc.setGlobalAlpha(0.5);
|
||||||
fitMarksToCanvas();
|
fitMarksToCanvas();
|
||||||
drawGoogleMap();
|
drawGoogleMap();
|
||||||
// TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml
|
FPSDisplay.setLayoutX(5);
|
||||||
initializeBoats();
|
FPSDisplay.setLayoutY(20);
|
||||||
|
FPSDisplay.setStrokeWidth(2);
|
||||||
|
group.getChildren().add(FPSDisplay);
|
||||||
|
group.getChildren().add(raceBorder);
|
||||||
initializeMarks();
|
initializeMarks();
|
||||||
timer = new AnimationTimer() {
|
initializeBoats();
|
||||||
|
|
||||||
private int UPDATE_FPM_PERIOD = 50; // update FPM label every 50 frames
|
timer = new AnimationTimer() {
|
||||||
private int updateFPMCounter = 100;
|
private long lastTime = 0;
|
||||||
|
private int FPSCount = 30;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(long now) {
|
public void handle(long now) {
|
||||||
|
if (lastTime == 0) {
|
||||||
//fps stuff
|
lastTime = now;
|
||||||
|
} else {
|
||||||
|
if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized
|
||||||
long oldFrameTime = frameTimes[frameTimeIndex];
|
long oldFrameTime = frameTimes[frameTimeIndex];
|
||||||
frameTimes[frameTimeIndex] = now;
|
frameTimes[frameTimeIndex] = now;
|
||||||
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
|
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
|
||||||
@@ -131,20 +149,44 @@ public class CanvasController {
|
|||||||
elapsedNanos = now - oldFrameTime;
|
elapsedNanos = now - oldFrameTime;
|
||||||
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
|
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
|
||||||
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
|
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
|
||||||
if (updateFPMCounter++ > UPDATE_FPM_PERIOD) {
|
if (FPSCount-- == 0) {
|
||||||
updateFPMCounter = 0;
|
FPSCount = 30;
|
||||||
drawFps(frameRate.intValue());
|
drawFps(frameRate.intValue());
|
||||||
}
|
}
|
||||||
raceViewController.updateSparkLine();
|
raceViewController.updateSparkLine();
|
||||||
}
|
}
|
||||||
updateGroups();
|
updateGroups();
|
||||||
if (StreamParser.isRaceFinished()) {
|
if (ClientPacketParser.isRaceFinished()) {
|
||||||
this.stop();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
lastTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ClientPacketParser.isRaceFinished()) {
|
||||||
|
this.stop();
|
||||||
|
switchToFinishScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void switchToFinishScreen() {
|
||||||
|
try {
|
||||||
|
// canvas view -> anchor pane -> grid pane -> main view
|
||||||
|
GridPane gridPane = (GridPane) canvasPane.getParent().getParent();
|
||||||
|
AnchorPane contentPane = (AnchorPane) gridPane.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren().addAll(
|
||||||
|
(Pane) FXMLLoader.load(getClass().getResource("/views/FinishScreenView.fxml")));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
System.out.println("[Controller] FXML load exception");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[Controller] IO exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First find the top right and bottom left points' geo locations, then retrieve
|
* First find the top right and bottom left points' geo locations, then retrieve
|
||||||
* map from google to display on image view. - Haoming 22/5/2017
|
* map from google to display on image view. - Haoming 22/5/2017
|
||||||
@@ -153,19 +195,29 @@ public class CanvasController {
|
|||||||
findMetersPerPixel();
|
findMetersPerPixel();
|
||||||
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
||||||
// distance from top left extreme to panel origin (top left corner)
|
// distance from top left extreme to panel origin (top left corner)
|
||||||
double distanceFromTopLeftToOrigin = Math.sqrt(Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math.pow(topLeftPoint.getY() * metersPerPixelY, 2));
|
double distanceFromTopLeftToOrigin = Math.sqrt(
|
||||||
|
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
|
||||||
|
.pow(topLeftPoint.getY() * metersPerPixelY, 2));
|
||||||
// angle from top left extreme to panel origin
|
// angle from top left extreme to panel origin
|
||||||
double bearingFromTopLeftToOrigin = Math.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
|
double bearingFromTopLeftToOrigin = Math
|
||||||
|
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
|
||||||
// the top left extreme
|
// the top left extreme
|
||||||
Position topLeftPos = new Position(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
|
||||||
Position originPos = GeoUtility.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
|
GeoPoint originPos = GeoUtility
|
||||||
|
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
|
||||||
|
|
||||||
// distance from origin corner to bottom right corner of the panel
|
// distance from origin corner to bottom right corner of the panel
|
||||||
double distanceFromOriginToBottomRight = Math.sqrt(Math.pow(PANEL_HEIGHT* metersPerPixelY, 2) + Math.pow(PANEL_WIDTH * metersPerPixelX, 2));
|
double distanceFromOriginToBottomRight = Math.sqrt(
|
||||||
double bearingFromOriginToBottomRight = Math.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
|
Math.pow(PANEL_HEIGHT * metersPerPixelY, 2) + Math
|
||||||
Position bottomRightPos = GeoUtility.getGeoCoordinate(originPos, bearingFromOriginToBottomRight, distanceFromOriginToBottomRight);
|
.pow(PANEL_WIDTH * metersPerPixelX, 2));
|
||||||
|
double bearingFromOriginToBottomRight = Math
|
||||||
|
.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
|
||||||
|
GeoPoint bottomRightPos = GeoUtility
|
||||||
|
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
|
||||||
|
distanceFromOriginToBottomRight);
|
||||||
|
|
||||||
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(), bottomRightPos.getLat(), originPos.getLng());
|
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
|
||||||
|
bottomRightPos.getLat(), originPos.getLng());
|
||||||
CanvasMap canvasMap = new CanvasMap(boundary);
|
CanvasMap canvasMap = new CanvasMap(boundary);
|
||||||
mapImage.setImage(canvasMap.getMapImage());
|
mapImage.setImage(canvasMap.getMapImage());
|
||||||
}
|
}
|
||||||
@@ -173,57 +225,37 @@ public class CanvasController {
|
|||||||
/**
|
/**
|
||||||
* Adds border marks to the canvas, taken from the XML file
|
* Adds border marks to the canvas, taken from the XML file
|
||||||
*
|
*
|
||||||
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and CompoundMark which are
|
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and
|
||||||
* named the same as those in the model package but are, however not the same, so they do not have things such as
|
* CompoundMark which are named the same as those in the model package but are, however not the
|
||||||
* a type and must be derived from the number of marks in a compound mark etc..
|
* same, so they do not have things such as a type and must be derived from the number of marks
|
||||||
|
* in a compound mark etc..
|
||||||
*/
|
*/
|
||||||
private void addRaceBorder() {
|
private void addRaceBorder() {
|
||||||
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML();
|
XMLParser.RaceXMLObject raceXMLObject = ClientPacketParser.getXmlObject().getRaceXML();
|
||||||
ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit();
|
ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit();
|
||||||
gc.setStroke(Color.DARKBLUE);
|
raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
|
||||||
gc.setLineWidth(3);
|
raceBorder.setStrokeWidth(3);
|
||||||
double[] xBoundaryPoints = new double[courseLimits.size()];
|
raceBorder.setFill(new Color(0,0,0,0));
|
||||||
double[] yBoundaryPoints = new double[courseLimits.size()];
|
List<Double> boundaryPoints = new ArrayList<>();
|
||||||
for (int i = 0; i < courseLimits.size() - 1; i++) {
|
for (Limit limit : courseLimits) {
|
||||||
Limit thisPoint1 = courseLimits.get(i);
|
Point2D location = findScaledXY(limit.getLat(), limit.getLng());
|
||||||
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
|
boundaryPoints.add(location.getX());
|
||||||
Limit thisPoint2 = courseLimits.get(i+1);
|
boundaryPoints.add(location.getY());
|
||||||
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
|
|
||||||
Point2D borderPoint1 = findScaledXY(thisMark1);
|
|
||||||
Point2D borderPoint2 = findScaledXY(thisMark2);
|
|
||||||
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
|
||||||
borderPoint2.getX(), borderPoint2.getY());
|
|
||||||
xBoundaryPoints[i] = borderPoint1.getX();
|
|
||||||
yBoundaryPoints[i] = borderPoint1.getY();
|
|
||||||
}
|
}
|
||||||
Limit thisPoint1 = courseLimits.get(courseLimits.size()-1);
|
raceBorder.getPoints().setAll(boundaryPoints);
|
||||||
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
|
|
||||||
Limit thisPoint2 = courseLimits.get(0);
|
|
||||||
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
|
|
||||||
Point2D borderPoint1 = findScaledXY(thisMark1);
|
|
||||||
Point2D borderPoint2 = findScaledXY(thisMark2);
|
|
||||||
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
|
||||||
borderPoint2.getX(), borderPoint2.getY());
|
|
||||||
xBoundaryPoints[courseLimits.size()-1] = borderPoint1.getX();
|
|
||||||
yBoundaryPoints[courseLimits.size()-1] = borderPoint1.getY();
|
|
||||||
// gc.setFill(Color.LIGHTBLUE);
|
|
||||||
// gc.fillPolygon(xBoundaryPoints,yBoundaryPoints,yBoundaryPoints.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGroups() {
|
private void updateGroups() {
|
||||||
for (BoatGroup boatGroup : boatGroups) {
|
for (BoatGroup boatGroup : boatGroups) {
|
||||||
// some raceObjects will have multiple ID's (for instance gate marks)
|
// some raceObjects will have multiple ID's (for instance gate marks)
|
||||||
//checking if the current "ID" has any updates associated with it
|
//checking if the current "ID" has any updates associated with it
|
||||||
if (StreamParser.boatLocations.containsKey(boatGroup.getRaceId())) {
|
if (ClientPacketParser.boatLocations.containsKey(boatGroup.getRaceId())) {
|
||||||
if (boatGroup.isStopped()) {
|
|
||||||
updateBoatGroup(boatGroup);
|
updateBoatGroup(boatGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boatGroup.move();
|
|
||||||
}
|
|
||||||
for (MarkGroup markGroup : markGroups) {
|
for (MarkGroup markGroup : markGroups) {
|
||||||
for (Long id : markGroup.getRaceIds()) {
|
for (Long id : markGroup.getRaceIds()) {
|
||||||
if (StreamParser.markLocations.containsKey(id)) {
|
if (ClientPacketParser.markLocations.containsKey(id)) {
|
||||||
updateMarkGroup(id, markGroup);
|
updateMarkGroup(id, markGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,38 +264,39 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkForCourseChanges() {
|
private void checkForCourseChanges() {
|
||||||
if (StreamParser.isNewRaceXmlReceived()){
|
if (ClientPacketParser.isNewRaceXmlReceived()) {
|
||||||
gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
||||||
drawGoogleMap();
|
|
||||||
addRaceBorder();
|
addRaceBorder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBoatGroup(BoatGroup boatGroup) {
|
private void updateBoatGroup(BoatGroup boatGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
PriorityBlockingQueue<BoatPositionPacket> movementQueue = ClientPacketParser.boatLocations
|
||||||
|
.get(boatGroup.getRaceId());
|
||||||
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
||||||
if (movementQueue.size() > 0) {
|
if (movementQueue.size() > 0) {
|
||||||
try {
|
try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
BoatPositionPacket positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
double heading = 360.0 / 0xffff * positionPacket.getHeading();
|
double heading = 360.0 / 0xffff * positionPacket.getHeading();
|
||||||
boatGroup.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), positionPacket.getTimeValid(), frameRate, boatGroup.getRaceId());
|
boatGroup.setDestination(
|
||||||
|
p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(),
|
||||||
|
positionPacket.getTimeValid(), frameRate);
|
||||||
} catch (InterruptedException e){
|
} catch (InterruptedException e){
|
||||||
e.printStackTrace();
|
System.out.println("[CanvasController] Interrupted Exception");
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.markLocations.get(raceId);
|
PriorityBlockingQueue<BoatPositionPacket> movementQueue = ClientPacketParser.markLocations
|
||||||
|
.get(raceId);
|
||||||
if (movementQueue.size() > 0){
|
if (movementQueue.size() > 0){
|
||||||
try {
|
try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
BoatPositionPacket positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[CanvasController] Interrupted exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,29 +305,52 @@ public class CanvasController {
|
|||||||
* Draws all the boats.
|
* Draws all the boats.
|
||||||
*/
|
*/
|
||||||
private void initializeBoats() {
|
private void initializeBoats() {
|
||||||
Map<Integer, Yacht> boats = StreamParser.getBoats();
|
Map<Integer, Yacht> boats = ClientPacketParser.getBoats();
|
||||||
Group boatAnnotations = new Group();
|
Group wakes = new Group();
|
||||||
|
Group trails = new Group();
|
||||||
|
Group annotations = new Group();
|
||||||
|
|
||||||
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML().getParticipants();
|
ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
|
||||||
|
.getParticipants();
|
||||||
ArrayList<Integer> participantIDs = new ArrayList<>();
|
ArrayList<Integer> participantIDs = new ArrayList<>();
|
||||||
for (Participant p : participants) {
|
for (Participant p : participants) {
|
||||||
participantIDs.add(p.getsourceID());
|
participantIDs.add(p.getsourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Yacht boat : boats.values()) {
|
for (Yacht boat : boats.values()) {
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
if (participantIDs.contains(boat.getSourceId())) {
|
||||||
boat.setColour(Colors.getColor());
|
boat.setColour(Colors.getColor());
|
||||||
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
|
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
|
||||||
|
if (boat.getSourceId().equals(Integer.parseInt(ClientState.getClientSourceId()))) {
|
||||||
|
boatGroup.setAsPlayer();
|
||||||
boatGroups.add(boatGroup);
|
boatGroups.add(boatGroup);
|
||||||
boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations());
|
annotations.getChildren().add(boatGroup.getAnnotations());
|
||||||
|
} else {
|
||||||
|
//Move annotations and boat to bottom of group keeping player ontop.
|
||||||
|
if (boatGroups.size() > 0) {
|
||||||
|
boatGroups.add(0, boatGroup);
|
||||||
|
} else {
|
||||||
|
boatGroups.add(boatGroup);
|
||||||
|
}
|
||||||
|
if (annotations.getChildren().size() > 0) {
|
||||||
|
annotations.getChildren().add(0, boatGroup.getAnnotations());
|
||||||
|
} else {
|
||||||
|
annotations.getChildren().add(boatGroup.getAnnotations());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
group.getChildren().add(boatAnnotations);
|
trails.getChildren().add(boatGroup.getTrail());
|
||||||
|
wakes.getChildren().add(boatGroup.getWake());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.getChildren().addAll(trails);
|
||||||
|
group.getChildren().addAll(wakes);
|
||||||
|
group.getChildren().addAll(annotations);
|
||||||
group.getChildren().addAll(boatGroups);
|
group.getChildren().addAll(boatGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMarks() {
|
private void initializeMarks() {
|
||||||
List<Mark> allMarks = StreamParser.getXmlObject().getRaceXML().getNonDupCompoundMarks();
|
List<Mark> allMarks = ClientPacketParser.getXmlObject().getRaceXML()
|
||||||
|
.getNonDupCompoundMarks();
|
||||||
for (Mark mark : allMarks) {
|
for (Mark mark : allMarks) {
|
||||||
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
|
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
|
||||||
SingleMark sMark = (SingleMark) mark;
|
SingleMark sMark = (SingleMark) mark;
|
||||||
@@ -304,7 +360,8 @@ public class CanvasController {
|
|||||||
} else {
|
} else {
|
||||||
GateMark gMark = (GateMark) mark;
|
GateMark gMark = (GateMark) mark;
|
||||||
|
|
||||||
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()), findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
|
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()),
|
||||||
|
findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
|
||||||
markGroups.add(markGroup);
|
markGroups.add(markGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,6 +393,7 @@ public class CanvasController {
|
|||||||
public double prefWidth(double height) {
|
public double prefWidth(double height) {
|
||||||
return getWidth();
|
return getWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double prefHeight(double width) {
|
public double prefHeight(double width) {
|
||||||
return getHeight();
|
return getHeight();
|
||||||
@@ -345,23 +403,20 @@ public class CanvasController {
|
|||||||
|
|
||||||
private void drawFps(int fps){
|
private void drawFps(int fps){
|
||||||
if (raceViewController.isDisplayFps()){
|
if (raceViewController.isDisplayFps()){
|
||||||
gc.clearRect(5, 5, 60, 30);
|
FPSDisplay.setVisible(true);
|
||||||
gc.setFont(new Font(16));
|
FPSDisplay.setText(String.format("%d FPS", fps));
|
||||||
gc.setLineWidth(4);
|
|
||||||
gc.setGlobalAlpha(0.75);
|
|
||||||
gc.fillText(fps + " FPS", 5, 20);
|
|
||||||
gc.setGlobalAlpha(0.5);
|
|
||||||
} else {
|
} else {
|
||||||
gc.clearRect(5,5,60,30);
|
FPSDisplay.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates x and y location for every marker that fits it to the canvas the race will be drawn on.
|
* Calculates x and y location for every marker that fits it to the canvas the race will be
|
||||||
|
* drawn on.
|
||||||
*/
|
*/
|
||||||
private void fitMarksToCanvas() {
|
private void fitMarksToCanvas() {
|
||||||
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
//Check is called once to avoid unnecessarily change the course limits once the race is running
|
||||||
StreamParser.isNewRaceXmlReceived();
|
ClientPacketParser.isNewRaceXmlReceived();
|
||||||
findMinMaxPoint();
|
findMinMaxPoint();
|
||||||
double minLonToMaxLon = scaleRaceExtremities();
|
double minLonToMaxLon = scaleRaceExtremities();
|
||||||
calculateReferencePointLocation(minLonToMaxLon);
|
calculateReferencePointLocation(minLonToMaxLon);
|
||||||
@@ -371,12 +426,14 @@ public class CanvasController {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker with the leftmost
|
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker
|
||||||
* marker, rightmost marker, southern most marker and northern most marker respectively.
|
* with the leftmost marker, rightmost marker, southern most marker and northern most marker
|
||||||
|
* respectively.
|
||||||
*/
|
*/
|
||||||
private void findMinMaxPoint() {
|
private void findMinMaxPoint() {
|
||||||
List<Limit> sortedPoints = new ArrayList<>();
|
List<Limit> sortedPoints = new ArrayList<>();
|
||||||
for (Limit limit : StreamParser.getXmlObject().getRaceXML().getCourseLimit()) {
|
|
||||||
|
for (Limit limit : ClientPacketParser.getXmlObject().getRaceXML().getCourseLimit()) {
|
||||||
sortedPoints.add(limit);
|
sortedPoints.add(limit);
|
||||||
}
|
}
|
||||||
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
||||||
@@ -397,10 +454,11 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the location of a reference point, this is always the point with minimum latitude, in relation to the
|
* Calculates the location of a reference point, this is always the point with minimum latitude,
|
||||||
* canvas.
|
* in relation to the canvas.
|
||||||
*
|
*
|
||||||
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to maximum longitude.
|
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
|
||||||
|
* maximum longitude.
|
||||||
*/
|
*/
|
||||||
private void calculateReferencePointLocation(double minLonToMaxLon) {
|
private void calculateReferencePointLocation(double minLonToMaxLon) {
|
||||||
Mark referencePoint = minLatPoint;
|
Mark referencePoint = minLatPoint;
|
||||||
@@ -431,20 +489,23 @@ public class CanvasController {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns it to distanceScaleFactor
|
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
|
||||||
* Returns the max horizontal distance of the map.
|
* it to distanceScaleFactor Returns the max horizontal distance of the map.
|
||||||
*/
|
*/
|
||||||
private double scaleRaceExtremities() {
|
private double scaleRaceExtremities() {
|
||||||
|
|
||||||
double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint));
|
double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint));
|
||||||
double vertDistance = Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
|
double vertDistance =
|
||||||
|
Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
|
||||||
double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint);
|
double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint);
|
||||||
|
|
||||||
if (horiAngle <= (Math.PI / 2))
|
if (horiAngle <= (Math.PI / 2)) {
|
||||||
horiAngle = (Math.PI / 2) - horiAngle;
|
horiAngle = (Math.PI / 2) - horiAngle;
|
||||||
else
|
} else {
|
||||||
horiAngle = horiAngle - (Math.PI / 2);
|
horiAngle = horiAngle - (Math.PI / 2);
|
||||||
double horiDistance = Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
|
}
|
||||||
|
double horiDistance =
|
||||||
|
Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
|
||||||
|
|
||||||
double vertScale = (CANVAS_HEIGHT - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
|
double vertScale = (CANVAS_HEIGHT - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
|
||||||
|
|
||||||
@@ -468,23 +529,35 @@ public class CanvasController {
|
|||||||
int xAxisLocation = (int) referencePointX;
|
int xAxisLocation = (int) referencePointX;
|
||||||
int yAxisLocation = (int) referencePointY;
|
int yAxisLocation = (int) referencePointY;
|
||||||
|
|
||||||
angleFromReference = Mark.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
|
angleFromReference = Mark
|
||||||
distanceFromReference = Mark.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
|
.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
|
||||||
|
unscaledLon);
|
||||||
|
distanceFromReference = Mark
|
||||||
|
.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
|
||||||
|
unscaledLon);
|
||||||
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
|
||||||
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
xAxisLocation += (int) Math
|
||||||
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation -= (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
} else if (angleFromReference >= 0) {
|
} else if (angleFromReference >= 0) {
|
||||||
angleFromReference = angleFromReference - Math.PI / 2;
|
angleFromReference = angleFromReference - Math.PI / 2;
|
||||||
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
xAxisLocation += (int) Math
|
||||||
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation += (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
|
||||||
angleFromReference = Math.abs(angleFromReference);
|
angleFromReference = Math.abs(angleFromReference);
|
||||||
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
xAxisLocation -= (int) Math
|
||||||
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation -= (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
} else {
|
} else {
|
||||||
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
|
||||||
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
xAxisLocation -= (int) Math
|
||||||
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
|
||||||
|
yAxisLocation += (int) Math
|
||||||
|
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
|
||||||
}
|
}
|
||||||
if(horizontalInversion) {
|
if(horizontalInversion) {
|
||||||
xAxisLocation = CANVAS_WIDTH - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
|
xAxisLocation = CANVAS_WIDTH - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
|
||||||
@@ -520,4 +593,5 @@ public class CanvasController {
|
|||||||
List<MarkGroup> getMarkGroups() {
|
List<MarkGroup> getMarkGroups() {
|
||||||
return markGroups;
|
return markGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,106 @@
|
|||||||
package seng302.controllers;
|
package seng302.controllers;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import seng302.models.stream.StreamParser;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.client.ClientState;
|
||||||
|
import seng302.client.ClientToServerThread;
|
||||||
|
import seng302.server.messages.BoatActionMessage;
|
||||||
|
import seng302.server.messages.BoatActionType;
|
||||||
|
|
||||||
public class Controller implements Initializable {
|
public class Controller implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private AnchorPane contentPane;
|
private AnchorPane contentPane;
|
||||||
|
private ClientToServerThread clientToServerThread;
|
||||||
|
private long lastSendingTime;
|
||||||
|
private int KEY_STROKE_SENDING_FREQUENCY = 50;
|
||||||
|
|
||||||
private void setContentPane(String jfxUrl) {
|
public Object setContentPane(String jfxUrl) {
|
||||||
try {
|
try {
|
||||||
contentPane.getChildren().removeAll();
|
contentPane.getChildren().removeAll();
|
||||||
contentPane.getChildren().clear();
|
contentPane.getChildren().clear();
|
||||||
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
contentPane.getChildren()
|
FXMLLoader fxmlLoader = new FXMLLoader((getClass().getResource(jfxUrl)));
|
||||||
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
Parent view = fxmlLoader.load();
|
||||||
|
contentPane.getChildren().addAll(view);
|
||||||
|
return fxmlLoader.getController();
|
||||||
} catch (javafx.fxml.LoadException e) {
|
} catch (javafx.fxml.LoadException e) {
|
||||||
System.err.println(e.getCause());
|
System.err.println(e.getCause());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println(e);
|
System.err.println(e);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
setUpStartScreen();
|
||||||
|
lastSendingTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUpStartScreen() {
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
setContentPane("/views/StartScreenView.fxml");
|
StartScreenController startScreenController = (StartScreenController) setContentPane("/views/StartScreenView.fxml");
|
||||||
StreamParser.boatLocations.clear();
|
startScreenController.setController(this);
|
||||||
|
ClientPacketParser.boatLocations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Handle the key-pressed event from the text field. */
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
BoatActionMessage boatActionMessage;
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
if (currentTime - lastSendingTime > KEY_STROKE_SENDING_FREQUENCY && ClientState.isRaceStarted()) {
|
||||||
|
lastSendingTime = currentTime;
|
||||||
|
switch (e.getCode()) {
|
||||||
|
case SPACE: // align with vmg
|
||||||
|
boatActionMessage = new BoatActionMessage(BoatActionType.VMG);
|
||||||
|
clientToServerThread.sendBoatActionMessage(boatActionMessage);
|
||||||
|
break;
|
||||||
|
case PAGE_UP: // upwind
|
||||||
|
boatActionMessage = new BoatActionMessage(BoatActionType.UPWIND);
|
||||||
|
clientToServerThread.sendBoatActionMessage(boatActionMessage);
|
||||||
|
break;
|
||||||
|
case PAGE_DOWN: // downwind
|
||||||
|
boatActionMessage = new BoatActionMessage(BoatActionType.DOWNWIND);
|
||||||
|
clientToServerThread.sendBoatActionMessage(boatActionMessage);
|
||||||
|
break;
|
||||||
|
case ENTER: // tack/gybe
|
||||||
|
boatActionMessage = new BoatActionMessage(BoatActionType.TACK_GYBE);
|
||||||
|
clientToServerThread.sendBoatActionMessage(boatActionMessage);
|
||||||
|
break;
|
||||||
|
//TODO Allow a zoom in and zoom out methods
|
||||||
|
case Z: // zoom in
|
||||||
|
System.out.println("Zoom in");
|
||||||
|
break;
|
||||||
|
case X: // zoom out
|
||||||
|
System.out.println("Zoom out");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
switch (e.getCode()) {
|
||||||
|
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
|
||||||
|
case SHIFT: // sails in/sails out
|
||||||
|
BoatActionMessage boatActionMessage = new BoatActionMessage(BoatActionType.SAILS_IN);
|
||||||
|
clientToServerThread.sendBoatActionMessage(boatActionMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientToServerThread(ClientToServerThread ctt) {
|
||||||
|
clientToServerThread = ctt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
||||||
|
|
||||||
|
public class FinishScreenViewController implements Initializable {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane finishScreenGridPane;
|
||||||
|
@FXML
|
||||||
|
private TableView<Yacht> finishOrderTable;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> posCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> boatNameCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> shortNameCol;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<Yacht, String> countryCol;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
finishScreenGridPane.getStylesheets()
|
||||||
|
.add(getClass().getResource("/css/master.css").toString());
|
||||||
|
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
|
||||||
|
// set up data for table
|
||||||
|
ObservableList<Yacht> data = FXCollections.observableArrayList();
|
||||||
|
finishOrderTable.setItems(data);
|
||||||
|
|
||||||
|
// setting table col data
|
||||||
|
posCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("position")
|
||||||
|
);
|
||||||
|
boatNameCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("boatName")
|
||||||
|
);
|
||||||
|
shortNameCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("shortName")
|
||||||
|
);
|
||||||
|
countryCol.setCellValueFactory(
|
||||||
|
new PropertyValueFactory<>("country")
|
||||||
|
);
|
||||||
|
|
||||||
|
// check if the boat is racing
|
||||||
|
ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
|
||||||
|
.getParticipants();
|
||||||
|
ArrayList<Integer> participantIDs = new ArrayList<>();
|
||||||
|
for (Participant p : participants) {
|
||||||
|
participantIDs.add(p.getsourceID());
|
||||||
|
}
|
||||||
|
|
||||||
|
// add data to table
|
||||||
|
for (Yacht boat : ClientPacketParser.getBoatsPos().values()) {
|
||||||
|
if (participantIDs.contains(boat.getSourceId())) {
|
||||||
|
data.add(boat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finishOrderTable.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContentPane(String jfxUrl) {
|
||||||
|
try {
|
||||||
|
// get the main controller anchor pane (FinishView -> MainView)
|
||||||
|
AnchorPane contentPane = (AnchorPane) finishScreenGridPane.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren()
|
||||||
|
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
System.out.println("[Controller] FXML load exception");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[Controller] IO exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToStartScreenView() {
|
||||||
|
setContentPane("/views/StartScreenView.fxml");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
package seng302.controllers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import seng302.client.ClientState;
|
||||||
|
import seng302.client.ClientStateQueryingRunnable;
|
||||||
|
import seng302.gameServer.GameStages;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
|
import seng302.gameServer.MainServerThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing the actions of the lobby screen
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class LobbyController implements Initializable, Observer{
|
||||||
|
@FXML
|
||||||
|
private GridPane lobbyScreen;
|
||||||
|
@FXML
|
||||||
|
private Text lobbyIpText;
|
||||||
|
@FXML
|
||||||
|
private Button readyButton;
|
||||||
|
@FXML
|
||||||
|
private ListView firstListView;
|
||||||
|
@FXML
|
||||||
|
private ListView secondListView;
|
||||||
|
@FXML
|
||||||
|
private ListView thirdListView;
|
||||||
|
@FXML
|
||||||
|
private ListView fourthListView;
|
||||||
|
@FXML
|
||||||
|
private ListView fifthListView;
|
||||||
|
@FXML
|
||||||
|
private ListView sixthListView;
|
||||||
|
@FXML
|
||||||
|
private ListView seventhListView;
|
||||||
|
@FXML
|
||||||
|
private ListView eighthListView;
|
||||||
|
@FXML
|
||||||
|
private ImageView firstImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView secondImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView thirdImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView fourthImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView fifthImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView sixthImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView seventhImageView;
|
||||||
|
@FXML
|
||||||
|
private ImageView eighthImageView;
|
||||||
|
|
||||||
|
private static List<ObservableList<String>> competitors = new ArrayList<>();
|
||||||
|
private static ObservableList<String> firstCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> secondCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> thirdCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> fourthCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> fifthCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> sixthCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> seventhCompetitor = FXCollections.observableArrayList();
|
||||||
|
private static ObservableList<String> eighthCompetitor = FXCollections.observableArrayList();
|
||||||
|
private ClientStateQueryingRunnable clientStateQueryingRunnable;
|
||||||
|
private static List<ImageView> imageViews;
|
||||||
|
private static List<ListView> listViews;
|
||||||
|
|
||||||
|
private int MAX_NUM_PLAYERS = 8;
|
||||||
|
|
||||||
|
private Boolean switchedPane = false;
|
||||||
|
private MainServerThread mainServerThread;
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
private void setContentPane(String jfxUrl) {
|
||||||
|
try {
|
||||||
|
AnchorPane contentPane = (AnchorPane) lobbyScreen.getParent();
|
||||||
|
contentPane.getChildren().removeAll();
|
||||||
|
contentPane.getChildren().clear();
|
||||||
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
contentPane.getChildren()
|
||||||
|
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
||||||
|
} catch (javafx.fxml.LoadException e) {
|
||||||
|
System.out.println("[Controller] FXML load exception");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[Controller] IO exception");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// System.out.println("[Controller] Null Pointer Exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
if (ClientState.isHost()) {
|
||||||
|
lobbyIpText.setText("Lobby Host IP: " + ClientState.getHostIp());
|
||||||
|
readyButton.setDisable(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lobbyIpText.setText("Connected to IP: " + ClientState.getHostIp());
|
||||||
|
readyButton.setDisable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put all javafx objects in lists, so we can iterate though conveniently
|
||||||
|
imageViews = new ArrayList<>();
|
||||||
|
Collections.addAll(imageViews, firstImageView, secondImageView, thirdImageView, fourthImageView,
|
||||||
|
fifthImageView, sixthImageView, seventhImageView, eighthImageView);
|
||||||
|
listViews = new ArrayList<>();
|
||||||
|
Collections.addAll(listViews, firstListView, secondListView, thirdListView, fourthListView, fifthListView,
|
||||||
|
sixthListView, seventhListView, eighthListView);
|
||||||
|
competitors = new ArrayList<>();
|
||||||
|
Collections.addAll(competitors, firstCompetitor, secondCompetitor, thirdCompetitor,
|
||||||
|
fourthCompetitor, fifthCompetitor, sixthCompetitor, seventhCompetitor, eighthCompetitor);
|
||||||
|
|
||||||
|
initialiseListView();
|
||||||
|
initialiseImageView(); // parrot gif init
|
||||||
|
|
||||||
|
// set up client state query thread, so that when it receives the race-started packet
|
||||||
|
// it can switch to the race view
|
||||||
|
ClientStateQueryingRunnable clientStateQueryingRunnable = new ClientStateQueryingRunnable();
|
||||||
|
clientStateQueryingRunnable.addObserver(this);
|
||||||
|
Thread clientStateQueryingThread = new Thread(clientStateQueryingRunnable, "Client State querying thread");
|
||||||
|
clientStateQueryingThread.setDaemon(true);
|
||||||
|
clientStateQueryingThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observers "ClientStateQueryingRunnable".
|
||||||
|
* When the clients state has been marked to "race start", the querying thread
|
||||||
|
* will notify this lobby to change the view
|
||||||
|
* @param o
|
||||||
|
* @param arg
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
Platform.runLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (arg.equals("game started") && !switchedPane) {
|
||||||
|
switchToRaceView();
|
||||||
|
}
|
||||||
|
if (arg.equals(("update players"))) {
|
||||||
|
initialiseListView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all ListViews and ImageViews according to the current competitors
|
||||||
|
*/
|
||||||
|
private void initialiseListView() {
|
||||||
|
listViews.forEach(listView -> listView.getItems().clear());
|
||||||
|
imageViews.forEach(gif -> gif.setVisible(false));
|
||||||
|
competitors.forEach(ol -> ol.removeAll());
|
||||||
|
|
||||||
|
List<Integer> ids = new ArrayList<>(ClientState.getBoats().keySet());
|
||||||
|
for (int i = 0; i < ids.size(); i++) {
|
||||||
|
competitors.get(i).add(ClientState.getBoats().get(ids.get(i)).getBoatName());
|
||||||
|
listViews.get(i).setItems(competitors.get(i));
|
||||||
|
imageViews.get(i).setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads preset images into imageViews
|
||||||
|
*/
|
||||||
|
private void initialiseImageView() {
|
||||||
|
for (int i = 0; i < MAX_NUM_PLAYERS; i++) {
|
||||||
|
imageViews.get(i).setImage(new Image(getClass().getResourceAsStream("/pics/sail.png")));
|
||||||
|
}
|
||||||
|
// Image image1 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// firstImageView.setImage(image1);
|
||||||
|
// Image image2 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// secondImageView.setImage(image2);
|
||||||
|
// Image image3 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// thirdImageView.setImage(image3);
|
||||||
|
// Image image4 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// fourthImageView.setImage(image4);
|
||||||
|
// Image image5 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// fifthImageView.setImage(image5);
|
||||||
|
// Image image6 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// sixthImageView.setImage(image6);
|
||||||
|
// Image image7 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// seventhImageView.setImage(image7);
|
||||||
|
// Image image8 = new Image(getClass().getResourceAsStream("/pics/sail.png"));
|
||||||
|
// eighthImageView.setImage(image8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void leaveLobbyButtonPressed() {
|
||||||
|
if (ClientState.isHost()) {
|
||||||
|
GameState.setCurrentStage(GameStages.CANCELLED);
|
||||||
|
mainServerThread.terminate();
|
||||||
|
}
|
||||||
|
ClientState.setConnectedToHost(false);
|
||||||
|
controller.setUpStartScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void readyButtonPressed() {
|
||||||
|
GameState.setCurrentStage(GameStages.RACING);
|
||||||
|
mainServerThread.startGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void switchToRaceView() {
|
||||||
|
if (!switchedPane) {
|
||||||
|
switchedPane = true;
|
||||||
|
setContentPane("/views/RaceView.fxml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMainServerThread(MainServerThread mainServerThread) {
|
||||||
|
this.mainServerThread = mainServerThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(Controller controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import javafx.collections.ObservableList;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.geometry.Side;
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.chart.NumberAxis;
|
import javafx.scene.chart.NumberAxis;
|
||||||
@@ -28,17 +27,18 @@ import javafx.stage.Stage;
|
|||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import seng302.GeometryUtils;
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.controllers.annotations.Annotation;
|
import seng302.controllers.annotations.Annotation;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationController;
|
import seng302.controllers.annotations.ImportantAnnotationController;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationDelegate;
|
import seng302.controllers.annotations.ImportantAnnotationDelegate;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationsState;
|
import seng302.controllers.annotations.ImportantAnnotationsState;
|
||||||
|
import seng302.fxObjects.BoatGroup;
|
||||||
|
import seng302.fxObjects.MarkGroup;
|
||||||
import seng302.models.*;
|
import seng302.models.*;
|
||||||
import seng302.models.mark.GateMark;
|
import seng302.models.mark.GateMark;
|
||||||
import seng302.models.mark.Mark;
|
import seng302.models.mark.Mark;
|
||||||
import seng302.models.mark.MarkGroup;
|
|
||||||
import seng302.models.mark.SingleMark;
|
import seng302.models.mark.SingleMark;
|
||||||
import seng302.models.stream.StreamParser;
|
|
||||||
import seng302.models.stream.XMLParser;
|
import seng302.models.stream.XMLParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -51,6 +51,8 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Text windSpeedText;
|
||||||
@FXML
|
@FXML
|
||||||
private LineChart raceSparkLine;
|
private LineChart raceSparkLine;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -93,7 +95,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
raceSparkLine.getYAxis().setTranslateX(-5);
|
raceSparkLine.getYAxis().setTranslateX(-5);
|
||||||
raceSparkLine.getYAxis().setAutoRanging(false);
|
raceSparkLine.getYAxis().setAutoRanging(false);
|
||||||
sparklineYAxis.setTickMarkVisible(false);
|
sparklineYAxis.setTickMarkVisible(false);
|
||||||
startingBoats = new ArrayList<>(StreamParser.getBoats().values());
|
startingBoats = new ArrayList<>(ClientPacketParser.getBoats().values());
|
||||||
|
|
||||||
includedCanvasController.setup(this);
|
includedCanvasController.setup(this);
|
||||||
includedCanvasController.initializeCanvas();
|
includedCanvasController.initializeCanvas();
|
||||||
@@ -103,6 +105,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
initialiseBoatSelectionComboBox();
|
initialiseBoatSelectionComboBox();
|
||||||
includedCanvasController.timer.start();
|
includedCanvasController.timer.start();
|
||||||
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -136,14 +139,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
Scene scene = new Scene(fxmlLoader.load(), 469, 298);
|
Scene scene = new Scene(fxmlLoader.load(), 469, 298);
|
||||||
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
stage.initStyle(StageStyle.UNDECORATED);
|
stage.initStyle(StageStyle.UNDECORATED);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
|
|
||||||
controller.loadState(importantAnnotations);
|
controller.loadState(importantAnnotations);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[RaceViewController] IO exception");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +201,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
*/
|
*/
|
||||||
void updateSparkLine(){
|
void updateSparkLine(){
|
||||||
// Collect the racing boats that aren't already in the chart
|
// Collect the racing boats that aren't already in the chart
|
||||||
ArrayList<Yacht> sparkLineCandidates = startingBoats.stream().filter(yacht -> !sparkLineData.containsKey(yacht.getSourceID())
|
ArrayList<Yacht> sparkLineCandidates = startingBoats.stream().filter(yacht -> !sparkLineData.containsKey(yacht.getSourceId())
|
||||||
&& yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
&& yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
|
||||||
// Obtain the qualifying boats to set the max on the Y axis
|
// Obtain the qualifying boats to set the max on the Y axis
|
||||||
@@ -212,7 +214,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
Series<String, Double> yachtData = new Series<>();
|
Series<String, Double> yachtData = new Series<>();
|
||||||
yachtData.setName(yacht.getBoatName());
|
yachtData.setName(yacht.getBoatName());
|
||||||
yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
||||||
sparkLineData.put(yacht.getSourceID(), yachtData);
|
sparkLineData.put(yacht.getSourceId(), yachtData);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
||||||
@@ -242,7 +244,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
* @param legNumber the leg number that the position will be assigned to
|
* @param legNumber the leg number that the position will be assigned to
|
||||||
*/
|
*/
|
||||||
public static void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
|
public static void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
|
||||||
XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
|
XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceId());
|
||||||
positionData.getData().add(new XYChart.Data<>(Integer.toString(legNumber), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
positionData.getData().add(new XYChart.Data<>(Integer.toString(legNumber), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,8 +285,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
event -> {
|
event -> {
|
||||||
updateRaceTime();
|
updateRaceTime();
|
||||||
updateWindDirection();
|
updateWindDirection();
|
||||||
updateOrder();
|
// updateOrder();
|
||||||
updateBoatSelectionComboBox();
|
updateBoatSelectionComboBox();
|
||||||
|
updateOrder();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -303,7 +306,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
private Mark getNextMark(BoatGroup bg) {
|
private Mark getNextMark(BoatGroup bg) {
|
||||||
Integer legNumber = bg.getBoat().getLegNumber();
|
Integer legNumber = bg.getBoat().getLegNumber();
|
||||||
|
|
||||||
List<XMLParser.RaceXMLObject.Corner> markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence();
|
List<XMLParser.RaceXMLObject.Corner> markSequence = ClientPacketParser.getXmlObject()
|
||||||
|
.getRaceXML().getCompoundMarkSequence();
|
||||||
|
|
||||||
if (legNumber == 0) {
|
if (legNumber == 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -315,7 +319,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
if (legNumber + 2 == corner.getSeqID()) {
|
if (legNumber + 2 == corner.getSeqID()) {
|
||||||
Integer thisCompoundMarkID = corner.getCompoundMarkID();
|
Integer thisCompoundMarkID = corner.getCompoundMarkID();
|
||||||
|
|
||||||
for (Mark mark : StreamParser.getXmlObject().getRaceXML().getAllCompoundMarks()) {
|
for (Mark mark : ClientPacketParser.getXmlObject().getRaceXML()
|
||||||
|
.getAllCompoundMarks()) {
|
||||||
if (mark.getCompoundMarkID() == thisCompoundMarkID) {
|
if (mark.getCompoundMarkID() == thisCompoundMarkID) {
|
||||||
return mark;
|
return mark;
|
||||||
}
|
}
|
||||||
@@ -328,11 +333,12 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the wind direction arrow and text as from info from the StreamParser
|
* Updates the wind direction arrow and text as from info from the ClientPacketParser
|
||||||
*/
|
*/
|
||||||
private void updateWindDirection() {
|
private void updateWindDirection() {
|
||||||
windDirectionText.setText(String.format("%.1f°", StreamParser.getWindDirection()));
|
windDirectionText.setText(String.format("%.1f°", ClientPacketParser.getWindDirection()));
|
||||||
windArrowText.setRotate(StreamParser.getWindDirection());
|
windArrowText.setRotate(ClientPacketParser.getWindDirection());
|
||||||
|
windSpeedText.setText(String.format("%.1f Knots", ClientPacketParser.getWindSpeed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -340,7 +346,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
* Updates the clock for the race
|
* Updates the clock for the race
|
||||||
*/
|
*/
|
||||||
private void updateRaceTime() {
|
private void updateRaceTime() {
|
||||||
if (StreamParser.isRaceFinished()) {
|
if (ClientPacketParser.isRaceFinished()) {
|
||||||
timerLabel.setFill(Color.RED);
|
timerLabel.setFill(Color.RED);
|
||||||
timerLabel.setText("Race Finished!");
|
timerLabel.setText("Race Finished!");
|
||||||
} else {
|
} else {
|
||||||
@@ -350,18 +356,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grabs the boats currently in the race as from the StreamParser and sets them to be selectable
|
* Grabs the boats currently in the race as from the ClientPacketParser and sets them to be selectable
|
||||||
* in the boat selection combo box
|
* in the boat selection combo box
|
||||||
*/
|
*/
|
||||||
private void updateBoatSelectionComboBox() {
|
private void updateBoatSelectionComboBox() {
|
||||||
ObservableList<Yacht> observableBoats = FXCollections
|
ObservableList<Yacht> observableBoats = FXCollections
|
||||||
.observableArrayList(StreamParser.getBoatsPos().values());
|
.observableArrayList(ClientPacketParser.getBoatsPos().values());
|
||||||
boatSelectionComboBox.setItems(observableBoats);
|
boatSelectionComboBox.setItems(observableBoats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the order of the boats as from the StreamParser and sets them in the boat order
|
* Updates the order of the boats as from the ClientPacketParser and sets them in the boat order
|
||||||
* section
|
* section
|
||||||
*/
|
*/
|
||||||
private void updateOrder() {
|
private void updateOrder() {
|
||||||
@@ -370,17 +376,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
|
||||||
// list of racing boat id
|
// list of racing boat id
|
||||||
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
|
ArrayList<Participant> participants = ClientPacketParser.getXmlObject().getRaceXML()
|
||||||
.getParticipants();
|
.getParticipants();
|
||||||
ArrayList<Integer> participantIDs = new ArrayList<>();
|
ArrayList<Integer> participantIDs = new ArrayList<>();
|
||||||
for (Participant p : participants) {
|
for (Participant p : participants) {
|
||||||
participantIDs.add(p.getsourceID());
|
participantIDs.add(p.getsourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StreamParser.isRaceStarted()) {
|
if (ClientPacketParser.isRaceStarted()) {
|
||||||
for (Yacht boat : StreamParser.getBoatsPos().values()) {
|
/*
|
||||||
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
|
for (Yacht boat : ClientPacketParser.getBoatsPos().values()) {
|
||||||
if (boat.getBoatStatus() == 3) { // 3 is finish status
|
System.out.println("Hi tjere" + boat.getBoatName());
|
||||||
|
if (participantIDs.contains(boat.getSourceId()) || true
|
||||||
|
) { // check if the boat is racing
|
||||||
|
if (boat.getBoatStatus() == 69) { // 3 is finish status
|
||||||
Text textToAdd = new Text(boat.getPosition() + ". " +
|
Text textToAdd = new Text(boat.getPosition() + ". " +
|
||||||
boat.getShortName() + " (Finished)");
|
boat.getShortName() + " (Finished)");
|
||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
@@ -392,12 +401,20 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
textToAdd.setStyle("");
|
textToAdd.setStyle("");
|
||||||
positionVbox.getChildren().add(textToAdd);
|
positionVbox.getChildren().add(textToAdd);
|
||||||
|
System.out.println("Adding " + textToAdd.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
for (Yacht boat : ClientPacketParser.getBoats().values()){
|
||||||
|
Text textToAdd = new Text(boat.getSourceId() + ". " + boat.getShortName() + " ");
|
||||||
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
|
textToAdd.setStyle("");
|
||||||
|
positionVbox.getChildren().add(textToAdd);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (Yacht boat : StreamParser.getBoats().values()) {
|
for (Yacht boat : ClientPacketParser.getBoats().values()) {
|
||||||
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
|
if (participantIDs.contains(boat.getSourceId())) { // check if the boat is racing
|
||||||
Text textToAdd = new Text(boat.getPosition() + ". " +
|
Text textToAdd = new Text(boat.getPosition() + ". " +
|
||||||
boat.getShortName() + " ");
|
boat.getShortName() + " ");
|
||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
@@ -434,9 +451,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
|
Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
|
||||||
HashMap<Double, Double> angleAndSpeed;
|
HashMap<Double, Double> angleAndSpeed;
|
||||||
if (isUpwind) {
|
if (isUpwind) {
|
||||||
angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed());
|
angleAndSpeed = PolarTable
|
||||||
|
.getOptimalUpwindVMG(ClientPacketParser.getWindSpeed());
|
||||||
} else {
|
} else {
|
||||||
angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed());
|
angleAndSpeed = PolarTable
|
||||||
|
.getOptimalDownwindVMG(ClientPacketParser.getWindSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
Double resultingAngle = angleAndSpeed.keySet().iterator().next();
|
Double resultingAngle = angleAndSpeed.keySet().iterator().next();
|
||||||
@@ -444,15 +463,23 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
|
Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
|
||||||
Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
|
Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
|
||||||
Integer lineFuncResult = GeometryUtils.lineFunction(boatCurrentPos, gateMidPoint, markPoint2);
|
Integer lineFuncResult = GeoUtility.lineFunction(boatCurrentPos, gateMidPoint, markPoint2);
|
||||||
Line rightLayline = new Line();
|
Line rightLayline = new Line();
|
||||||
Line leftLayline = new Line();
|
Line leftLayline = new Line();
|
||||||
if (lineFuncResult == 1) {
|
if (lineFuncResult == 1) {
|
||||||
rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
|
rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle,
|
||||||
leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
|
ClientPacketParser
|
||||||
|
.getWindDirection());
|
||||||
|
leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle,
|
||||||
|
ClientPacketParser
|
||||||
|
.getWindDirection());
|
||||||
} else if (lineFuncResult == -1) {
|
} else if (lineFuncResult == -1) {
|
||||||
rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
|
rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle,
|
||||||
leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
|
ClientPacketParser
|
||||||
|
.getWindDirection());
|
||||||
|
leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle,
|
||||||
|
ClientPacketParser
|
||||||
|
.getWindDirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
leftLayline.setStrokeWidth(0.5);
|
leftLayline.setStrokeWidth(0.5);
|
||||||
@@ -548,16 +575,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
private String getTimeSinceStartOfRace() {
|
private String getTimeSinceStartOfRace() {
|
||||||
String timerString = "0:00";
|
String timerString = "0:00";
|
||||||
if (StreamParser.getTimeSinceStart() > 0) {
|
if (ClientPacketParser.getTimeSinceStart() > 0) {
|
||||||
String timerMinute = Long.toString(StreamParser.getTimeSinceStart() / 60);
|
String timerMinute = Long.toString(ClientPacketParser.getTimeSinceStart() / 60);
|
||||||
String timerSecond = Long.toString(StreamParser.getTimeSinceStart() % 60);
|
String timerSecond = Long.toString(ClientPacketParser.getTimeSinceStart() % 60);
|
||||||
if (timerSecond.length() == 1) {
|
if (timerSecond.length() == 1) {
|
||||||
timerSecond = "0" + timerSecond;
|
timerSecond = "0" + timerSecond;
|
||||||
}
|
}
|
||||||
timerString = "-" + timerMinute + ":" + timerSecond;
|
timerString = "-" + timerMinute + ":" + timerSecond;
|
||||||
} else {
|
} else {
|
||||||
String timerMinute = Long.toString(-1 * StreamParser.getTimeSinceStart() / 60);
|
String timerMinute = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() / 60);
|
||||||
String timerSecond = Long.toString(-1 * StreamParser.getTimeSinceStart() % 60);
|
String timerSecond = Long.toString(-1 * ClientPacketParser.getTimeSinceStart() % 60);
|
||||||
if (timerSecond.length() == 1) {
|
if (timerSecond.length() == 1) {
|
||||||
timerSecond = "0" + timerSecond;
|
timerSecond = "0" + timerSecond;
|
||||||
}
|
}
|
||||||
@@ -571,76 +598,31 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
return displayFps;
|
return displayFps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the important annotations for a specific BoatGroup
|
|
||||||
* @param bg The boat group to set the annotations for
|
|
||||||
*/
|
|
||||||
private void setBoatGroupImportantAnnotations(BoatGroup bg) {
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.NAME)) {
|
|
||||||
bg.setTeamNameObjectVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setTeamNameObjectVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.SPEED)) {
|
|
||||||
bg.setVelocityObjectVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setVelocityObjectVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.TRACK)) {
|
|
||||||
bg.setLineGroupVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setLineGroupVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.WAKE)) {
|
|
||||||
bg.setWakeVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setWakeVisible(false);
|
|
||||||
}
|
|
||||||
//TODO fix boat annotations with new boatgroup
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK)) {
|
|
||||||
bg.setEstTimeToNextMarkObjectVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setEstTimeToNextMarkObjectVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importantAnnotations.getAnnotationState(Annotation.LEGTIME)) {
|
|
||||||
bg.setLegTimeObjectVisible(true);
|
|
||||||
} else {
|
|
||||||
bg.setLegTimeObjectVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAnnotations(Integer annotationLevel) {
|
private void setAnnotations(Integer annotationLevel) {
|
||||||
switch (annotationLevel) {
|
switch (annotationLevel) {
|
||||||
// No Annotations
|
// No Annotations
|
||||||
case 0:
|
case 0:
|
||||||
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
||||||
bg.setTeamNameObjectVisible(false);
|
bg.setVisibility(false, false, false, false, false, false);
|
||||||
bg.setVelocityObjectVisible(false);
|
|
||||||
bg.setEstTimeToNextMarkObjectVisible(false);
|
|
||||||
bg.setLegTimeObjectVisible(false);
|
|
||||||
bg.setLineGroupVisible(false);
|
|
||||||
bg.setWakeVisible(false);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Important Annotations
|
// Important Annotations
|
||||||
case 1:
|
case 1:
|
||||||
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
||||||
setBoatGroupImportantAnnotations(bg);
|
bg.setVisibility(
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.NAME),
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.SPEED),
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.LEGTIME),
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.TRACK),
|
||||||
|
importantAnnotations.getAnnotationState(Annotation.WAKE)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// All Annotations
|
// All Annotations
|
||||||
case 2:
|
case 2:
|
||||||
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
|
||||||
bg.setTeamNameObjectVisible(true);
|
bg.setVisibility(true, true, true, true, true, true);
|
||||||
bg.setVelocityObjectVisible(true);
|
|
||||||
bg.setEstTimeToNextMarkObjectVisible(true);
|
|
||||||
bg.setLegTimeObjectVisible(true);
|
|
||||||
bg.setLineGroupVisible(true);
|
|
||||||
bg.setWakeVisible(true);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -682,4 +664,5 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
public static boolean sparkLineStatus(Integer yachtId) {
|
public static boolean sparkLineStatus(Integer yachtId) {
|
||||||
return sparkLineData.containsKey(yachtId);
|
return sparkLineData.containsKey(yachtId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,188 +1,164 @@
|
|||||||
package seng302.controllers;
|
package seng302.controllers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.net.Inet4Address;
|
||||||
import java.net.URL;
|
import java.net.NetworkInterface;
|
||||||
import java.util.ArrayList;
|
import java.util.Enumeration;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Alert.AlertType;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.control.TableColumn;
|
|
||||||
import javafx.scene.control.TableView;
|
|
||||||
import javafx.scene.control.cell.PropertyValueFactory;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.paint.Color;
|
import seng302.client.ClientState;
|
||||||
import seng302.models.Yacht;
|
import seng302.client.ClientToServerThread;
|
||||||
import seng302.models.stream.StreamParser;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.gameServer.MainServerThread;
|
||||||
|
|
||||||
public class StartScreenController implements Initializable {
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Class describing the actions of the start screen controller
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class StartScreenController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private GridPane gridPane;
|
private TextField ipTextField;
|
||||||
@FXML
|
@FXML
|
||||||
private Label timeTillLive;
|
private TextField portTextField;
|
||||||
@FXML
|
@FXML
|
||||||
private Button streamButton;
|
private GridPane startScreen2;
|
||||||
@FXML
|
|
||||||
private Button switchToRaceViewButton;
|
|
||||||
@FXML
|
|
||||||
private TableView<Yacht> teamList;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> boatNameCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> shortNameCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> countryCol;
|
|
||||||
@FXML
|
|
||||||
private TableColumn<Yacht, String> posCol;
|
|
||||||
@FXML
|
|
||||||
private Label realTime;
|
|
||||||
|
|
||||||
private boolean switchedToRaceView = false;
|
private Controller controller;
|
||||||
|
|
||||||
private void setContentPane(String jfxUrl) {
|
/**
|
||||||
|
* Loads the fxml content into the parent pane
|
||||||
|
* @param jfxUrl
|
||||||
|
* @return the controller of the fxml
|
||||||
|
*/
|
||||||
|
private Object setContentPane(String jfxUrl) {
|
||||||
try {
|
try {
|
||||||
// get the main controller anchor pane (MainView.fxml)
|
AnchorPane contentPane = (AnchorPane) startScreen2.getParent();
|
||||||
AnchorPane contentPane = (AnchorPane) gridPane.getParent();
|
|
||||||
contentPane.getChildren().removeAll();
|
contentPane.getChildren().removeAll();
|
||||||
contentPane.getChildren().clear();
|
contentPane.getChildren().clear();
|
||||||
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
contentPane.getChildren()
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(jfxUrl));
|
||||||
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
contentPane.getChildren().addAll((Pane) fxmlLoader.load());
|
||||||
|
|
||||||
|
return fxmlLoader.getController();
|
||||||
} catch (javafx.fxml.LoadException e) {
|
} catch (javafx.fxml.LoadException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[Controller] FXML load exception");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[Controller] IO exception");
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
/**
|
||||||
gridPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
* ATTEMPTS TO:
|
||||||
teamList.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
* Sets up a new game state with your IP address as designated as the host.
|
||||||
|
* Starts a thread to listen for incoming connections.
|
||||||
|
* Starts a client to server thread and connects to own ip.
|
||||||
|
* Switches to the lobby screen
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
public void hostButtonPressed() {
|
||||||
|
try {
|
||||||
|
String ipAddress = InetAddress.getLocalHost().getHostAddress();
|
||||||
|
// get the lobby controller so that we can pass the game server thread to it
|
||||||
|
new GameState(getLocalHostIp());
|
||||||
|
MainServerThread mainServerThread = new MainServerThread();
|
||||||
|
ClientState.setHost(true);
|
||||||
|
// host will connect and handshake to itself after setting up the server
|
||||||
|
// TODO: 24/07/17 wmu16 - Make port number some static global type constant?
|
||||||
|
ClientToServerThread clientToServerThread = new ClientToServerThread(ClientState.getHostIp(), 4942);
|
||||||
|
ClientState.setConnectedToHost(true);
|
||||||
|
controller.setClientToServerThread(clientToServerThread);
|
||||||
|
LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
|
||||||
|
lobbyController.setMainServerThread(mainServerThread);
|
||||||
|
lobbyController.setController(controller);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Alert alert = new Alert(AlertType.ERROR);
|
||||||
|
alert.setHeaderText("Cannot host");
|
||||||
|
alert.setContentText("Oops, failed to host, try to restart.");
|
||||||
|
alert.showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Running a timer to update the livestream status on welcome screen. Update interval is 1
|
* ATTEMPTS TO:
|
||||||
* second.
|
* Connect to an ip address and port using the ip and port specified on start screen.
|
||||||
|
* Starts a Client To Server Thread to maintain connection to host.
|
||||||
|
* Switch view to lobby view.
|
||||||
*/
|
*/
|
||||||
public void startStream() {
|
@FXML
|
||||||
if (StreamParser.isStreamStatus()) {
|
public void connectButtonPressed() {
|
||||||
streamButton.setVisible(false);
|
// TODO: 10/07/17 wmu16 - Finish function
|
||||||
realTime.setVisible(true);
|
try {
|
||||||
timeTillLive.setVisible(true);
|
String ipAddress = ipTextField.getText().trim().toLowerCase();
|
||||||
timeTillLive.setTextFill(Color.GREEN);
|
Integer port = Integer.valueOf(portTextField.getText().trim());
|
||||||
timeTillLive.setText("Connecting...");
|
|
||||||
Timer timer = new Timer();
|
ClientToServerThread clientToServerThread = new ClientToServerThread(ipAddress, port);
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
ClientState.setHost(false);
|
||||||
@Override
|
ClientState.setConnectedToHost(true);
|
||||||
public void run() {
|
|
||||||
Platform.runLater(() -> {
|
ClientState.setHostIp(ipAddress);
|
||||||
if (StreamParser.isRaceStarted()) {
|
controller.setClientToServerThread(clientToServerThread);
|
||||||
if (!switchedToRaceView) {
|
LobbyController lobbyController = (LobbyController) setContentPane("/views/LobbyView.fxml");
|
||||||
switchToRaceView();
|
lobbyController.setController(controller);
|
||||||
}
|
} catch (Exception e) {
|
||||||
timer.cancel();
|
Alert alert = new Alert(AlertType.ERROR);
|
||||||
}
|
alert.setHeaderText("Cannot reach the host");
|
||||||
if (StreamParser.isRaceFinished()) {
|
alert.setContentText("Please check your host IP address.");
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
alert.showAndWait();
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
timeTillLive.setText("Race finished! Waiting for new race...");
|
|
||||||
switchToRaceViewButton.setDisable(true);
|
|
||||||
} else if (StreamParser.getTimeSinceStart() > 0) {
|
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
|
||||||
updateTeamList();
|
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
switchToRaceViewButton.setDisable(false);
|
|
||||||
String timerMinute = Long
|
|
||||||
.toString(StreamParser.getTimeSinceStart() / 60);
|
|
||||||
String timerSecond = Long
|
|
||||||
.toString(StreamParser.getTimeSinceStart() % 60);
|
|
||||||
if (timerSecond.length() == 1) {
|
|
||||||
timerSecond = "0" + timerSecond;
|
|
||||||
}
|
|
||||||
String timerString = "-" + timerMinute + ":" + timerSecond;
|
|
||||||
timeTillLive.setText(timerString);
|
|
||||||
} else {
|
|
||||||
realTime.setText(StreamParser.getCurrentTimeString());
|
|
||||||
updateTeamList();
|
|
||||||
timeTillLive.setTextFill(Color.BLACK);
|
|
||||||
switchToRaceViewButton.setDisable(false);
|
|
||||||
String timerMinute = Long
|
|
||||||
.toString(-1 * StreamParser.getTimeSinceStart() / 60);
|
|
||||||
String timerSecond = Long
|
|
||||||
.toString(-1 * StreamParser.getTimeSinceStart() % 60);
|
|
||||||
if (timerSecond.length() == 1) {
|
|
||||||
timerSecond = "0" + timerSecond;
|
|
||||||
}
|
|
||||||
String timerString = timerMinute + ":" + timerSecond;
|
|
||||||
timeTillLive.setText(timerString);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 0, 1000);
|
|
||||||
} else {
|
|
||||||
timeTillLive.setText("Stream not available.");
|
|
||||||
timeTillLive.setTextFill(Color.RED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToRaceView() {
|
public void setController(Controller controller) {
|
||||||
StreamParser.boatLocations.clear();
|
this.controller = controller;
|
||||||
switchedToRaceView = true;
|
|
||||||
setContentPane("/views/RaceView.fxml");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTeamList() {
|
/**
|
||||||
ObservableList<Yacht> data = FXCollections.observableArrayList();
|
* Gets the local host ip address and sets this ip to ClientState.
|
||||||
|
* Only runs by the host.
|
||||||
|
*
|
||||||
|
* @return the localhost ip address
|
||||||
|
*/
|
||||||
|
private String getLocalHostIp() {
|
||||||
|
String ipAddress = null;
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
NetworkInterface ni = e.nextElement();
|
||||||
|
if (ni.isLoopback())
|
||||||
|
continue;
|
||||||
|
if(ni.isPointToPoint())
|
||||||
|
continue;
|
||||||
|
if(ni.isVirtual())
|
||||||
|
continue;
|
||||||
|
|
||||||
teamList.setItems(data);
|
Enumeration<InetAddress> addresses = ni.getInetAddresses();
|
||||||
|
while(addresses.hasMoreElements()) {
|
||||||
boatNameCol.setCellValueFactory(
|
InetAddress address = addresses.nextElement();
|
||||||
new PropertyValueFactory<>("boatName")
|
if(address instanceof Inet4Address) { // skip all ipv6
|
||||||
);
|
ipAddress = address.getHostAddress();
|
||||||
shortNameCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("shortName")
|
|
||||||
);
|
|
||||||
countryCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("country")
|
|
||||||
);
|
|
||||||
posCol.setCellValueFactory(
|
|
||||||
new PropertyValueFactory<>("position")
|
|
||||||
);
|
|
||||||
|
|
||||||
// check if the boat is racing
|
|
||||||
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
|
|
||||||
.getParticipants();
|
|
||||||
ArrayList<Integer> participantIDs = new ArrayList<>();
|
|
||||||
for (Participant p : participants) {
|
|
||||||
participantIDs.add(p.getsourceID());
|
|
||||||
}
|
|
||||||
|
|
||||||
// add boats to the start screen list
|
|
||||||
if (StreamParser.isRaceStarted()) { // if race is started, use StreamParser.getBoatsPos()
|
|
||||||
for (Yacht boat : StreamParser.getBoatsPos().values()) {
|
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
|
||||||
data.add(boat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // else use StreamParser.getBoats()
|
|
||||||
for (Yacht boat : StreamParser.getBoats().values()) {
|
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
|
||||||
data.add(boat);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
teamList.refresh();
|
} catch (Exception e) {
|
||||||
|
System.out.println("[StartScreenController] Exception");
|
||||||
|
}
|
||||||
|
if (ipAddress == null) {
|
||||||
|
System.out.println("[HOST] Cannot obtain local host ip address.");
|
||||||
|
}
|
||||||
|
ClientState.setHostIp(ipAddress);
|
||||||
|
return ipAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,8 @@ import javafx.scene.control.Button;
|
|||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import seng302.controllers.RaceViewController;
|
|
||||||
import seng302.controllers.annotations.Annotation;
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class ImportantAnnotationController implements Initializable {
|
public class ImportantAnnotationController implements Initializable {
|
||||||
@@ -139,7 +135,11 @@ public class ImportantAnnotationController implements Initializable {
|
|||||||
boatEstTimeToNextMarkSelect.isSelected()));
|
boatEstTimeToNextMarkSelect.isSelected()));
|
||||||
boatElapsedTimeSelect.setOnAction(
|
boatElapsedTimeSelect.setOnAction(
|
||||||
event -> setAnnotation(Annotation.LEGTIME, boatElapsedTimeSelect.isSelected()));
|
event -> setAnnotation(Annotation.LEGTIME, boatElapsedTimeSelect.isSelected()));
|
||||||
|
// TODO: 26/07/17 cir27 - Create a more robust fix for this when the annotation for the game are decided upon.
|
||||||
|
boatEstTimeToNextMarkSelect.setVisible(false);
|
||||||
|
boatEstTimeToNextMarkSelect.setDisable(true);
|
||||||
|
boatElapsedTimeSelect.setVisible(false);
|
||||||
|
boatElapsedTimeSelect.setDisable(true);
|
||||||
closeButton.setOnAction(event -> stage.close());
|
closeButton.setOnAction(event -> stage.close());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package seng302.fxObjects;
|
||||||
|
|
||||||
|
import javafx.scene.CacheHint;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of annotations for boats.
|
||||||
|
*/
|
||||||
|
public class BoatAnnotations extends Group{
|
||||||
|
|
||||||
|
//Text offset constants
|
||||||
|
private static final double X_OFFSET_TEXT = 18d;
|
||||||
|
private static final double Y_OFFSET_TEXT_INIT = -29d;
|
||||||
|
private static final double Y_OFFSET_PER_TEXT = 12d;
|
||||||
|
//Background constants
|
||||||
|
private static final double TEXT_BUFFER = 3;
|
||||||
|
private static final double BACKGROUND_X = X_OFFSET_TEXT - TEXT_BUFFER;
|
||||||
|
private static final double BACKGROUND_Y = Y_OFFSET_TEXT_INIT - TEXT_BUFFER;
|
||||||
|
private static final double BACKGROUND_H_PER_TEXT = 9.5d;
|
||||||
|
private static final double BACKGROUND_W = 125d;
|
||||||
|
private static final double BACKGROUND_ARC_SIZE = 10;
|
||||||
|
|
||||||
|
private Rectangle background = new Rectangle();
|
||||||
|
private Text teamNameObject;
|
||||||
|
private Text velocityObject;
|
||||||
|
private Text estTimeToNextMarkObject;
|
||||||
|
private Text legTimeObject;
|
||||||
|
private boolean isPlayer = false;
|
||||||
|
private Yacht boat;
|
||||||
|
|
||||||
|
BoatAnnotations (Yacht boat, Color theme) {
|
||||||
|
super.setCache(true);
|
||||||
|
this.boat = boat;
|
||||||
|
background.setX(BACKGROUND_X);
|
||||||
|
background.setY(BACKGROUND_Y);
|
||||||
|
background.setWidth(BACKGROUND_W);
|
||||||
|
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * 4);
|
||||||
|
background.setArcHeight(BACKGROUND_ARC_SIZE);
|
||||||
|
background.setArcWidth(BACKGROUND_ARC_SIZE);
|
||||||
|
background.setFill(new Color(1, 1, 1, 0.75));
|
||||||
|
background.setStroke(theme);
|
||||||
|
background.setStrokeWidth(2);
|
||||||
|
background.setCache(true);
|
||||||
|
background.setCacheHint(CacheHint.SPEED);
|
||||||
|
|
||||||
|
teamNameObject = getTextObject(boat.getShortName(), theme);
|
||||||
|
teamNameObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT);
|
||||||
|
|
||||||
|
velocityObject = getTextObject("0 m/s", theme);
|
||||||
|
velocityObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 2);
|
||||||
|
velocityObject.setVisible(false);
|
||||||
|
|
||||||
|
estTimeToNextMarkObject = getTextObject("Next mark: ", theme);
|
||||||
|
estTimeToNextMarkObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 3);
|
||||||
|
estTimeToNextMarkObject.setVisible(false);
|
||||||
|
|
||||||
|
legTimeObject = getTextObject("Last mark: -", theme);
|
||||||
|
legTimeObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 4);
|
||||||
|
legTimeObject.setVisible(false);
|
||||||
|
|
||||||
|
this.setVisible(true, false, false, false);
|
||||||
|
|
||||||
|
super.getChildren().addAll(background, teamNameObject, velocityObject, estTimeToNextMarkObject, legTimeObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a text object with caching and a color applied
|
||||||
|
*
|
||||||
|
* @param defaultText The default text to display
|
||||||
|
* @param fill The text fill color
|
||||||
|
* @return The text object
|
||||||
|
*/
|
||||||
|
private Text getTextObject(String defaultText, Color fill) {
|
||||||
|
Text text = new Text(defaultText);
|
||||||
|
text.setFill(fill);
|
||||||
|
text.setStrokeWidth(2);
|
||||||
|
text.setCacheHint(CacheHint.SPEED);
|
||||||
|
text.setCache(true);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update () {
|
||||||
|
teamNameObject.setText("Player: " + boat.getShortName());
|
||||||
|
|
||||||
|
velocityObject.setText(String.format(String.format("%.2f m/s", boat.getVelocityMMS())));
|
||||||
|
|
||||||
|
if (boat.getTimeTillNext() != null) {
|
||||||
|
DateFormat format = new SimpleDateFormat("mm:ss");
|
||||||
|
String timeToNextMark = format
|
||||||
|
.format(boat.getTimeTillNext() - ClientPacketParser.getCurrentTimeLong());
|
||||||
|
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
|
||||||
|
} else {
|
||||||
|
estTimeToNextMarkObject.setText("Next mark: -");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boat.getMarkRoundTime() != null) {
|
||||||
|
DateFormat format = new SimpleDateFormat("mm:ss");
|
||||||
|
String elapsedTime = format
|
||||||
|
.format(ClientPacketParser.getCurrentTimeLong() - boat.getMarkRoundTime());
|
||||||
|
legTimeObject.setText("Last mark: " + elapsedTime);
|
||||||
|
}else {
|
||||||
|
legTimeObject.setText("Last mark: - ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisible(boolean nameVisibility, boolean speedVisibility,
|
||||||
|
boolean estTimeVisibility, boolean lastMarkVisibility) {
|
||||||
|
int totalVisible = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is a temporary fix until the new annotation group is added along with the visualiser
|
||||||
|
overhaul.
|
||||||
|
*/
|
||||||
|
totalVisible = updateVisibility(nameVisibility, teamNameObject, totalVisible);
|
||||||
|
if (isPlayer)
|
||||||
|
totalVisible = updateVisibility(speedVisibility, velocityObject, totalVisible);
|
||||||
|
// totalVisible = updateVisibility(estTimeVisibility, estTimeToNextMarkObject, totalVisible);
|
||||||
|
// totalVisible = updateVisibility(lastMarkVisibility, legTimeObject, totalVisible);
|
||||||
|
if (totalVisible != 0) {
|
||||||
|
background.setVisible(true);
|
||||||
|
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * totalVisible);
|
||||||
|
} else {
|
||||||
|
background.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int updateVisibility (boolean visibility, Text text, int totalVisible) {
|
||||||
|
if (visibility){
|
||||||
|
totalVisible ++;
|
||||||
|
text.setVisible(true);
|
||||||
|
text.setLayoutX(X_OFFSET_TEXT);
|
||||||
|
text.setLayoutY(Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * totalVisible);
|
||||||
|
} else {
|
||||||
|
text.setVisible(false);
|
||||||
|
}
|
||||||
|
return totalVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets these annotations to show more detailed info.
|
||||||
|
*/
|
||||||
|
public void setAsPlayer () {
|
||||||
|
isPlayer = true;
|
||||||
|
velocityObject.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,22 @@
|
|||||||
package seng302.models;
|
package seng302.fxObjects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.CacheHint;
|
import javafx.scene.CacheHint;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.shape.Line;
|
import javafx.scene.shape.Line;
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
import seng302.GeometryUtils;
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.controllers.CanvasController;
|
import seng302.controllers.CanvasController;
|
||||||
import seng302.models.mark.GateMark;
|
import seng302.models.mark.GateMark;
|
||||||
import seng302.models.mark.Mark;
|
import seng302.models.mark.Mark;
|
||||||
import seng302.models.mark.SingleMark;
|
import seng302.models.mark.SingleMark;
|
||||||
import seng302.models.stream.StreamParser;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
|
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
|
||||||
@@ -31,14 +29,6 @@ import java.text.SimpleDateFormat;
|
|||||||
public class BoatGroup extends Group {
|
public class BoatGroup extends Group {
|
||||||
|
|
||||||
//Constants for drawing
|
//Constants for drawing
|
||||||
private static final double TEAMNAME_X_OFFSET = 10d;
|
|
||||||
private static final double TEAMNAME_Y_OFFSET = -29d;
|
|
||||||
private static final double VELOCITY_X_OFFSET = 10d;
|
|
||||||
private static final double VELOCITY_Y_OFFSET = -17d;
|
|
||||||
private static final double ESTTIMETONEXTMARK_X_OFFSET = 10d;
|
|
||||||
private static final double ESTTIMETONEXTMARK_Y_OFFSET = -5d;
|
|
||||||
private static final double LEGTIME_X_OFFSET = 10d;
|
|
||||||
private static final double LEGTIME_Y_OFFSET = 7d;
|
|
||||||
private static final double BOAT_HEIGHT = 15d;
|
private static final double BOAT_HEIGHT = 15d;
|
||||||
private static final double BOAT_WIDTH = 10d;
|
private static final double BOAT_WIDTH = 10d;
|
||||||
//Variables for boat logic.
|
//Variables for boat logic.
|
||||||
@@ -52,19 +42,16 @@ public class BoatGroup extends Group {
|
|||||||
private Yacht boat;
|
private Yacht boat;
|
||||||
private Group lineGroup = new Group();
|
private Group lineGroup = new Group();
|
||||||
private Polygon boatPoly;
|
private Polygon boatPoly;
|
||||||
private Text teamNameObject;
|
|
||||||
private Text velocityObject;
|
|
||||||
private Text estTimeToNextMarkObject;
|
|
||||||
private Text legTimeObject;
|
|
||||||
private Wake wake;
|
private Wake wake;
|
||||||
private Line leftLayLine;
|
private Line leftLayLine;
|
||||||
private Line rightLayline;
|
private Line rightLayline;
|
||||||
private Double distanceTravelled = 0.0;
|
private Double distanceTravelled = 0.0;
|
||||||
private Point2D lastPoint;
|
private Point2D lastPoint;
|
||||||
private boolean destinationSet;
|
private boolean destinationSet;
|
||||||
private Color textColor = Color.RED;
|
private BoatAnnotations boatAnnotations;
|
||||||
|
private Color color;
|
||||||
private Boolean isSelected = true; //All boats are initalised as selected
|
private Boolean isSelected = true; //All boats are initialised as selected\
|
||||||
|
private boolean isPlayer = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a BoatGroup with the default triangular boat polygon.
|
* Creates a BoatGroup with the default triangular boat polygon.
|
||||||
@@ -74,9 +61,9 @@ public class BoatGroup extends Group {
|
|||||||
* @param color The colour of the boat polygon and the trailing line.
|
* @param color The colour of the boat polygon and the trailing line.
|
||||||
*/
|
*/
|
||||||
public BoatGroup(Yacht boat, Color color) {
|
public BoatGroup(Yacht boat, Color color) {
|
||||||
|
destinationSet = false;
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
initChildren(color);
|
initChildren(color);
|
||||||
this.textColor = color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,27 +77,11 @@ public class BoatGroup extends Group {
|
|||||||
* polygon.
|
* polygon.
|
||||||
*/
|
*/
|
||||||
public BoatGroup(Yacht boat, Color color, double... points) {
|
public BoatGroup(Yacht boat, Color color, double... points) {
|
||||||
|
destinationSet = false;
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
initChildren(color, points);
|
initChildren(color, points);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a text object with caching and a color applied
|
|
||||||
*
|
|
||||||
* @param defaultText The default text to display
|
|
||||||
* @param fill The text fill color
|
|
||||||
* @return The text object
|
|
||||||
*/
|
|
||||||
private Text getTextObject(String defaultText, Color fill) {
|
|
||||||
Text text = new Text(defaultText);
|
|
||||||
|
|
||||||
text.setFill(fill);
|
|
||||||
text.setCacheHint(CacheHint.SPEED);
|
|
||||||
text.setCache(true);
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the javafx objects that will be the in the group by default.
|
* Creates the javafx objects that will be the in the group by default.
|
||||||
*
|
*
|
||||||
@@ -119,52 +90,27 @@ public class BoatGroup extends Group {
|
|||||||
* polygon.
|
* polygon.
|
||||||
*/
|
*/
|
||||||
private void initChildren(Color color, double... points) {
|
private void initChildren(Color color, double... points) {
|
||||||
textColor = color;
|
this.color = color;
|
||||||
destinationSet = false;
|
|
||||||
|
|
||||||
boatPoly = new Polygon(points);
|
boatPoly = new Polygon(points);
|
||||||
boatPoly.setFill(color);
|
boatPoly.setFill(this.color);
|
||||||
boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE));
|
boatPoly.setOnMouseEntered(event -> {
|
||||||
boatPoly.setOnMouseExited(event -> boatPoly.setFill(color));
|
boatPoly.setFill(Color.FLORALWHITE);
|
||||||
|
boatPoly.setStroke(Color.RED);
|
||||||
|
});
|
||||||
|
boatPoly.setOnMouseExited(event -> {
|
||||||
|
boatPoly.setFill(this.color);
|
||||||
|
boatPoly.setStroke(Color.BLACK);
|
||||||
|
});
|
||||||
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
|
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
|
||||||
boatPoly.setCache(true);
|
boatPoly.setCache(true);
|
||||||
boatPoly.setCacheHint(CacheHint.SPEED);
|
boatPoly.setCacheHint(CacheHint.SPEED);
|
||||||
|
boatAnnotations = new BoatAnnotations(boat, this.color);
|
||||||
teamNameObject = getTextObject(boat.getShortName(), textColor);
|
|
||||||
velocityObject = getTextObject(boat.getVelocity().toString(), textColor);
|
|
||||||
|
|
||||||
teamNameObject.setX(TEAMNAME_X_OFFSET);
|
|
||||||
teamNameObject.setY(TEAMNAME_Y_OFFSET);
|
|
||||||
teamNameObject.relocate(teamNameObject.getX(), teamNameObject.getY());
|
|
||||||
|
|
||||||
velocityObject.setX(VELOCITY_X_OFFSET);
|
|
||||||
velocityObject.setY(VELOCITY_Y_OFFSET);
|
|
||||||
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
|
|
||||||
|
|
||||||
updateLastMarkRoundingTime();
|
|
||||||
updateTimeTillNextMark();
|
|
||||||
|
|
||||||
if (estTimeToNextMarkObject != null) {
|
|
||||||
estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET);
|
|
||||||
estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET);
|
|
||||||
estTimeToNextMarkObject
|
|
||||||
.relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legTimeObject != null) {
|
|
||||||
legTimeObject.setX(LEGTIME_X_OFFSET);
|
|
||||||
legTimeObject.setY(LEGTIME_Y_OFFSET);
|
|
||||||
legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
leftLayLine = new Line();
|
leftLayLine = new Line();
|
||||||
rightLayline = new Line();
|
rightLayline = new Line();
|
||||||
|
|
||||||
wake = new Wake(0, -BOAT_HEIGHT);
|
wake = new Wake(0, -BOAT_HEIGHT);
|
||||||
super.getChildren()
|
super.getChildren().addAll(boatPoly, boatAnnotations);
|
||||||
.addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject,
|
|
||||||
legTimeObject, leftLayLine, rightLayline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,14 +135,8 @@ public class BoatGroup extends Group {
|
|||||||
private void moveGroupBy(double dx, double dy) {
|
private void moveGroupBy(double dx, double dy) {
|
||||||
boatPoly.setLayoutX(boatPoly.getLayoutX() + dx);
|
boatPoly.setLayoutX(boatPoly.getLayoutX() + dx);
|
||||||
boatPoly.setLayoutY(boatPoly.getLayoutY() + dy);
|
boatPoly.setLayoutY(boatPoly.getLayoutY() + dy);
|
||||||
teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx);
|
boatAnnotations.setLayoutX(boatAnnotations.getLayoutX() + dx);
|
||||||
teamNameObject.setLayoutY(teamNameObject.getLayoutY() + dy);
|
boatAnnotations.setLayoutY(boatAnnotations.getLayoutY() + dy);
|
||||||
velocityObject.setLayoutX(velocityObject.getLayoutX() + dx);
|
|
||||||
velocityObject.setLayoutY(velocityObject.getLayoutY() + dy);
|
|
||||||
estTimeToNextMarkObject.setLayoutX(estTimeToNextMarkObject.getLayoutX() + dx);
|
|
||||||
estTimeToNextMarkObject.setLayoutY(estTimeToNextMarkObject.getLayoutY() + dy);
|
|
||||||
legTimeObject.setLayoutX(legTimeObject.getLayoutX() + dx);
|
|
||||||
legTimeObject.setLayoutY(legTimeObject.getLayoutY() + dy);
|
|
||||||
wake.setLayoutX(wake.getLayoutX() + dx);
|
wake.setLayoutX(wake.getLayoutX() + dx);
|
||||||
wake.setLayoutY(wake.getLayoutY() + dy);
|
wake.setLayoutY(wake.getLayoutY() + dy);
|
||||||
}
|
}
|
||||||
@@ -212,14 +152,8 @@ public class BoatGroup extends Group {
|
|||||||
rotateTo(rotation);
|
rotateTo(rotation);
|
||||||
boatPoly.setLayoutX(x);
|
boatPoly.setLayoutX(x);
|
||||||
boatPoly.setLayoutY(y);
|
boatPoly.setLayoutY(y);
|
||||||
teamNameObject.setLayoutX(x);
|
boatAnnotations.setLayoutX(x);
|
||||||
teamNameObject.setLayoutY(y);
|
boatAnnotations.setLayoutY(y);
|
||||||
velocityObject.setLayoutX(x);
|
|
||||||
velocityObject.setLayoutY(y);
|
|
||||||
estTimeToNextMarkObject.setLayoutX(x);
|
|
||||||
estTimeToNextMarkObject.setLayoutY(y);
|
|
||||||
legTimeObject.setLayoutX(x);
|
|
||||||
legTimeObject.setLayoutY(y);
|
|
||||||
wake.setLayoutX(x);
|
wake.setLayoutX(x);
|
||||||
wake.setLayoutY(y);
|
wake.setLayoutY(y);
|
||||||
wake.rotate(rotation);
|
wake.rotate(rotation);
|
||||||
@@ -230,54 +164,32 @@ public class BoatGroup extends Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the time until next mark label, will create a label if one doesn't exist
|
* Sets the destination of the boat and the headng it should have once it reaches
|
||||||
|
*
|
||||||
|
* @param newXValue The X co-ordinate the boat needs to move to.
|
||||||
|
* @param newYValue The Y co-ordinate the boat needs to move to.
|
||||||
|
* @param rotation Rotation to move graphics to.
|
||||||
|
* @param timeValid the time the position values are valid for
|
||||||
*/
|
*/
|
||||||
private void updateTimeTillNextMark() {
|
public void setDestination(double newXValue, double newYValue, double rotation,
|
||||||
if (estTimeToNextMarkObject == null) {
|
double groundSpeed, long timeValid, double frameRate) {
|
||||||
estTimeToNextMarkObject = getTextObject("Next mark: -", textColor);
|
|
||||||
}
|
|
||||||
if (boat.getEstimateTimeAtNextMark() != null) {
|
|
||||||
DateFormat format = new SimpleDateFormat("mm:ss");
|
|
||||||
String timeToNextMark = format
|
|
||||||
.format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong());
|
|
||||||
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
|
|
||||||
} else {
|
|
||||||
estTimeToNextMarkObject.setText("Next mark: -");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
destinationSet = true;
|
||||||
* Updates the time since last mark rounding, will create a label if one doesn't exist
|
Double dx = Math.abs(boatPoly.getLayoutX() - newXValue);
|
||||||
*/
|
Double dy = Math.abs(boatPoly.getLayoutY() - newYValue);
|
||||||
private void updateLastMarkRoundingTime() {
|
moveTo(newXValue, newYValue, rotation);
|
||||||
if (legTimeObject == null) {
|
|
||||||
legTimeObject = getTextObject("Last mark: -", textColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (boat.getMarkRoundingTime() != null) {
|
|
||||||
DateFormat format = new SimpleDateFormat("mm:ss");
|
|
||||||
String elapsedTime = format
|
|
||||||
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime());
|
|
||||||
legTimeObject.setText("Last mark: " + elapsedTime);
|
|
||||||
} else {
|
|
||||||
legTimeObject.setText("Last mark: -");
|
|
||||||
|
|
||||||
}
|
rotateTo(rotation);
|
||||||
}
|
wake.setRotation(rotation, groundSpeed);
|
||||||
|
boat.setVelocity(groundSpeed);
|
||||||
|
isStopped = false;
|
||||||
|
lastRotation = rotation;
|
||||||
|
boatAnnotations.update();
|
||||||
|
|
||||||
public void move() {
|
distanceTravelled += Math.sqrt((dx * dx) + (dy * dy));
|
||||||
double dx = xIncrement * framesToMove;
|
|
||||||
double dy = yIncrement * framesToMove;
|
|
||||||
|
|
||||||
distanceTravelled += Math.abs(dx) + Math.abs(dy);
|
if (distanceTravelled > 10 && isPlayer) {
|
||||||
moveGroupBy(xIncrement, yIncrement);
|
|
||||||
framesToMove = framesToMove - 1;
|
|
||||||
|
|
||||||
if (framesToMove <= 0) {
|
|
||||||
isStopped = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distanceTravelled > 70) {
|
|
||||||
distanceTravelled = 0d;
|
distanceTravelled = 0d;
|
||||||
|
|
||||||
if (lastPoint != null) {
|
if (lastPoint != null) {
|
||||||
@@ -293,81 +205,10 @@ public class BoatGroup extends Group {
|
|||||||
l.setCacheHint(CacheHint.SPEED);
|
l.setCacheHint(CacheHint.SPEED);
|
||||||
lineGroup.getChildren().add(l);
|
lineGroup.getChildren().add(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destinationSet) {
|
if (destinationSet) {
|
||||||
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
|
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wake.updatePosition(1000 / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the rotational velocity required to reach the rotationalGoal from the
|
|
||||||
* currentRotation.
|
|
||||||
*/
|
|
||||||
protected Double calculateRotationalVelocity(Double rotationalGoal) {
|
|
||||||
Double rotationalVelocity = 0.0;
|
|
||||||
|
|
||||||
if (Math.abs(rotationalGoal - lastRotation) > 180) {
|
|
||||||
if (rotationalGoal - lastRotation >= 0.0) {
|
|
||||||
rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200;
|
|
||||||
} else {
|
|
||||||
rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rotationalVelocity = (rotationalGoal - lastRotation) / 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sometimes the rotation is too large to be realistic. In that case just do it instantly.
|
|
||||||
if (Math.abs(rotationalVelocity) > 1) {
|
|
||||||
rotationalVelocity = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rotationalVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the destination of the boat and the headng it should have once it reaches
|
|
||||||
*
|
|
||||||
* @param newXValue The X co-ordinate the boat needs to move to.
|
|
||||||
* @param newYValue The Y co-ordinate the boat needs to move to.
|
|
||||||
* @param rotation Rotation to move graphics to.
|
|
||||||
* @param timeValid the time the position values are valid for
|
|
||||||
*/
|
|
||||||
public void setDestination(double newXValue, double newYValue, double rotation,
|
|
||||||
double groundSpeed, long timeValid, double frameRate, long id) {
|
|
||||||
if (lastTimeValid == 0) {
|
|
||||||
lastTimeValid = timeValid - 200;
|
|
||||||
moveTo(newXValue, newYValue, rotation);
|
|
||||||
}
|
|
||||||
framesToMove = Math.round((frameRate / (1000.0f / (timeValid - lastTimeValid))));
|
|
||||||
double dx = newXValue - boatPoly.getLayoutX();
|
|
||||||
double dy = newYValue - boatPoly.getLayoutY();
|
|
||||||
|
|
||||||
xIncrement = dx / framesToMove;
|
|
||||||
yIncrement = dy / framesToMove;
|
|
||||||
|
|
||||||
destinationSet = true;
|
|
||||||
|
|
||||||
Double rotationalVelocity = calculateRotationalVelocity(rotation);
|
|
||||||
|
|
||||||
updateTimeTillNextMark();
|
|
||||||
updateLastMarkRoundingTime();
|
|
||||||
|
|
||||||
if (Math.abs(rotationalVelocity) > 0.075) {
|
|
||||||
rotationalVelocity = 0.0;
|
|
||||||
wake.rotate(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
rotateTo(rotation);
|
|
||||||
wake.setRotationalVelocity(rotationalVelocity, groundSpeed);
|
|
||||||
|
|
||||||
velocityObject.setText(String.format("%.2f m/s", groundSpeed));
|
|
||||||
lastTimeValid = timeValid;
|
|
||||||
isStopped = false;
|
|
||||||
|
|
||||||
lastRotation = rotation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -380,7 +221,7 @@ public class BoatGroup extends Group {
|
|||||||
*/
|
*/
|
||||||
public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) {
|
public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) {
|
||||||
|
|
||||||
Double windAngle = StreamParser.getWindDirection();
|
Double windAngle = ClientPacketParser.getWindDirection();
|
||||||
GateMark thisGateMark = (GateMark) nextMark;
|
GateMark thisGateMark = (GateMark) nextMark;
|
||||||
SingleMark nextMark1 = thisGateMark.getSingleMark1();
|
SingleMark nextMark1 = thisGateMark.getSingleMark1();
|
||||||
SingleMark nextMark2 = thisGateMark.getSingleMark2();
|
SingleMark nextMark2 = thisGateMark.getSingleMark2();
|
||||||
@@ -388,11 +229,11 @@ public class BoatGroup extends Group {
|
|||||||
Point2D nextMarkPoint2 = canvasController.findScaledXY(nextMark2.getLatitude(), nextMark2.getLongitude());
|
Point2D nextMarkPoint2 = canvasController.findScaledXY(nextMark2.getLatitude(), nextMark2.getLongitude());
|
||||||
|
|
||||||
Point2D boatCurrentPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
|
Point2D boatCurrentPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
|
||||||
Point2D windTestPoint = GeometryUtils.makeArbitraryVectorPoint(nextMarkPoint1, windAngle, 10d);
|
Point2D windTestPoint = GeoUtility.makeArbitraryVectorPoint(nextMarkPoint1, windAngle, 10d);
|
||||||
|
|
||||||
|
|
||||||
Integer boatLineFuncResult = GeometryUtils.lineFunction(nextMarkPoint1, nextMarkPoint2, boatCurrentPoint);
|
Integer boatLineFuncResult = GeoUtility.lineFunction(nextMarkPoint1, nextMarkPoint2, boatCurrentPoint);
|
||||||
Integer windLineFuncResult = GeometryUtils.lineFunction(nextMarkPoint1, nextMarkPoint2, windTestPoint);
|
Integer windLineFuncResult = GeoUtility.lineFunction(nextMarkPoint1, nextMarkPoint2, windTestPoint);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -411,30 +252,16 @@ public class BoatGroup extends Group {
|
|||||||
|
|
||||||
public void setIsSelected(Boolean isSelected) {
|
public void setIsSelected(Boolean isSelected) {
|
||||||
this.isSelected = isSelected;
|
this.isSelected = isSelected;
|
||||||
setTeamNameObjectVisible(isSelected);
|
|
||||||
setVelocityObjectVisible(isSelected);
|
|
||||||
setLineGroupVisible(isSelected);
|
setLineGroupVisible(isSelected);
|
||||||
setWakeVisible(isSelected);
|
setWakeVisible(isSelected);
|
||||||
setEstTimeToNextMarkObjectVisible(isSelected);
|
boatAnnotations.setVisible(isSelected);
|
||||||
setLegTimeObjectVisible(isSelected);
|
|
||||||
setLayLinesVisible(isSelected);
|
setLayLinesVisible(isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) {
|
||||||
public void setTeamNameObjectVisible(Boolean visible) {
|
boatAnnotations.setVisible(teamName, velocity, estTime, legTime);
|
||||||
teamNameObject.setVisible(visible);
|
this.wake.setVisible(wake);
|
||||||
}
|
this.lineGroup.setVisible(trail);
|
||||||
|
|
||||||
public void setVelocityObjectVisible(Boolean visible) {
|
|
||||||
velocityObject.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstTimeToNextMarkObjectVisible(Boolean visible) {
|
|
||||||
estTimeToNextMarkObject.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLegTimeObjectVisible(Boolean visible) {
|
|
||||||
legTimeObject.setVisible(visible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLineGroupVisible(Boolean visible) {
|
public void setLineGroupVisible(Boolean visible) {
|
||||||
@@ -472,22 +299,20 @@ public class BoatGroup extends Group {
|
|||||||
* @return An array containing all ID's associated with this RaceObject.
|
* @return An array containing all ID's associated with this RaceObject.
|
||||||
*/
|
*/
|
||||||
public long getRaceId() {
|
public long getRaceId() {
|
||||||
return boat.getSourceID();
|
return boat.getSourceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Group getWake () {
|
||||||
* Due to javaFX limitations annotations associated with a boat that you want to appear below
|
return wake;
|
||||||
* all boats in the Z-axis need to be pulled out of the BoatGroup and added to the parent group
|
|
||||||
* of the BoatGroups. This function returns these annotations as a group.
|
|
||||||
*
|
|
||||||
* @return A group containing low priority annotations.
|
|
||||||
*/
|
|
||||||
public Group getLowPriorityAnnotations() {
|
|
||||||
Group group = new Group();
|
|
||||||
group.getChildren().addAll(wake, lineGroup);
|
|
||||||
return group;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Group getTrail() {
|
||||||
|
return lineGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Group getAnnotations() {
|
||||||
|
return boatAnnotations;
|
||||||
|
}
|
||||||
|
|
||||||
public Double getBoatLayoutX() {
|
public Double getBoatLayoutX() {
|
||||||
return boatPoly.getLayoutX();
|
return boatPoly.getLayoutX();
|
||||||
@@ -506,4 +331,19 @@ public class BoatGroup extends Group {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return boat.toString();
|
return boat.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this boat to appear highlighted
|
||||||
|
*/
|
||||||
|
public void setAsPlayer() {
|
||||||
|
boatPoly.getPoints().setAll(
|
||||||
|
-BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75,
|
||||||
|
0.0, -BOAT_HEIGHT / 1.75,
|
||||||
|
BOAT_WIDTH / 1.75, BOAT_HEIGHT / 1.75
|
||||||
|
);
|
||||||
|
boatPoly.setStroke(Color.BLACK);
|
||||||
|
boatPoly.setStrokeWidth(3);
|
||||||
|
boatAnnotations.setAsPlayer();
|
||||||
|
isPlayer = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package seng302.models.mark;
|
package seng302.fxObjects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -8,7 +8,10 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.shape.Line;
|
import javafx.scene.shape.Line;
|
||||||
import seng302.GeometryUtils;
|
import seng302.models.mark.GateMark;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.models.mark.MarkType;
|
||||||
|
import seng302.models.mark.SingleMark;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grouping of javaFX objects needed to represent a Mark on screen.
|
* Grouping of javaFX objects needed to represent a Mark on screen.
|
||||||
@@ -109,30 +112,30 @@ public class MarkGroup extends Group {
|
|||||||
}
|
}
|
||||||
super.getChildren().add(line);
|
super.getChildren().add(line);
|
||||||
|
|
||||||
//Laylines
|
|
||||||
// if (mark.)
|
|
||||||
|
|
||||||
// addLayLine(points1, 12.0, 90.0);
|
|
||||||
// addLayLine(points2, 12.0, 90.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveMarkTo (double x, double y, long raceId)
|
public void moveMarkTo (double x, double y, long raceId)
|
||||||
{
|
{
|
||||||
if (mainMark.getMarkType() == MarkType.SINGLE_MARK) {
|
if (mainMark.getMarkType() == MarkType.SINGLE_MARK) {
|
||||||
Circle markCircle = (Circle) super.getChildren().get(0);
|
Circle markCircle = (Circle) super.getChildren().get(0);
|
||||||
|
//One of the test streams produced frequent, jittery movements. Added this as a fix.
|
||||||
|
if (Math.abs(markCircle.getCenterX() - x) > 5 || Math.abs(markCircle.getCenterY() - y) > 5) {
|
||||||
markCircle.setCenterX(x);
|
markCircle.setCenterX(x);
|
||||||
markCircle.setCenterY(y);
|
markCircle.setCenterY(y);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Circle markCircle1 = (Circle) super.getChildren().get(0);
|
Circle markCircle1 = (Circle) super.getChildren().get(0);
|
||||||
Circle markCircle2 = (Circle) super.getChildren().get(1);
|
Circle markCircle2 = (Circle) super.getChildren().get(1);
|
||||||
Line connectingLine = (Line) super.getChildren().get(2);
|
Line connectingLine = (Line) super.getChildren().get(2);
|
||||||
if (marks.get(0).getId() == raceId) {
|
if (marks.get(0).getId() == raceId) {
|
||||||
|
if (Math.abs(markCircle1.getCenterX() - x) > 5 || Math.abs(markCircle1.getCenterY() - y) > 5) {
|
||||||
markCircle1.setCenterX(x);
|
markCircle1.setCenterX(x);
|
||||||
markCircle1.setCenterY(y);
|
markCircle1.setCenterY(y);
|
||||||
connectingLine.setStartX(markCircle1.getCenterX());
|
connectingLine.setStartX(markCircle1.getCenterX());
|
||||||
connectingLine.setStartY(markCircle1.getCenterY());
|
connectingLine.setStartY(markCircle1.getCenterY());
|
||||||
|
}
|
||||||
} else if (marks.get(1).getId() == raceId) {
|
} else if (marks.get(1).getId() == raceId) {
|
||||||
|
if (Math.abs(markCircle2.getCenterX() - x) > 5 || Math.abs(markCircle2.getCenterY() - y) > 5) {
|
||||||
markCircle2.setCenterX(x);
|
markCircle2.setCenterX(x);
|
||||||
markCircle2.setCenterY(y);
|
markCircle2.setCenterY(y);
|
||||||
connectingLine.setEndX(markCircle2.getCenterX());
|
connectingLine.setEndX(markCircle2.getCenterX());
|
||||||
@@ -140,6 +143,7 @@ public class MarkGroup extends Group {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasRaceId (int... raceIds) {
|
public boolean hasRaceId (int... raceIds) {
|
||||||
for (int id : raceIds)
|
for (int id : raceIds)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package seng302.models;
|
package seng302.fxObjects;
|
||||||
|
|
||||||
import javafx.scene.CacheHint;
|
import javafx.scene.CacheHint;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
@@ -7,24 +7,24 @@ import javafx.scene.shape.Arc;
|
|||||||
import javafx.scene.shape.ArcType;
|
import javafx.scene.shape.ArcType;
|
||||||
import javafx.scene.shape.StrokeLineCap;
|
import javafx.scene.shape.StrokeLineCap;
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
|
import javafx.scene.transform.Scale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group containing objects used to represent wakes onscreen. Contains functionality for their animation.
|
* A group containing objects used to represent wakes onscreen. Contains functionality for their animation.
|
||||||
*/
|
*/
|
||||||
class Wake extends Group {
|
public class Wake extends Group {
|
||||||
|
|
||||||
//The number of wakes
|
//The number of wakes
|
||||||
private int numWakes = 8;
|
private int numWakes = 8;
|
||||||
//The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less.
|
//The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less.
|
||||||
private final double MAX_DIFF = 75;
|
private final double MAX_DIFF = 75;
|
||||||
//Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation.
|
//Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation.
|
||||||
private final int UNIFICATION_SPEED = 750;
|
private final int UNIFICATION_SPEED = 45;
|
||||||
|
|
||||||
|
|
||||||
private Arc[] arcs = new Arc[numWakes];
|
private Arc[] arcs = new Arc[numWakes];
|
||||||
private double[] rotationalVelocities = new double[numWakes];
|
private double[] rotationalVelocities = new double[numWakes];
|
||||||
private double[] rotations = new double[numWakes];
|
private double[] rotations = new double[numWakes];
|
||||||
private double baseRad;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a wake at the given location.
|
* Create a wake at the given location.
|
||||||
@@ -40,62 +40,60 @@ class Wake extends Group {
|
|||||||
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg.
|
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg.
|
||||||
arc = new Arc(0, 0, 0, 0, -110, 40);
|
arc = new Arc(0, 0, 0, 0, -110, 40);
|
||||||
arc.setCache(true);
|
arc.setCache(true);
|
||||||
arc.setCacheHint(CacheHint.SPEED);
|
arc.setCacheHint(CacheHint.ROTATE);
|
||||||
arc.setType(ArcType.OPEN);
|
arc.setType(ArcType.OPEN);
|
||||||
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
|
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
|
||||||
arc.setStrokeWidth(3.0);
|
arc.setStrokeWidth(3.0);
|
||||||
arc.setStrokeLineCap(StrokeLineCap.ROUND);
|
arc.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
|
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
|
||||||
baseRad = (20 / numWakes);
|
|
||||||
arcs[i] = arc;
|
arcs[i] = arc;
|
||||||
|
arc.getTransforms().setAll(
|
||||||
|
new Rotate(1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
super.getChildren().addAll(arcs);
|
super.getChildren().addAll(arcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void setRotation (double rotation, double velocity) {
|
||||||
* Sets the rotationalVelocity of each arc.
|
// if (Math.abs(rotations[0] - rotation) > 20) {
|
||||||
*
|
rotate(rotation);
|
||||||
* @param rotationalVelocity The rotationalVelocity the wake should move at.
|
// } else {
|
||||||
* @param velocity The real world velocity of the boat in m/s.
|
// rotations[0] = rotation;
|
||||||
*/
|
// ((Rotate) arcs[0].getTransforms().get(0)).setAngle(rotation);
|
||||||
void setRotationalVelocity(double rotationalVelocity, double velocity) {
|
// for (int i = 1; i < numWakes; i++) {
|
||||||
rotationalVelocities[0] = rotationalVelocity;
|
// double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]);
|
||||||
for (int i = 1; i < numWakes; i++) {
|
// double shortestDistance = Math.atan2(
|
||||||
double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]);
|
// Math.sin(wakeSeparationRad),
|
||||||
double shortestDistance = Math.atan2(
|
// Math.cos(wakeSeparationRad)
|
||||||
Math.sin(wakeSeparationRad),
|
// );
|
||||||
Math.cos(wakeSeparationRad)
|
// double distDeg = Math.toDegrees(shortestDistance);
|
||||||
);
|
// if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
||||||
double distDeg = Math.toDegrees(shortestDistance);
|
// rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * 2 * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// if (distDeg < (MAX_DIFF / numWakes)) {
|
||||||
|
// rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
||||||
|
// } else
|
||||||
|
// rotationalVelocities[i] = rotationalVelocities[i - 1];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
double rad = (14 / numWakes) + velocity;
|
||||||
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (distDeg < (MAX_DIFF / numWakes))
|
|
||||||
rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
|
||||||
else
|
|
||||||
rotationalVelocities[i] = rotationalVelocities[i - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double rad = baseRad + velocity;
|
|
||||||
for (Arc arc : arcs) {
|
for (Arc arc : arcs) {
|
||||||
arc.setRadiusX(rad);
|
arc.setRadiusX(rad);
|
||||||
arc.setRadiusY(rad);
|
arc.setRadiusY(rad);
|
||||||
rad += (20 / numWakes) + (velocity / 2);
|
rad += (14 / numWakes) + (velocity / 2.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arcs rotate based on the distance they would have travelled over the supplied time interval.
|
* Arcs rotate based on the distance they would have travelled over the supplied time interval.
|
||||||
*
|
|
||||||
* @param timeInterval the time interval, in microseconds, that the wake should move.
|
|
||||||
*/
|
*/
|
||||||
void updatePosition(long timeInterval) {
|
void updatePosition() {
|
||||||
for (int i = 0; i < numWakes; i++) {
|
for (int i = 0; i < numWakes; i++) {
|
||||||
rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval;
|
rotations[i] = rotations[i] + rotationalVelocities[i];
|
||||||
arcs[i].getTransforms().setAll(new Rotate(rotations[i]));
|
((Rotate) arcs[i].getTransforms().get(0)).setAngle(rotations[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
|
||||||
|
public interface ClientConnectionDelegate {
|
||||||
|
/**
|
||||||
|
* A player has connected to the server
|
||||||
|
* @param serverToClientThread The player that has connected
|
||||||
|
*/
|
||||||
|
void clientConnected(ServerToClientThread serverToClientThread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has disconnected from the server
|
||||||
|
* @param player The player that has disconnected
|
||||||
|
*/
|
||||||
|
void clientDisconnected(Player player);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum describing the states of the game
|
||||||
|
* Created by wmu16 on 11/07/17.
|
||||||
|
*/
|
||||||
|
public enum GameStages {
|
||||||
|
|
||||||
|
LOBBYING(0),
|
||||||
|
PRE_RACE(1),
|
||||||
|
RACING(2),
|
||||||
|
FINISHED(3),
|
||||||
|
CANCELLED(4);
|
||||||
|
|
||||||
|
private long code;
|
||||||
|
|
||||||
|
GameStages(long code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCode(){
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
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)
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class GameState implements Runnable {
|
||||||
|
|
||||||
|
private static Integer STATE_UPDATES_PER_SECOND = 60;
|
||||||
|
|
||||||
|
private static Long previousUpdateTime;
|
||||||
|
public static Double windDirection;
|
||||||
|
private static Double windSpeed;
|
||||||
|
|
||||||
|
private static String hostIpAddress;
|
||||||
|
private static List<Player> players;
|
||||||
|
private static Map<Integer, Yacht> yachts;
|
||||||
|
private static Boolean isRaceStarted;
|
||||||
|
private static GameStages currentStage;
|
||||||
|
|
||||||
|
private static long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
|
||||||
|
public GameState(String hostIpAddress) {
|
||||||
|
windDirection = 180d;
|
||||||
|
windSpeed = 10000d;
|
||||||
|
this.hostIpAddress = hostIpAddress;
|
||||||
|
players = new ArrayList<>();
|
||||||
|
currentStage = GameStages.LOBBYING;
|
||||||
|
isRaceStarted = false;
|
||||||
|
yachts = new HashMap<>();
|
||||||
|
//set this when game stage changes to prerace
|
||||||
|
previousUpdateTime = System.currentTimeMillis();
|
||||||
|
yachts = new HashMap<>();
|
||||||
|
|
||||||
|
new Thread(this).start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHostIpAddress() {
|
||||||
|
return hostIpAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Player> getPlayers() {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addPlayer(Player player) {
|
||||||
|
players.add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePlayer(Player player) {
|
||||||
|
players.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addYacht(Integer sourceId, Yacht yacht) {
|
||||||
|
yachts.put(sourceId, yacht);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeYacht(Integer yachtId) {
|
||||||
|
yachts.remove(yachtId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean getIsRaceStarted() {
|
||||||
|
return isRaceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameStages getCurrentStage() {
|
||||||
|
return currentStage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCurrentStage(GameStages currentStage) {
|
||||||
|
if (currentStage == GameStages.RACING){
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
GameState.currentStage = currentStage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getStartTime(){
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double getWindDirection() {
|
||||||
|
return windDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double getWindSpeedMMS() {
|
||||||
|
return windSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double getWindSpeedKnots() {
|
||||||
|
return windSpeed / 1000 * ClientPacketParser.MS_TO_KNOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, Yacht> getYachts() {
|
||||||
|
return yachts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateBoat(Integer sourceId, BoatActionType actionType) {
|
||||||
|
Yacht playerYacht = yachts.get(sourceId);
|
||||||
|
// System.out.println("-----------------------");
|
||||||
|
switch (actionType) {
|
||||||
|
case VMG:
|
||||||
|
playerYacht.turnToVMG();
|
||||||
|
// System.out.println("Snapping to VMG");
|
||||||
|
break;
|
||||||
|
case SAILS_IN:
|
||||||
|
playerYacht.toggleSailIn();
|
||||||
|
// System.out.println("Toggling Sails");
|
||||||
|
break;
|
||||||
|
case SAILS_OUT:
|
||||||
|
playerYacht.toggleSailIn();
|
||||||
|
// System.out.println("Toggling Sails");
|
||||||
|
break;
|
||||||
|
case TACK_GYBE:
|
||||||
|
playerYacht.tackGybe(windDirection);
|
||||||
|
// System.out.println("Tack/Gybe");
|
||||||
|
break;
|
||||||
|
case UPWIND:
|
||||||
|
playerYacht.turnUpwind();
|
||||||
|
// System.out.println("Moving upwind");
|
||||||
|
break;
|
||||||
|
case DOWNWIND:
|
||||||
|
playerYacht.turnDownwind();
|
||||||
|
// System.out.println("Moving downwind");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// printBoatStatus(playerYacht);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update() {
|
||||||
|
Long timeInterval = System.currentTimeMillis() - previousUpdateTime;
|
||||||
|
previousUpdateTime = System.currentTimeMillis();
|
||||||
|
for (Yacht yacht : yachts.values()) {
|
||||||
|
yacht.update(timeInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new ID based off the size of current players + 1
|
||||||
|
* @return a playerID to be allocated to a new connetion
|
||||||
|
*/
|
||||||
|
public static Integer getUniquePlayerID() {
|
||||||
|
// TODO: 22/07/17 wmu16 - This may not be robust enough and may have to be improved on.
|
||||||
|
return yachts.size() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread to have the game state update itself at certain intervals
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000 / STATE_UPDATES_PER_SECOND);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("[GameState] interrupted exception");
|
||||||
|
}
|
||||||
|
if (currentStage == GameStages.PRE_RACE) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
//RACING
|
||||||
|
if (currentStage == GameStages.RACING) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printBoatStatus(Yacht playerYacht) {
|
||||||
|
System.out.println("-----------------------");
|
||||||
|
System.out.println("Sails are in: " + playerYacht.getSailIn());
|
||||||
|
System.out.println("Heading: " + playerYacht.getHeading());
|
||||||
|
System.out.println("Velocity: " + playerYacht.getVelocityMMS() / 1000);
|
||||||
|
System.out.println("Lat: " + playerYacht.getLocation().getLat());
|
||||||
|
System.out.println("Lng: " + playerYacht.getLocation().getLng());
|
||||||
|
System.out.println("-----------------------\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
import seng302.server.messages.Heartbeat;
|
||||||
|
import seng302.server.messages.Message;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send Heartbeat messages to connected player at a specified interval
|
||||||
|
* Will call .clientDisconnected on the delegate when a heartbeat message
|
||||||
|
* cannot be sent to a player
|
||||||
|
*/
|
||||||
|
public class HeartbeatThread extends Thread{
|
||||||
|
private final int HEARTBEAT_PERIOD = 200;
|
||||||
|
private ClientConnectionDelegate delegate;
|
||||||
|
private Integer seqNum;
|
||||||
|
private Stack<Player> disconnectedPlayers;
|
||||||
|
|
||||||
|
public HeartbeatThread(ClientConnectionDelegate delegate){
|
||||||
|
this.delegate = delegate;
|
||||||
|
seqNum = 0;
|
||||||
|
disconnectedPlayers = new Stack<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has lost connection to the server
|
||||||
|
* The player is added to a stack so that the delegate
|
||||||
|
* can be notified
|
||||||
|
*
|
||||||
|
* @param player The player that has disconnected
|
||||||
|
*/
|
||||||
|
private void playerLostConnection(Player player){
|
||||||
|
disconnectedPlayers.push(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a heartbeat message to each connected player
|
||||||
|
* The delegate is notified if a player has disconnected
|
||||||
|
*/
|
||||||
|
private void sendHeartbeatToAllPlayers(){
|
||||||
|
Message heartbeat = new Heartbeat(seqNum);
|
||||||
|
|
||||||
|
for (Player player : GameState.getPlayers()){
|
||||||
|
if (!player.getSocket().isConnected()) {
|
||||||
|
playerLostConnection(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
player.getSocket().getOutputStream().write(heartbeat.getBuffer());
|
||||||
|
} catch (IOException e) {
|
||||||
|
playerLostConnection(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDelegate();
|
||||||
|
seqNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the delegate about
|
||||||
|
* each disconnected player
|
||||||
|
*/
|
||||||
|
private void updateDelegate() {
|
||||||
|
while (!disconnectedPlayers.empty()){
|
||||||
|
delegate.clientDisconnected(disconnectedPlayers.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
Timer t = new Timer();
|
||||||
|
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
sendHeartbeatToAllPlayers();
|
||||||
|
}
|
||||||
|
}, 0, HEARTBEAT_PERIOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Observable;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.models.Player;
|
||||||
|
import seng302.models.stream.PacketBufferDelegate;
|
||||||
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing the overall server, which creates and collects server threads for each client
|
||||||
|
* Created by wmu16 on 13/07/17.
|
||||||
|
*/
|
||||||
|
public class MainServerThread extends Observable implements Runnable, ClientConnectionDelegate{
|
||||||
|
|
||||||
|
private static final int PORT = 4942;
|
||||||
|
private static final Integer CLIENT_UPDATES_PER_SECOND = 10;
|
||||||
|
private static final int LOG_LEVEL = 1;
|
||||||
|
private boolean terminated;
|
||||||
|
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
private ServerSocket serverSocket = null;
|
||||||
|
private ArrayList<ServerToClientThread> serverToClientThreads = new ArrayList<>();
|
||||||
|
|
||||||
|
public MainServerThread() {
|
||||||
|
try {
|
||||||
|
serverSocket = new ServerSocket(PORT);
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("IO error in server thread handler upon trying to make new server socket", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminated = false;
|
||||||
|
thread = new Thread(this);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
ServerListenThread serverListenThread;
|
||||||
|
HeartbeatThread heartbeatThread;
|
||||||
|
|
||||||
|
serverListenThread = new ServerListenThread(serverSocket, this);
|
||||||
|
heartbeatThread = new HeartbeatThread(this);
|
||||||
|
|
||||||
|
heartbeatThread.start();
|
||||||
|
serverListenThread.start();
|
||||||
|
|
||||||
|
//You should handle interrupts in some way, so that the thread won't keep on forever if you exit the app.
|
||||||
|
while (!terminated) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000 / CLIENT_UPDATES_PER_SECOND);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
serverLog("Interrupted exception in Main Server Thread thread sleep", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||||
|
updateClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
//RACING
|
||||||
|
if (GameState.getCurrentStage() == GameStages.RACING) {
|
||||||
|
updateClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
//FINISHED
|
||||||
|
else if (GameState.getCurrentStage() == GameStages.FINISHED) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 14/07/17 wmu16 - Send out disconnect packet to clients
|
||||||
|
try {
|
||||||
|
serverSocket.close();
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread handler upon closing socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateClients() {
|
||||||
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
|
serverToClientThread.updateClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void serverLog(String message, int logLevel){
|
||||||
|
if(logLevel <= LOG_LEVEL){
|
||||||
|
System.out.println("[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client has tried to connect to the server
|
||||||
|
* @param serverToClientThread The player that connected
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clientConnected(ServerToClientThread serverToClientThread) {
|
||||||
|
serverLog("Player Connected From " + serverToClientThread.getThread().getName(), 0);
|
||||||
|
serverToClientThreads.add(serverToClientThread);
|
||||||
|
this.addObserver(serverToClientThread);
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player has left the game, remove the player from the GameState
|
||||||
|
* @param player The player that left
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clientDisconnected(Player player) {
|
||||||
|
try {
|
||||||
|
player.getSocket().close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
serverLog("Cannot disconnect the socket for the disconnected player.", 0);
|
||||||
|
}
|
||||||
|
serverLog("Player " + player.getYacht().getSourceId() + "'s socket disconnected", 0);
|
||||||
|
GameState.removeYacht(player.getYacht().getSourceId());
|
||||||
|
GameState.removePlayer(player);
|
||||||
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
|
if (serverToClientThread.getSocket() == player.getSocket()) {
|
||||||
|
this.deleteObserver(serverToClientThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startGame() {
|
||||||
|
Timer t = new Timer();
|
||||||
|
|
||||||
|
t.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
|
serverToClientThread.sendRaceStatusMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void terminate() {
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.channels.ServerSocketChannel;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for a thread to listen to connections
|
||||||
|
* Created by wmu16 on 11/07/17.
|
||||||
|
*/
|
||||||
|
public class ServerListenThread extends Thread{
|
||||||
|
private ServerSocket serverSocket;
|
||||||
|
private ClientConnectionDelegate delegate;
|
||||||
|
|
||||||
|
public ServerListenThread(ServerSocket serverSocket, ClientConnectionDelegate delegate){
|
||||||
|
this.serverSocket = serverSocket;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for a connection and upon finding one, creates a Player object and adds it to the universal GameState
|
||||||
|
*/
|
||||||
|
private void acceptConnection() {
|
||||||
|
try {
|
||||||
|
Socket thisClient = serverSocket.accept();
|
||||||
|
if (thisClient != null && GameState.getCurrentStage().equals(GameStages.LOBBYING)) {
|
||||||
|
ServerToClientThread thisConnection = new ServerToClientThread(thisClient);
|
||||||
|
delegate.clientConnected(thisConnection);
|
||||||
|
} else {
|
||||||
|
thisClient.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(){
|
||||||
|
while (true){
|
||||||
|
acceptConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
import seng302.server.messages.BoatActionType;
|
||||||
|
|
||||||
|
|
||||||
|
public class ServerPacketParser {
|
||||||
|
|
||||||
|
|
||||||
|
public static BoatActionType extractBoatAction(StreamPacket packet) {
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long actionTypeValue = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
|
||||||
|
return BoatActionType.getType((int) actionTypeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* takes an array of up to 7 bytes and returns a positive
|
||||||
|
* long constructed from the input bytes
|
||||||
|
*
|
||||||
|
* @return a positive long if there is less than 7 bytes -1 otherwise
|
||||||
|
*/
|
||||||
|
private static long bytesToLong(byte[] bytes) {
|
||||||
|
long partialLong = 0;
|
||||||
|
int index = 0;
|
||||||
|
for (byte b : bytes) {
|
||||||
|
if (index > 6) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return partialLong;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
package seng302.gameServer;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
|
import seng302.models.Player;
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
import seng302.models.stream.packets.PacketType;
|
||||||
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
import seng302.models.xml.Race;
|
||||||
|
import seng302.models.xml.Regatta;
|
||||||
|
import seng302.models.xml.XMLGenerator;
|
||||||
|
import seng302.server.messages.BoatActionType;
|
||||||
|
import seng302.server.messages.BoatLocationMessage;
|
||||||
|
import seng302.server.messages.BoatStatus;
|
||||||
|
import seng302.server.messages.BoatSubMessage;
|
||||||
|
import seng302.server.messages.Message;
|
||||||
|
|
||||||
|
import seng302.server.messages.RaceStatus;
|
||||||
|
import seng302.server.messages.RaceStatusMessage;
|
||||||
|
import seng302.server.messages.RaceType;
|
||||||
|
import seng302.server.messages.XMLMessage;
|
||||||
|
import seng302.server.messages.XMLMessageSubType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class describing a single connection to a Client for the purposes of sending and receiving on
|
||||||
|
* its own thread. All server threads created and owned by the server thread handler which can
|
||||||
|
* trigger client updates on its threads Created by wmu16 on 13/07/17.
|
||||||
|
*/
|
||||||
|
public class ServerToClientThread implements Runnable, Observer {
|
||||||
|
|
||||||
|
private static final Integer LOG_LEVEL = 1;
|
||||||
|
private static final Integer MAX_ID_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
private Thread thread;
|
||||||
|
|
||||||
|
private InputStream is;
|
||||||
|
private OutputStream os;
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
private ByteArrayOutputStream crcBuffer;
|
||||||
|
|
||||||
|
private Boolean userIdentified = false;
|
||||||
|
private Boolean connected = true;
|
||||||
|
private Boolean updateClient = true;
|
||||||
|
// private Boolean initialisedRace = true;
|
||||||
|
|
||||||
|
private Integer seqNo;
|
||||||
|
private Integer sourceId;
|
||||||
|
|
||||||
|
private XMLGenerator xml;
|
||||||
|
|
||||||
|
public ServerToClientThread(Socket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
BufferedReader fn;
|
||||||
|
String fName = "";
|
||||||
|
BufferedReader ln;
|
||||||
|
String lName = "";
|
||||||
|
try {
|
||||||
|
is = socket.getInputStream();
|
||||||
|
os = socket.getOutputStream();
|
||||||
|
fn = new BufferedReader(
|
||||||
|
new InputStreamReader(
|
||||||
|
ServerToClientThread.class.getResourceAsStream(
|
||||||
|
"/server_config/CSV_Database_of_First_Names.csv"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
List<String> all = fn.lines().collect(Collectors.toList());
|
||||||
|
fName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||||
|
ln = new BufferedReader(
|
||||||
|
new InputStreamReader(
|
||||||
|
ServerToClientThread.class.getResourceAsStream(
|
||||||
|
"/server_config/CSV_Database_of_Last_Names.csv"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
all = ln.lines().collect(Collectors.toList());
|
||||||
|
lName = all.get(ThreadLocalRandom.current().nextInt(0, all.size()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("IO error in server thread upon grabbing streams", 1);
|
||||||
|
}
|
||||||
|
//Attempt threeway handshake with connection
|
||||||
|
sourceId = GameState.getUniquePlayerID();
|
||||||
|
if (threeWayHandshake(sourceId)) {
|
||||||
|
serverLog("Successful handshake. Client allocated id: " + sourceId, 0);
|
||||||
|
Yacht yacht = new Yacht(
|
||||||
|
"Yacht", sourceId, sourceId.toString(), fName, fName + " " + lName, "NZ"
|
||||||
|
);
|
||||||
|
// Yacht yacht = new Yacht("Kappa", "Kap", new GeoPoint(57.6708220, 11.8321340), 90.0);
|
||||||
|
GameState.addYacht(sourceId, yacht);
|
||||||
|
GameState.addPlayer(new Player(socket, yacht));
|
||||||
|
} else {
|
||||||
|
serverLog("Unsuccessful handshake. Connection rejected", 1);
|
||||||
|
closeSocket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
seqNo = 0;
|
||||||
|
thread = new Thread(this);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serverLog(String message, int logLevel) {
|
||||||
|
if (logLevel <= LOG_LEVEL) {
|
||||||
|
System.out.println(
|
||||||
|
"[SERVER " + LocalDateTime.now().toLocalTime().toString() + "] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
sendSetupMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
int sync1;
|
||||||
|
int sync2;
|
||||||
|
// TODO: 14/07/17 wmu16 - Work out how to fix this while loop
|
||||||
|
|
||||||
|
while (socket.isConnected()) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
//Perform a write if it is time to as delegated by the MainServerThread
|
||||||
|
if (updateClient) {
|
||||||
|
// TODO: 13/07/17 wmu16 - Write out game state - some function that would write all appropriate messages to this output stream
|
||||||
|
// ChatterMessage chatterMessage = new ChatterMessage(4, 14, "Hello, it's me");
|
||||||
|
// sendMessage(chatterMessage);
|
||||||
|
// try {
|
||||||
|
// GameState.outputState(os);
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// System.out.println("IO error in server thread upon writing to output stream");
|
||||||
|
// }
|
||||||
|
// sendBoatLocationPackets();
|
||||||
|
updateClient = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
crcBuffer = new ByteArrayOutputStream();
|
||||||
|
sync1 = readByte();
|
||||||
|
sync2 = readByte();
|
||||||
|
//checking if it is the start of the packet
|
||||||
|
if (sync1 == 0x47 && sync2 == 0x83) {
|
||||||
|
int type = readByte();
|
||||||
|
//No. of milliseconds since Jan 1st 1970
|
||||||
|
long timeStamp = Message.bytesToLong(getBytes(6));
|
||||||
|
skipBytes(4);
|
||||||
|
long payloadLength = Message.bytesToLong(getBytes(2));
|
||||||
|
byte[] payload = getBytes((int) payloadLength);
|
||||||
|
Checksum checksum = new CRC32();
|
||||||
|
checksum.update(crcBuffer.toByteArray(), 0, crcBuffer.size());
|
||||||
|
long computedCrc = checksum.getValue();
|
||||||
|
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||||
|
if (computedCrc == packetCrc) {
|
||||||
|
//System.out.println("RECEIVED A PACKET");
|
||||||
|
switch (PacketType.assignPacketType(type)) {
|
||||||
|
case BOAT_ACTION:
|
||||||
|
BoatActionType actionType = ServerPacketParser
|
||||||
|
.extractBoatAction(
|
||||||
|
new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||||
|
GameState.updateBoat(sourceId, actionType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverLog("Packet has been dropped", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: 24/07/17 zyt10 - fix a logic here when a client disconnected
|
||||||
|
// serverLog("ERROR OCCURRED, CLOSING SERVER CONNECTION: " + socket.getRemoteSocketAddress().toString(), 1);
|
||||||
|
closeSocket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSetupMessages() {
|
||||||
|
xml = new XMLGenerator();
|
||||||
|
Race race = new Race();
|
||||||
|
|
||||||
|
for (Yacht yacht : GameState.getYachts().values()) {
|
||||||
|
race.addBoat(yacht);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@TODO calculate lat/lng values
|
||||||
|
xml.setRegatta(new Regatta("RaceVision Test Game", 57.6679590, 11.8503233));
|
||||||
|
xml.setRace(race);
|
||||||
|
|
||||||
|
XMLMessage xmlMessage;
|
||||||
|
xmlMessage = new XMLMessage(xml.getRegattaAsXml(), XMLMessageSubType.REGATTA,
|
||||||
|
xml.getRegattaAsXml().length());
|
||||||
|
sendMessage(xmlMessage);
|
||||||
|
|
||||||
|
xmlMessage = new XMLMessage(xml.getBoatsAsXml(), XMLMessageSubType.BOAT,
|
||||||
|
xml.getBoatsAsXml().length());
|
||||||
|
sendMessage(xmlMessage);
|
||||||
|
|
||||||
|
xmlMessage = new XMLMessage(xml.getRaceAsXml(), XMLMessageSubType.RACE,
|
||||||
|
xml.getRaceAsXml().length());
|
||||||
|
sendMessage(xmlMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateClient() {
|
||||||
|
sendBoatLocationPackets();
|
||||||
|
updateClient = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to confirm the connection just accepted.
|
||||||
|
* Sends ID, expects that ID echoed for confirmation,
|
||||||
|
* if so, sends a confirmation packet back to that connection
|
||||||
|
* Creates a player instance with that ID and this thread and adds it to the GameState
|
||||||
|
* If not, close the socket and end the threads execution
|
||||||
|
*
|
||||||
|
* @param id the id to try and assign to the connection
|
||||||
|
* @return A boolean indicating if it was a successful handshake
|
||||||
|
*/
|
||||||
|
private Boolean threeWayHandshake(Integer id) {
|
||||||
|
Integer confirmationID = null;
|
||||||
|
Integer identificationAttempt = 0;
|
||||||
|
while (!userIdentified) {
|
||||||
|
try {
|
||||||
|
os.write(id); //Send out new ID looking for echo
|
||||||
|
confirmationID = is.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("Three way handshake failed", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.equals(confirmationID)) { //ID is echoed back. Connection is a client
|
||||||
|
return true;
|
||||||
|
} else if (identificationAttempt > MAX_ID_ATTEMPTS) { //No response. not a client. tidy up and go home.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
identificationAttempt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeSocket() {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("IO error in server thread upon trying to close socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int readByte() throws Exception {
|
||||||
|
int currentByte = -1;
|
||||||
|
try {
|
||||||
|
// @TODO @FIX ConnectionReset Exception when a client disconnects before it is garbage collected
|
||||||
|
currentByte = is.read();
|
||||||
|
crcBuffer.write(currentByte);
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("Socket read failed", 1);
|
||||||
|
}
|
||||||
|
if (currentByte == -1) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getBytes(int n) throws Exception {
|
||||||
|
byte[] bytes = new byte[n];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
bytes[i] = (byte) readByte();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipBytes(long n) throws Exception {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
readByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(Message message) {
|
||||||
|
try {
|
||||||
|
os.write(message.getBuffer());
|
||||||
|
} catch (SocketException e) {
|
||||||
|
//serverLog("Player " + sourceId + " side socket disconnected", 1);
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
serverLog("Message send failed", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSeqNo() {
|
||||||
|
seqNo++;
|
||||||
|
return seqNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendBoatLocationPackets() {
|
||||||
|
ArrayList<Yacht> yachts = new ArrayList<>(GameState.getYachts().values());
|
||||||
|
for (Yacht yacht : yachts) {
|
||||||
|
// System.out.println("[SERVER] Lat: " + yacht.getLocation().getLat() + " Lon: " + yacht.getLocation().getLng());
|
||||||
|
BoatLocationMessage boatLocationMessage =
|
||||||
|
new BoatLocationMessage(
|
||||||
|
yacht.getSourceId(),
|
||||||
|
getSeqNo(),
|
||||||
|
yacht.getLocation().getLat(),
|
||||||
|
yacht.getLocation().getLng(),
|
||||||
|
yacht.getHeading(),
|
||||||
|
(long) yacht.getVelocityMMS());
|
||||||
|
|
||||||
|
sendMessage(boatLocationMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread getThread() {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendRaceStatusMessage() {
|
||||||
|
// variables taken from GameServerThread
|
||||||
|
|
||||||
|
|
||||||
|
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
|
||||||
|
BoatStatus boatStatus;
|
||||||
|
RaceStatus raceStatus;
|
||||||
|
|
||||||
|
for (Player player : GameState.getPlayers()) {
|
||||||
|
Yacht y = player.getYacht();
|
||||||
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.PRE_RACE) {
|
||||||
|
boatStatus = BoatStatus.PRESTART;
|
||||||
|
} else if (GameState.getCurrentStage() == GameStages.RACING) {
|
||||||
|
boatStatus = BoatStatus.RACING;
|
||||||
|
} else {
|
||||||
|
boatStatus = BoatStatus.UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoatSubMessage m = new BoatSubMessage(y.getSourceId(), boatStatus, 0, 0, 0, 1234l,
|
||||||
|
1234l);
|
||||||
|
boatSubMessages.add(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameState.getCurrentStage() == GameStages.RACING) {
|
||||||
|
raceStatus = RaceStatus.STARTED;
|
||||||
|
} else {
|
||||||
|
raceStatus = RaceStatus.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(new RaceStatusMessage(1, raceStatus, GameState.getStartTime(), GameState.getWindDirection(),
|
||||||
|
GameState.getWindSpeedMMS().longValue(), GameState.getPlayers().size(),
|
||||||
|
RaceType.MATCH_RACE, 1, boatSubMessages));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket getSocket() {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package seng302.models;
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Class defining a player and their respective details in the game as held by the model
|
||||||
|
* Created by wmu16 on 10/07/17.
|
||||||
|
*/
|
||||||
|
public class Player {
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
private Yacht yacht;
|
||||||
|
private Integer lastMarkPassed;
|
||||||
|
|
||||||
|
|
||||||
|
public Player(Socket socket, Yacht yacht) {
|
||||||
|
this.socket = socket;
|
||||||
|
this.yacht = yacht;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket getSocket() {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLastMarkPassed() {
|
||||||
|
return lastMarkPassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastMarkPassed(Integer lastMarkPassed) {
|
||||||
|
this.lastMarkPassed = lastMarkPassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Yacht getYacht() {
|
||||||
|
return yacht;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String playerAddress = null;
|
||||||
|
|
||||||
|
if (socket == null){
|
||||||
|
return "Disconnected Player";
|
||||||
|
}
|
||||||
|
|
||||||
|
playerAddress = socket.getRemoteSocketAddress().toString();
|
||||||
|
|
||||||
|
|
||||||
|
return playerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(obj instanceof Player)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((Player) obj).socket.equals(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return socket.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
import java.util.ArrayList;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,9 +26,8 @@ public final class PolarTable {
|
|||||||
* Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed.
|
* Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed.
|
||||||
* These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap
|
* These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap
|
||||||
* as a value
|
* as a value
|
||||||
* @param file containing the polar csv information
|
|
||||||
*/
|
*/
|
||||||
public static void parsePolarFile(String file) {
|
public static void parsePolarFile(InputStream polarFile) {
|
||||||
polarTable = new HashMap<>();
|
polarTable = new HashMap<>();
|
||||||
upwindOptimal = new HashMap<>();
|
upwindOptimal = new HashMap<>();
|
||||||
downwindOptimal = new HashMap<>();
|
downwindOptimal = new HashMap<>();
|
||||||
@@ -34,7 +35,7 @@ public final class PolarTable {
|
|||||||
String line;
|
String line;
|
||||||
Boolean isHeaderLine = true;
|
Boolean isHeaderLine = true;
|
||||||
|
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(polarFile))) {
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
String[] thisLine = line.split(",");
|
String[] thisLine = line.split(",");
|
||||||
|
|
||||||
@@ -67,8 +68,10 @@ public final class PolarTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[PolarTable] IO exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +125,7 @@ public final class PolarTable {
|
|||||||
*/
|
*/
|
||||||
public static HashMap<Double, Double> getOptimalUpwindVMG(Double thisWindSpeed) {
|
public static HashMap<Double, Double> getOptimalUpwindVMG(Double thisWindSpeed) {
|
||||||
|
|
||||||
Double polarWindSpeed = getClosestMatch(thisWindSpeed);
|
Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed);
|
||||||
return upwindOptimal.get(polarWindSpeed);
|
return upwindOptimal.get(polarWindSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,30 +137,48 @@ public final class PolarTable {
|
|||||||
*/
|
*/
|
||||||
public static HashMap<Double, Double> getOptimalDownwindVMG(Double thisWindSpeed) {
|
public static HashMap<Double, Double> getOptimalDownwindVMG(Double thisWindSpeed) {
|
||||||
|
|
||||||
Double polarWindSpeed = getClosestMatch(thisWindSpeed);
|
Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed);
|
||||||
return downwindOptimal.get(polarWindSpeed);
|
return downwindOptimal.get(polarWindSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Double getClosestMatch(Double thisWindSpeed) {
|
public static Double getBoatSpeed(Double thisWindSpeed, Double thisHeading) {
|
||||||
|
|
||||||
ArrayList<Double> windValues = new ArrayList<>(polarTable.keySet());
|
Double polarWindSpeed = getClosestWindSpeedInPolar(thisWindSpeed);
|
||||||
|
Double polarAngle = getClosestAngleInPolar(polarTable.get(polarWindSpeed), thisHeading);
|
||||||
|
|
||||||
Double lowerVal = windValues.get(0);
|
return polarTable.get(polarWindSpeed).get(polarAngle);
|
||||||
Double upperVal = windValues.get(1);
|
|
||||||
|
|
||||||
for(int i = 0; i < windValues.size() - 1; i++) {
|
|
||||||
lowerVal = windValues.get(i);
|
|
||||||
upperVal = windValues.get(i+1);
|
|
||||||
if (thisWindSpeed <= upperVal) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Double lowerDiff = Math.abs(lowerVal - thisWindSpeed);
|
|
||||||
Double upperDiff = Math.abs(upperVal - thisWindSpeed);
|
|
||||||
|
|
||||||
return (lowerDiff <= upperDiff) ? lowerVal : upperVal;
|
public static Double getClosestWindSpeedInPolar(Double thisWindSpeed) {
|
||||||
|
Double smallestDif = Double.POSITIVE_INFINITY;
|
||||||
|
Double closestWind = 0d;
|
||||||
|
|
||||||
|
for (Double polarWindSpeed : polarTable.keySet()) {
|
||||||
|
Double difference = Math.abs(polarWindSpeed - thisWindSpeed);
|
||||||
|
if (difference < smallestDif) {
|
||||||
|
smallestDif = difference;
|
||||||
|
closestWind = polarWindSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestWind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Double getClosestAngleInPolar(HashMap<Double, Double> thisWindSpeedPolar, Double thisHeading) {
|
||||||
|
Double smallestDif = Double.POSITIVE_INFINITY;
|
||||||
|
Double closestAngle = 0d;
|
||||||
|
|
||||||
|
for (Double polarAngle : thisWindSpeedPolar.keySet()) {
|
||||||
|
Double difference = Math.abs(polarAngle - thisHeading);
|
||||||
|
if (difference < smallestDif) {
|
||||||
|
smallestDif = difference;
|
||||||
|
closestAngle = polarAngle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
import javafx.scene.paint.Color;
|
import static seng302.utilities.GeoUtility.getGeoCoordinate;
|
||||||
import seng302.models.mark.Mark;
|
|
||||||
import seng302.controllers.RaceViewController;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import seng302.models.stream.StreamParser;
|
import java.util.HashMap;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
|
import javafx.scene.paint.Color;
|
||||||
|
import seng302.client.ClientPacketParser;
|
||||||
|
import seng302.controllers.RaceViewController;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yacht class for the racing boat.
|
* Yacht class for the racing boat.
|
||||||
@@ -17,37 +20,67 @@ import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
|
|||||||
*/
|
*/
|
||||||
public class Yacht {
|
public class Yacht {
|
||||||
|
|
||||||
|
private final Double TURN_STEP = 5.0;
|
||||||
|
|
||||||
|
private Double lastHeading;
|
||||||
|
private Boolean sailIn;
|
||||||
|
|
||||||
|
|
||||||
// Used in boat group
|
// Used in boat group
|
||||||
private Color colour;
|
private Color colour;
|
||||||
private double velocity;
|
|
||||||
|
|
||||||
private String boatType;
|
private String boatType;
|
||||||
private Integer sourceID;
|
private Integer sourceId;
|
||||||
private String hullID; //matches HullNum in the XML spec.
|
private String hullID; //matches HullNum in the XML spec.
|
||||||
private String shortName;
|
private String shortName;
|
||||||
private String boatName;
|
private String boatName;
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
|
// Situational data
|
||||||
|
|
||||||
|
|
||||||
// Boat status
|
// Boat status
|
||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Integer legNumber;
|
private Integer legNumber;
|
||||||
private Integer penaltiesAwarded;
|
private Integer penaltiesAwarded;
|
||||||
private Integer penaltiesServed;
|
private Integer penaltiesServed;
|
||||||
private Long estimateTimeAtNextMark;
|
|
||||||
private Long estimateTimeAtFinish;
|
private Long estimateTimeAtFinish;
|
||||||
private String position;
|
private String position;
|
||||||
|
private GeoPoint location;
|
||||||
|
private Double heading;
|
||||||
|
private Double velocity;
|
||||||
|
private Long timeTillNext;
|
||||||
|
private Long markRoundTime;
|
||||||
|
|
||||||
// Mark rounding
|
// Mark rounding
|
||||||
private Long markRoundingTime;
|
|
||||||
private Mark lastMarkRounded;
|
private Mark lastMarkRounded;
|
||||||
private Mark nextMark;
|
private Mark nextMark;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param location latlon location of the boat stored in a geopoint
|
||||||
|
* @param heading heading of the boat in degrees from 0 to 365 with 0 being north
|
||||||
|
*/
|
||||||
|
public Yacht(GeoPoint location, Double heading) {
|
||||||
|
this.location = location;
|
||||||
|
this.heading = heading;
|
||||||
|
this.velocity = 0.0;
|
||||||
|
this.sailIn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in EventTest and RaceTest.
|
* Used in EventTest and RaceTest.
|
||||||
*
|
*
|
||||||
* @param boatName Create a yacht object with name.
|
* @param boatName Create a yacht object with name.
|
||||||
*/
|
*/
|
||||||
public Yacht(String boatName) {
|
public Yacht(String boatName, String shortName, GeoPoint location, Double heading) {
|
||||||
this.boatName = boatName;
|
this.boatName = boatName;
|
||||||
|
this.shortName = shortName;
|
||||||
|
this.location = location;
|
||||||
|
this.heading = heading;
|
||||||
|
this.velocity = 0.0;
|
||||||
|
this.sailIn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,29 +94,178 @@ public class Yacht {
|
|||||||
this.boatName = boatName;
|
this.boatName = boatName;
|
||||||
this.velocity = boatVelocity;
|
this.velocity = boatVelocity;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
this.sourceID = id;
|
this.sourceId = id;
|
||||||
|
this.sailIn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Yacht(String boatType, Integer sourceID, String hullID, String shortName,
|
|
||||||
|
public Yacht(String boatType, Integer sourceId, String hullID, String shortName,
|
||||||
String boatName, String country) {
|
String boatName, String country) {
|
||||||
this.boatType = boatType;
|
this.boatType = boatType;
|
||||||
this.sourceID = sourceID;
|
this.sourceId = sourceId;
|
||||||
this.hullID = hullID;
|
this.hullID = hullID;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
this.boatName = boatName;
|
this.boatName = boatName;
|
||||||
this.country = country;
|
this.country = country;
|
||||||
this.position = "-";
|
this.position = "-";
|
||||||
|
this.sailIn = false;
|
||||||
|
this.location = new GeoPoint(57.670341, 11.826856);
|
||||||
|
this.heading = 120.0; //In degrees
|
||||||
|
this.velocity = 0d; //in mms-1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeInterval since last update in milliseconds
|
||||||
|
*/
|
||||||
|
public void update(Long timeInterval) {
|
||||||
|
|
||||||
|
Double secondsElapsed = timeInterval / 1000000.0;
|
||||||
|
Double windSpeedKnots = GameState.getWindSpeedKnots();
|
||||||
|
Double trueWindAngle = Math.abs(GameState.getWindDirection() - heading);
|
||||||
|
Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, trueWindAngle);
|
||||||
|
Double maxBoatSpeed = boatSpeedInKnots / ClientPacketParser.MS_TO_KNOTS * 1000;
|
||||||
|
if (sailIn && velocity <= maxBoatSpeed && maxBoatSpeed != 0d) {
|
||||||
|
|
||||||
|
if (velocity < maxBoatSpeed) {
|
||||||
|
velocity += maxBoatSpeed / 15; // Acceleration
|
||||||
|
}
|
||||||
|
if (velocity > maxBoatSpeed) {
|
||||||
|
velocity = maxBoatSpeed; // Prevent the boats from exceeding top speed
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // Deceleration
|
||||||
|
|
||||||
|
if (velocity > 0d) {
|
||||||
|
if (maxBoatSpeed != 0d) {
|
||||||
|
velocity -= maxBoatSpeed / 600;
|
||||||
|
} else {
|
||||||
|
velocity -= velocity / 100;
|
||||||
|
}
|
||||||
|
if (velocity < 0) {
|
||||||
|
velocity = 0d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Double metersCovered = velocity * secondsElapsed;
|
||||||
|
location = getGeoCoordinate(location, heading, metersCovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Double getHeading() {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void adjustHeading(Double amount) {
|
||||||
|
Double newVal = heading + amount;
|
||||||
|
lastHeading = heading;
|
||||||
|
// TODO: 24/07/17 wmu16 - '%' in java does remainder, we need modulo. All this must be changed here, this is why we have neg values!
|
||||||
|
heading = (double) Math.floorMod(newVal.longValue(), 360L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tackGybe(Double windDirection) {
|
||||||
|
Double normalizedHeading = normalizeHeading();
|
||||||
|
adjustHeading(-2 * normalizedHeading);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleSailIn() {
|
||||||
|
sailIn = !sailIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnUpwind() {
|
||||||
|
Double normalizedHeading = normalizeHeading();
|
||||||
|
if (normalizedHeading == 0) {
|
||||||
|
if (lastHeading < 180) {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
}
|
||||||
|
} else if (normalizedHeading == 180) {
|
||||||
|
if (lastHeading < 180) {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
}
|
||||||
|
} else if (normalizedHeading < 180) {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnDownwind() {
|
||||||
|
Double normalizedHeading = normalizeHeading();
|
||||||
|
if (normalizedHeading == 0) {
|
||||||
|
if (lastHeading < 180) {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
}
|
||||||
|
} else if (normalizedHeading == 180) {
|
||||||
|
if (lastHeading < 180) {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
}
|
||||||
|
} else if (normalizedHeading < 180) {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnToVMG() {
|
||||||
|
Double normalizedHeading = normalizeHeading();
|
||||||
|
Double optimalHeading;
|
||||||
|
HashMap<Double, Double> optimalPolarMap;
|
||||||
|
|
||||||
|
if (normalizedHeading >= 90 && normalizedHeading <= 270) { // Downwind
|
||||||
|
optimalPolarMap = PolarTable.getOptimalDownwindVMG(GameState.getWindSpeedKnots());
|
||||||
|
optimalHeading = optimalPolarMap.keySet().iterator().next();
|
||||||
|
} else {
|
||||||
|
optimalPolarMap = PolarTable.getOptimalUpwindVMG(GameState.getWindSpeedKnots());
|
||||||
|
optimalHeading = optimalPolarMap.keySet().iterator().next();
|
||||||
|
}
|
||||||
|
// Take optimal heading and turn into correct
|
||||||
|
optimalHeading =
|
||||||
|
optimalHeading + (double) Math.floorMod(GameState.getWindDirection().longValue(), 360L);
|
||||||
|
|
||||||
|
turnTowardsHeading(optimalHeading);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void turnTowardsHeading(Double newHeading) {
|
||||||
|
System.out.println(newHeading);
|
||||||
|
if (heading < 90 && newHeading > 270) {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
} else {
|
||||||
|
if (heading < newHeading) {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Double normalizeHeading() {
|
||||||
|
Double normalizedHeading = heading - GameState.windDirection;
|
||||||
|
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
|
||||||
|
return normalizedHeading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBoatType() {
|
public String getBoatType() {
|
||||||
return boatType;
|
return boatType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getSourceID() {
|
public Integer getSourceId() {
|
||||||
return sourceID;
|
//@TODO Remove and merge with Creating Game Loop
|
||||||
|
if (sourceId == null) return 0;
|
||||||
|
return sourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHullID() {
|
public String getHullID() {
|
||||||
|
if (hullID == null) return "";
|
||||||
return hullID;
|
return hullID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +278,7 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getCountry() {
|
public String getCountry() {
|
||||||
|
if (country == null) return "";
|
||||||
return country;
|
return country;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +295,8 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLegNumber(Integer legNumber) {
|
public void setLegNumber(Integer legNumber) {
|
||||||
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus(sourceID)) {
|
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus(
|
||||||
|
sourceId)) {
|
||||||
RaceViewController.updateYachtPositionSparkline(this, legNumber);
|
RaceViewController.updateYachtPositionSparkline(this, legNumber);
|
||||||
}
|
}
|
||||||
this.legNumber = legNumber;
|
this.legNumber = legNumber;
|
||||||
@@ -134,14 +318,8 @@ public class Yacht {
|
|||||||
this.penaltiesServed = penaltiesServed;
|
this.penaltiesServed = penaltiesServed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getEstimateTimeAtNextMark() {
|
|
||||||
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
|
||||||
// return format.format(estimateTimeAtNextMark);
|
|
||||||
return estimateTimeAtNextMark;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
|
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
|
||||||
this.estimateTimeAtNextMark = estimateTimeAtNextMark;
|
timeTillNext = estimateTimeAtNextMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEstimateTimeAtFinish() {
|
public String getEstimateTimeAtFinish() {
|
||||||
@@ -169,20 +347,29 @@ public class Yacht {
|
|||||||
this.colour = colour;
|
this.colour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getVelocity() {
|
|
||||||
return velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVelocity(double velocity) {
|
public void setVelocity(double velocity) {
|
||||||
this.velocity = velocity;
|
this.velocity = velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getMarkRoundingTime() {
|
|
||||||
return markRoundingTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMarkRoundingTime(Long markRoundingTime) {
|
public void setMarkRoundingTime(Long markRoundingTime) {
|
||||||
this.markRoundingTime = markRoundingTime;
|
this.markRoundTime = markRoundingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getVelocityMMS() {
|
||||||
|
return velocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getVelocityKnots() {
|
||||||
|
return velocity / 1000 * ClientPacketParser.MS_TO_KNOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTimeTillNext() {
|
||||||
|
return timeTillNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMarkRoundTime() {
|
||||||
|
return markRoundTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mark getLastMarkRounded() {
|
public Mark getLastMarkRounded() {
|
||||||
@@ -193,11 +380,6 @@ public class Yacht {
|
|||||||
this.lastMarkRounded = lastMarkRounded;
|
this.lastMarkRounded = lastMarkRounded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return boatName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNextMark(Mark nextMark) {
|
public void setNextMark(Mark nextMark) {
|
||||||
this.nextMark = nextMark;
|
this.nextMark = nextMark;
|
||||||
}
|
}
|
||||||
@@ -205,4 +387,18 @@ public class Yacht {
|
|||||||
public Mark getNextMark(){
|
public Mark getNextMark(){
|
||||||
return nextMark;
|
return nextMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getSailIn() {
|
||||||
|
return sailIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return boatName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoPoint getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package seng302.models.map;
|
package seng302.models.map;
|
||||||
|
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -35,7 +37,7 @@ public class CanvasMap {
|
|||||||
|
|
||||||
return new Image(connection.getInputStream());
|
return new Image(connection.getInputStream());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
System.out.println("[CanvasMap] Exception");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,10 +67,10 @@ public class CanvasMap {
|
|||||||
|
|
||||||
private MapSize getMapSize(int zoom, Boundary boundary) {
|
private MapSize getMapSize(int zoom, Boundary boundary) {
|
||||||
double scale = Math.pow(2, zoom);
|
double scale = Math.pow(2, zoom);
|
||||||
MapGeo geoSW = new MapGeo(boundary.getSouthLat(), boundary.getWestLng());
|
GeoPoint geoSW = new GeoPoint(boundary.getSouthLat(), boundary.getWestLng());
|
||||||
MapGeo geoNE = new MapGeo(boundary.getNorthLat(), boundary.getEastLng());
|
GeoPoint geoNE = new GeoPoint(boundary.getNorthLat(), boundary.getEastLng());
|
||||||
MapPoint pointSW = MercatorProjection.toMapPoint(geoSW);
|
Point2D pointSW = MercatorProjection.toMapPoint(geoSW);
|
||||||
MapPoint pointNE = MercatorProjection.toMapPoint(geoNE);
|
Point2D pointNE = MercatorProjection.toMapPoint(geoNE);
|
||||||
return new MapSize(Math.abs(pointNE.getX() - pointSW.getX()) * scale,
|
return new MapSize(Math.abs(pointNE.getX() - pointSW.getX()) * scale,
|
||||||
Math.abs(pointNE.getY() - pointSW.getY()) * scale);
|
Math.abs(pointNE.getY() - pointSW.getY()) * scale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package seng302.models.map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class represent Geo location (latitude, longitude).
|
|
||||||
* Created by Haoming on 15/5/2017
|
|
||||||
*/
|
|
||||||
class MapGeo {
|
|
||||||
|
|
||||||
private double lat, lng;
|
|
||||||
|
|
||||||
MapGeo(double lat, double lng) {
|
|
||||||
this.lat = lat;
|
|
||||||
this.lng = lng;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getLat() {
|
|
||||||
return lat;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLat(double lat) {
|
|
||||||
this.lat = lat;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getLng() {
|
|
||||||
return lng;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLng(double lng) {
|
|
||||||
this.lng = lng;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package seng302.models.map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class represent euclidean planar point (x, y)
|
|
||||||
* Created by Haoming on 15/5/2017
|
|
||||||
*/
|
|
||||||
class MapPoint {
|
|
||||||
|
|
||||||
private double x, y;
|
|
||||||
|
|
||||||
MapPoint(double x, double y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setX(double x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setY(double y) {
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package seng302.models.map;
|
package seng302.models.map;
|
||||||
|
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An utility class useful to convert between Geo locations and Mercator projection
|
* An utility class useful to convert between Geo locations and Mercator projection
|
||||||
* planar coordinates.
|
* planar coordinates.
|
||||||
@@ -22,31 +25,31 @@ public class MercatorProjection {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Projects a Geo Location (lat, lng) on a planar
|
* Projects a Geo Location (lat, lng) on a planar
|
||||||
* @param geo MapGeo (lat, lng) location to be projected
|
* @param geo GeoPoint (lat, lng) location to be projected
|
||||||
* @return the projection GeoPoint (x, y) on planar
|
* @return the projection Point2D (x, y) on planar
|
||||||
*/
|
*/
|
||||||
public static MapPoint toMapPoint(MapGeo geo) {
|
public static Point2D toMapPoint(GeoPoint geo) {
|
||||||
MapPoint point = new MapPoint(0, 0);
|
double x, y;
|
||||||
MapPoint origin = new MapPoint(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
||||||
point.setX(origin.getX() + geo.getLng() * pixelsPerLngDegree);
|
x = (origin.getX() + geo.getLng() * pixelsPerLngDegree);
|
||||||
|
|
||||||
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
|
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
|
||||||
// 89.189. This is about a third of a tile past the edge of the world tile.
|
// 89.189. This is about a third of a tile past the edge of the world tile.
|
||||||
double sinY = bound(Math.sin(Math.toRadians(geo.getLat())));
|
double sinY = bound(Math.sin(Math.toRadians(geo.getLat())));
|
||||||
point.setY(origin.getY() + 0.5 * Math.log((1 + sinY) / (1 - sinY)) * (-pixelsPerLngRadian));
|
y = origin.getY() + 0.5 * Math.log((1 + sinY) / (1 - sinY)) * (-pixelsPerLngRadian);
|
||||||
return point;
|
return new Point2D(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the planar projection (x, y) back to Geo Location (lat, lng)
|
* Converts the planar projection (x, y) back to Geo Location (lat, lng)
|
||||||
* @param point MapPoint (x, y) to be converted back
|
* @param point Point2D (x, y) to be converted back
|
||||||
* @return the original Geo location converted from the given projection point
|
* @return the original Geo location converted from the given projection point
|
||||||
*/
|
*/
|
||||||
public static MapGeo toMapGeo(MapPoint point) {
|
public static GeoPoint toMapGeo(Point2D point) {
|
||||||
MapPoint origin = new MapPoint(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
||||||
double lng = (point.getX() - origin.getX()) / pixelsPerLngDegree;
|
double lng = (point.getX() - origin.getX()) / pixelsPerLngDegree;
|
||||||
double latRadians = (point.getY() - origin.getY()) / (-pixelsPerLngRadian);
|
double latRadians = (point.getY() - origin.getY()) / (-pixelsPerLngRadian);
|
||||||
double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2.0);
|
double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2.0);
|
||||||
return new MapGeo(lat, lng);
|
return new GeoPoint(lat, lng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package seng302.models.stream;
|
||||||
|
|
||||||
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
|
||||||
|
public interface PacketBufferDelegate {
|
||||||
|
boolean addToBuffer(StreamPacket streamPacket);
|
||||||
|
}
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
package seng302.models.stream;
|
|
||||||
|
|
||||||
import seng302.models.stream.packets.StreamPacket;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
|
||||||
import java.util.zip.CRC32;
|
|
||||||
import java.util.zip.Checksum;
|
|
||||||
|
|
||||||
|
|
||||||
public class StreamReceiver extends Thread {
|
|
||||||
private InputStream stream;
|
|
||||||
private Socket host;
|
|
||||||
private ByteArrayOutputStream crcBuffer;
|
|
||||||
private Thread t;
|
|
||||||
private String threadName;
|
|
||||||
public static PriorityBlockingQueue<StreamPacket> packetBuffer;
|
|
||||||
private static boolean moreBytes;
|
|
||||||
|
|
||||||
public StreamReceiver(String hostAddress, int hostPort, String threadName) {
|
|
||||||
this.threadName = threadName;
|
|
||||||
this.setDaemon(true);
|
|
||||||
try {
|
|
||||||
host = new Socket(hostAddress, hostPort);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run(){
|
|
||||||
PriorityBlockingQueue<StreamPacket> pq = new PriorityBlockingQueue<>(256, new Comparator<StreamPacket>() {
|
|
||||||
@Override
|
|
||||||
public int compare(StreamPacket s1, StreamPacket s2) {
|
|
||||||
return (int) (s1.getTimeStamp() - s2.getTimeStamp());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
packetBuffer = pq;
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start () {
|
|
||||||
if (t == null) {
|
|
||||||
t = new Thread (this, threadName);
|
|
||||||
t.start ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public StreamReceiver(Socket host, PriorityBlockingQueue packetBuffer){
|
|
||||||
this.host=host;
|
|
||||||
this.packetBuffer = packetBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void connect(){
|
|
||||||
try {
|
|
||||||
stream = host.getInputStream();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int sync1;
|
|
||||||
int sync2;
|
|
||||||
moreBytes = true;
|
|
||||||
while(moreBytes) {
|
|
||||||
try {
|
|
||||||
crcBuffer = new ByteArrayOutputStream();
|
|
||||||
sync1 = readByte();
|
|
||||||
sync2 = readByte();
|
|
||||||
//checking if it is the start of the packet
|
|
||||||
if(sync1 == 0x47 && sync2 == 0x83) {
|
|
||||||
int type = readByte();
|
|
||||||
//No. of milliseconds since Jan 1st 1970
|
|
||||||
long timeStamp = bytesToLong(getBytes(6));
|
|
||||||
skipBytes(4);
|
|
||||||
long payloadLength = bytesToLong(getBytes(2));
|
|
||||||
byte[] payload = getBytes((int) payloadLength);
|
|
||||||
Checksum checksum = new CRC32();
|
|
||||||
checksum.update(crcBuffer.toByteArray(), 0, crcBuffer.size());
|
|
||||||
long computedCrc = checksum.getValue();
|
|
||||||
long packetCrc = bytesToLong(getBytes(4));
|
|
||||||
if (computedCrc == packetCrc) {
|
|
||||||
packetBuffer.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
|
||||||
} else {
|
|
||||||
System.err.println("Packet has been dropped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
moreBytes = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int readByte() throws Exception {
|
|
||||||
int currentByte = -1;
|
|
||||||
try {
|
|
||||||
currentByte = stream.read();
|
|
||||||
crcBuffer.write(currentByte);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
if (currentByte == -1){
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
return currentByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getBytes(int n) throws Exception{
|
|
||||||
byte[] bytes = new byte[n];
|
|
||||||
for (int i = 0; i < n; i++){
|
|
||||||
bytes[i] = (byte) readByte();
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void skipBytes(long n) throws Exception{
|
|
||||||
for (int i=0; i < n; i++){
|
|
||||||
readByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* takes an array of up to 7 bytes in little endian format and
|
|
||||||
* returns a positive long constructed from the input bytes
|
|
||||||
*
|
|
||||||
* @return a positive long if there is less than 8 bytes -1 otherwise
|
|
||||||
*/
|
|
||||||
private long bytesToLong(byte[] bytes){
|
|
||||||
long partialLong = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (byte b: bytes){
|
|
||||||
if (index > 6){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return partialLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1");
|
|
||||||
//StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread2");
|
|
||||||
sr.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void noMoreBytes(){
|
|
||||||
moreBytes = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -558,7 +558,7 @@ public class XMLParser {
|
|||||||
getNodeAttributeString(currentBoat, "Country"));
|
getNodeAttributeString(currentBoat, "Country"));
|
||||||
this.boats.add(boat);
|
this.boats.add(boat);
|
||||||
if (boat.getBoatType().equals("Yacht")) {
|
if (boat.getBoatType().equals("Yacht")) {
|
||||||
competingBoats.put(boat.getSourceID(), boat);
|
competingBoats.put(boat.getSourceId(), boat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public enum PacketType {
|
|||||||
MARK_ROUNDING,
|
MARK_ROUNDING,
|
||||||
COURSE_WIND,
|
COURSE_WIND,
|
||||||
AVG_WIND,
|
AVG_WIND,
|
||||||
|
BOAT_ACTION,
|
||||||
OTHER;
|
OTHER;
|
||||||
|
|
||||||
public static PacketType assignPacketType(int packetType){
|
public static PacketType assignPacketType(int packetType){
|
||||||
@@ -44,6 +45,8 @@ public enum PacketType {
|
|||||||
return COURSE_WIND;
|
return COURSE_WIND;
|
||||||
case 47:
|
case 47:
|
||||||
return AVG_WIND;
|
return AVG_WIND;
|
||||||
|
case 100:
|
||||||
|
return BOAT_ACTION;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return OTHER;
|
return OTHER;
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package seng302.models.xml;
|
||||||
|
|
||||||
|
import seng302.models.Yacht;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Race object that can be parsed into XML
|
||||||
|
*/
|
||||||
|
public class Race {
|
||||||
|
private List<Yacht> yachts;
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
public Race(){
|
||||||
|
yachts = new ArrayList<>();
|
||||||
|
startTime = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a boat to the race
|
||||||
|
* @param yacht The boat to add
|
||||||
|
*/
|
||||||
|
public void addBoat(Yacht yacht){
|
||||||
|
yachts.add(yacht);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of boats in the race
|
||||||
|
* @return A List of boats
|
||||||
|
*/
|
||||||
|
public List<Yacht> getBoats(){
|
||||||
|
return Collections.unmodifiableList(yachts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the time until the race starts
|
||||||
|
* @param seconds The time in seconds until the race starts
|
||||||
|
*/
|
||||||
|
public void setRaceStartDelay(Integer seconds){
|
||||||
|
startTime = startTime.plusMinutes(seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the time the race starts
|
||||||
|
* @return The time the race starts
|
||||||
|
*/
|
||||||
|
public String getRaceStartTime(){
|
||||||
|
return startTime.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package seng302.models.xml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Race regatta that can be parsed into XML
|
||||||
|
*/
|
||||||
|
public class Regatta {
|
||||||
|
private final Double DEFAULT_ALTITUDE = 0d;
|
||||||
|
private final Integer DEFAULT_REGATTA_ID = 0;
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
private String courseName;
|
||||||
|
|
||||||
|
private Double latitude;
|
||||||
|
private Double longitude;
|
||||||
|
private Double altitude;
|
||||||
|
|
||||||
|
private Integer utcOffset;
|
||||||
|
private Double magneticVariation;
|
||||||
|
|
||||||
|
public Regatta(String name, Double latitude, Double longitude) {
|
||||||
|
this.name = name;
|
||||||
|
this.id = DEFAULT_REGATTA_ID;
|
||||||
|
this.courseName = name;
|
||||||
|
|
||||||
|
this.latitude = latitude;
|
||||||
|
this.longitude = longitude;
|
||||||
|
this.altitude = DEFAULT_ALTITUDE;
|
||||||
|
|
||||||
|
this.utcOffset = 0;
|
||||||
|
this.magneticVariation = 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMagneticVariation(Double magneticVariation){
|
||||||
|
this.magneticVariation = magneticVariation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUtcOffset(Integer offset){
|
||||||
|
this.utcOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE!! The following getters must follow the JavaBean standard (getPropertyName()), and must be public.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public String getName(){
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCourseName(){
|
||||||
|
return courseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRegattaId(){
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLatitude() {
|
||||||
|
return latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLongitude() {
|
||||||
|
return longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAltitude() {
|
||||||
|
return altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getUtcOffset(){
|
||||||
|
return utcOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getMagneticVariation(){
|
||||||
|
return magneticVariation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package seng302.models.xml;
|
||||||
|
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.Template;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import seng302.server.messages.XMLMessageSubType;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
|
||||||
|
*/
|
||||||
|
public class XMLGenerator {
|
||||||
|
private static final String XML_TEMPLATE_DIR = "/server_config/xml_templates";
|
||||||
|
private static final String REGATTA_TEMPLATE_NAME = "regatta.ftlh";
|
||||||
|
private static final String BOATS_TEMPLATE_NAME = "boats.ftlh";
|
||||||
|
private static final String RACE_TEMPLATE_NAME = "race.ftlh";
|
||||||
|
private Configuration configuration;
|
||||||
|
private Regatta regatta;
|
||||||
|
private Race race;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a configuration instance for Apache Freemake
|
||||||
|
*/
|
||||||
|
private void setupConfiguration() {
|
||||||
|
configuration = new Configuration(Configuration.VERSION_2_3_26);
|
||||||
|
|
||||||
|
try {
|
||||||
|
configuration.setClassForTemplateLoading(getClass(), XML_TEMPLATE_DIR);
|
||||||
|
} catch (NullPointerException e){
|
||||||
|
System.out.println("[FATAL] Server could not load XML Template directory, ensure this directory isn't empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the XML Generator
|
||||||
|
*/
|
||||||
|
public XMLGenerator(){
|
||||||
|
setupConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the race regatta to send to players
|
||||||
|
* Note: This must be set before a regatta message can be generated
|
||||||
|
* @param regatta The race regatta
|
||||||
|
*/
|
||||||
|
public void setRegatta(Regatta regatta){
|
||||||
|
this.regatta = regatta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the race to send to players
|
||||||
|
* Note: This must be set before a boat or race message can be generated
|
||||||
|
* @param race The race
|
||||||
|
*/
|
||||||
|
public void setRace(Race race){
|
||||||
|
this.race = race;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an XML template and generate the output as a string
|
||||||
|
* @param templateName The templates file name
|
||||||
|
* @param type The XML message sub type
|
||||||
|
*/
|
||||||
|
private String parseToXmlString(String templateName, XMLMessageSubType type) throws IOException, TemplateException {
|
||||||
|
Template template;
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
OutputStreamWriter writer = new OutputStreamWriter(os);
|
||||||
|
|
||||||
|
template = configuration.getTemplate(templateName);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case REGATTA:
|
||||||
|
template.process(regatta, writer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOAT:
|
||||||
|
template.process(race, writer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RACE:
|
||||||
|
template.process(race, writer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return os.toString("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
System.out.println("[FATAL] UTF-8 Not supported");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the race regatta as a string
|
||||||
|
* Note: Regatta must be set before calling this
|
||||||
|
* @return String containing the regatta XML, null if there was an error
|
||||||
|
*/
|
||||||
|
public String getRegattaAsXml(){
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
if (regatta == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = parseToXmlString(REGATTA_TEMPLATE_NAME, XMLMessageSubType.REGATTA);
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
System.out.println("[FATAL] Error parsing regatta");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[FATAL] Error reading regatta");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the boats XML as a string
|
||||||
|
* Note: Race must be set before calling this
|
||||||
|
* @return String containing the boats XML, null if there was an error
|
||||||
|
*/
|
||||||
|
public String getBoatsAsXml() {
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
if (race == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = parseToXmlString(BOATS_TEMPLATE_NAME, XMLMessageSubType.BOAT);
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
System.out.println("[FATAL] Error parsing boats");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[FATAL] Error reading boats");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the race XML as a string
|
||||||
|
* Note: Race must be set before calling this
|
||||||
|
* @return String containing the race XML, null if there was an error
|
||||||
|
*/
|
||||||
|
public String getRaceAsXml() {
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
if (race == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = parseToXmlString(RACE_TEMPLATE_NAME, XMLMessageSubType.RACE);
|
||||||
|
} catch (TemplateException e) {
|
||||||
|
System.out.println("[FATAL] Error parsing race");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("[FATAL] Error reading race");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
package seng302.server;
|
|
||||||
|
|
||||||
import seng302.server.messages.*;
|
|
||||||
import seng302.server.simulator.Boat;
|
|
||||||
import seng302.server.simulator.Simulator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class ServerThread implements Runnable, Observer {
|
|
||||||
private StreamingServerSocket server;
|
|
||||||
private long startTime;
|
|
||||||
private boolean raceStarted = false;
|
|
||||||
private Map<Integer,Boolean> boatsFinished = new HashMap<>();
|
|
||||||
private List<Boat> boats;
|
|
||||||
private Simulator raceSimulator;
|
|
||||||
private boolean sendingRaceFinishedLocationMessages = true;
|
|
||||||
|
|
||||||
private final int HEARTBEAT_PERIOD = 5000;
|
|
||||||
private final int RACE_STATUS_PERIOD = 1000/2;
|
|
||||||
private final int RACE_START_STATUS_PERIOD = 1000;
|
|
||||||
private final int BOAT_LOCATION_PERIOD = 1000/5;
|
|
||||||
private final int PORT_NUMBER = 4949;
|
|
||||||
private final int TIME_TILL_RACE_START = 20*1000;
|
|
||||||
private static final int LOG_LEVEL = 1;
|
|
||||||
|
|
||||||
public ServerThread(String threadName){
|
|
||||||
Thread runner = new Thread(this, threadName);
|
|
||||||
runner.setDaemon(true);
|
|
||||||
|
|
||||||
raceSimulator = new Simulator(BOAT_LOCATION_PERIOD);
|
|
||||||
raceSimulator.addObserver(this);
|
|
||||||
// run race simulator, so it can send boats' static location.
|
|
||||||
Thread raceSimulatorThread = new Thread(raceSimulator, "Race Simulator");
|
|
||||||
|
|
||||||
boats = raceSimulator.getBoats();
|
|
||||||
|
|
||||||
for (Boat b : boats){
|
|
||||||
boatsFinished.put(b.getSourceID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
runner.start();
|
|
||||||
raceSimulatorThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void serverLog(String message, int logLevel){
|
|
||||||
if(logLevel <= LOG_LEVEL){
|
|
||||||
System.out.println("[SERVER] " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns an XML Message from the file specified
|
|
||||||
* @param fileName The source XML file
|
|
||||||
* @param type The XML Message type
|
|
||||||
* @return The XML Message
|
|
||||||
*/
|
|
||||||
private Message getXmlMessage(String fileName, XMLMessageSubType type){
|
|
||||||
String fileContents = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
InputStream thisStream = this.getClass().getResourceAsStream(fileName);
|
|
||||||
fileContents = new String(org.apache.commons.io.IOUtils.toByteArray(thisStream));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NullPointerException e){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileContents != null){
|
|
||||||
return new XMLMessage(fileContents, type, server.getSequenceNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Get a race status message for the current race
|
|
||||||
*/
|
|
||||||
private Message getRaceStatusMessage(){
|
|
||||||
List<BoatSubMessage> boatSubMessages = new ArrayList<>();
|
|
||||||
BoatStatus boatStatus;
|
|
||||||
RaceStatus raceStatus;
|
|
||||||
boolean thereAreBoatsNotFinished = false;
|
|
||||||
|
|
||||||
for (Boat b : boats){
|
|
||||||
if (!raceStarted){
|
|
||||||
boatStatus = BoatStatus.PRESTART;
|
|
||||||
thereAreBoatsNotFinished = true;
|
|
||||||
}
|
|
||||||
else if(boatsFinished.get(b.getSourceID())){
|
|
||||||
boatStatus = BoatStatus.FINISHED;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
boatStatus = BoatStatus.PRESTART;
|
|
||||||
thereAreBoatsNotFinished = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
BoatSubMessage m = new BoatSubMessage(b.getSourceID(), boatStatus, b.getLastPassedCorner().getSeqID(), 0, 0, b.getEstimatedTimeTillFinish(), b.getEstimatedTimeTillFinish());
|
|
||||||
boatSubMessages.add(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thereAreBoatsNotFinished){
|
|
||||||
if (raceStarted){
|
|
||||||
raceStatus = RaceStatus.STARTED;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
long timeDifference = startTime - currentTime;
|
|
||||||
|
|
||||||
if (timeDifference > 60*3){
|
|
||||||
raceStatus = RaceStatus.PRESTART;
|
|
||||||
}
|
|
||||||
else if (timeDifference > 60){
|
|
||||||
raceStatus = RaceStatus.WARNING;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
raceStatus = RaceStatus.PREPARATORY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
raceStatus = RaceStatus.TERMINATED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH,
|
|
||||||
100, boats.size(), RaceType.MATCH_RACE, 1, boatSubMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts an instance of the race simulator
|
|
||||||
*/
|
|
||||||
private void startRaceSim(){
|
|
||||||
// set race started to true, so the simulator will start moving boats
|
|
||||||
raceSimulator.setRaceStarted(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts sending heartbeat messages to the client
|
|
||||||
*/
|
|
||||||
private void startSendingHeartbeats() {
|
|
||||||
Timer t = new Timer();
|
|
||||||
|
|
||||||
t.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Message heartbeat = new Heartbeat(server.getSequenceNumber());
|
|
||||||
|
|
||||||
try {
|
|
||||||
server.send(heartbeat);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, HEARTBEAT_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start sending race start status messages until race starts
|
|
||||||
*/
|
|
||||||
private void startSendingRaceStartStatusMessages(){
|
|
||||||
Timer t = new Timer();
|
|
||||||
t.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Message raceStartStatusMessage = new RaceStartStatusMessage(server.getSequenceNumber(), startTime , 1,
|
|
||||||
RaceStartNotificationType.SET_RACE_START_TIME);
|
|
||||||
try {
|
|
||||||
if (startTime < System.currentTimeMillis() && !raceStarted){
|
|
||||||
startRaceSim();
|
|
||||||
raceStarted = true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
server.send(raceStartStatusMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, RACE_START_STATUS_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start sending race start status messages until race starts
|
|
||||||
*/
|
|
||||||
private void startSendingRaceStatusMessages(){
|
|
||||||
Timer t = new Timer();
|
|
||||||
t.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Message raceStatusMessage = getRaceStatusMessage();
|
|
||||||
try {
|
|
||||||
server.send(raceStatusMessage);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, RACE_STATUS_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the race, boat, and regatta XML files to the client
|
|
||||||
*/
|
|
||||||
private void sendXml(){
|
|
||||||
try{
|
|
||||||
Message raceData = getXmlMessage("/server_config/race.xml", XMLMessageSubType.RACE);
|
|
||||||
Message boatData = getXmlMessage("/server_config/boats.xml", XMLMessageSubType.BOAT);
|
|
||||||
Message regatta = getXmlMessage("/server_config/regatta.xml", XMLMessageSubType.REGATTA);
|
|
||||||
|
|
||||||
if (raceData != null){
|
|
||||||
server.send(raceData);
|
|
||||||
}
|
|
||||||
if (boatData != null){
|
|
||||||
server.send(boatData);
|
|
||||||
}
|
|
||||||
if (regatta != null){
|
|
||||||
server.send(regatta);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the post-start race course information
|
|
||||||
*/
|
|
||||||
private void sendPostStartCourseXml(){
|
|
||||||
Timer t = new Timer();
|
|
||||||
t.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE);
|
|
||||||
if (raceData != null) {
|
|
||||||
server.send(raceData);
|
|
||||||
}
|
|
||||||
}catch (IOException e) {
|
|
||||||
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},25000);
|
|
||||||
//Delays the new course xml data for 25 seconds so the boats are able to pass the starting line
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
try{
|
|
||||||
server = new StreamingServerSocket(PORT_NUMBER);
|
|
||||||
}
|
|
||||||
catch (IOException e){
|
|
||||||
serverLog("Failed to bind socket: " + e.getMessage(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for client to connect
|
|
||||||
server.start();
|
|
||||||
|
|
||||||
startTime = System.currentTimeMillis() + TIME_TILL_RACE_START;
|
|
||||||
|
|
||||||
startSendingHeartbeats();
|
|
||||||
sendXml();
|
|
||||||
startSendingRaceStartStatusMessages();
|
|
||||||
startSendingRaceStatusMessages();
|
|
||||||
sendPostStartCourseXml();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start sending static boat position updates when race has finished
|
|
||||||
*/
|
|
||||||
private void startSendingRaceFinishedBoatPositions(){
|
|
||||||
Timer t = new Timer();
|
|
||||||
t.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
for (Boat b : raceSimulator.getBoats()){
|
|
||||||
Message m = new BoatLocationMessage(b.getSourceID(), server.getSequenceNumber(), b.getLat(),
|
|
||||||
b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(),
|
|
||||||
((long) 0));
|
|
||||||
|
|
||||||
server.send(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, BOAT_LOCATION_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a boat location message when they are updated by the simulator
|
|
||||||
* @param o .
|
|
||||||
* @param arg .
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void update(Observable o, Object arg) {
|
|
||||||
// Only send if server started
|
|
||||||
// TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17
|
|
||||||
if(server == null || !server.isStarted()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numOfBoatsFinished = 0;
|
|
||||||
for (Boat boat : (List<Boat>) arg){
|
|
||||||
try {
|
|
||||||
if (boat.isFinished()) {
|
|
||||||
numOfBoatsFinished ++;
|
|
||||||
if (!boatsFinished.get(boat.getSourceID())) {
|
|
||||||
boatsFinished.put(boat.getSourceID(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(),
|
|
||||||
boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(),
|
|
||||||
((long) boat.getSpeed()));
|
|
||||||
server.send(m);
|
|
||||||
} catch (IOException e) {
|
|
||||||
serverLog("Couldn't send a boat status message", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (NullPointerException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numOfBoatsFinished == ((List<Boat>) arg).size()) {
|
|
||||||
startSendingRaceFinishedBoatPositions();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package seng302.server;
|
|
||||||
|
|
||||||
import seng302.server.messages.Message;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.ServerSocketChannel;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
class StreamingServerSocket {
|
|
||||||
private ServerSocketChannel socket;
|
|
||||||
private SocketChannel client;
|
|
||||||
private short seqNum;
|
|
||||||
private boolean isServerStarted;
|
|
||||||
|
|
||||||
StreamingServerSocket(int port) throws IOException{
|
|
||||||
socket = ServerSocketChannel.open();
|
|
||||||
socket.socket().bind(new InetSocketAddress("localhost", port));
|
|
||||||
//socket.setSoTimeout(10000);
|
|
||||||
seqNum = 0;
|
|
||||||
isServerStarted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void start(){
|
|
||||||
try {
|
|
||||||
client = socket.accept();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.getMessage();
|
|
||||||
}
|
|
||||||
if (client.socket() == null){
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
isServerStarted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void send(Message message) throws IOException{
|
|
||||||
if (client == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
message.send(client);
|
|
||||||
|
|
||||||
seqNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getSequenceNumber(){
|
|
||||||
return seqNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStarted(){
|
|
||||||
return isServerStarted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package seng302.server.messages;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by kre39 on 12/07/17.
|
||||||
|
*/
|
||||||
|
public class BoatActionMessage extends Message{
|
||||||
|
private final MessageType MESSAGE_TYPE = MessageType.BOAT_ACTION;
|
||||||
|
private final int MESSAGE_SIZE = 1;
|
||||||
|
private BoatActionType actionType;
|
||||||
|
|
||||||
|
public BoatActionMessage(BoatActionType actionType) {
|
||||||
|
this.actionType = actionType;
|
||||||
|
setHeader(new Header(MessageType.BOAT_ACTION, 0, (short) 1)); // the second variable is the source id
|
||||||
|
allocateBuffer();
|
||||||
|
writeHeaderToBuffer();
|
||||||
|
// Write message fields
|
||||||
|
putInt(actionType.getValue(), 1);
|
||||||
|
writeCRC();
|
||||||
|
rewind();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package seng302.server.messages;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by kre39 on 12/07/17.
|
||||||
|
*/
|
||||||
|
public enum BoatActionType {
|
||||||
|
|
||||||
|
VMG(1),
|
||||||
|
SAILS_IN(2),
|
||||||
|
SAILS_OUT(3),
|
||||||
|
TACK_GYBE(4),
|
||||||
|
UPWIND(5),
|
||||||
|
DOWNWIND(6);
|
||||||
|
|
||||||
|
private final int type;
|
||||||
|
private static final Map<Integer, BoatActionType> intToTypeMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (BoatActionType type : BoatActionType.values()) {
|
||||||
|
intToTypeMap.put(type.getValue(), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoatActionType(int type){
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoatActionType getType(int value) {
|
||||||
|
return intToTypeMap.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class BoatLocationMessage extends Message {
|
public class BoatLocationMessage extends Message {
|
||||||
private final int MESSAGE_SIZE = 56;
|
private final int MESSAGE_SIZE = 56;
|
||||||
@@ -39,7 +39,6 @@ public class BoatLocationMessage extends Message {
|
|||||||
* @param boatSpeed The boats speed
|
* @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){
|
||||||
boatSpeed /= 10;
|
|
||||||
messageVersionNumber = 1;
|
messageVersionNumber = 1;
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
@@ -64,6 +63,36 @@ public class BoatLocationMessage extends Message {
|
|||||||
this.rudderAngle = 0;
|
this.rudderAngle = 0;
|
||||||
|
|
||||||
setHeader(new Header(MessageType.BOAT_LOCATION, 1, (short) getSize()));
|
setHeader(new Header(MessageType.BOAT_LOCATION, 1, (short) getSize()));
|
||||||
|
allocateBuffer();
|
||||||
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
|
long headingToSend = (long)((heading/360.0) * 65535.0);
|
||||||
|
|
||||||
|
putByte((byte) messageVersionNumber);
|
||||||
|
putInt(time, 6);
|
||||||
|
putInt((int) sourceId, 4);
|
||||||
|
putUnsignedInt((int) sequenceNum, 4);
|
||||||
|
putByte((byte) deviceType.getCode());
|
||||||
|
putInt((int) latLonToBinaryPackedLong(latitude), 4);
|
||||||
|
putInt((int) latLonToBinaryPackedLong(longitude), 4);
|
||||||
|
putInt((int) altitude, 4);
|
||||||
|
putInt(headingToSend, 2);
|
||||||
|
putInt((int) pitch, 2);
|
||||||
|
putInt((int) roll, 2);
|
||||||
|
putInt((int) boatSpeed, 2);
|
||||||
|
putUnsignedInt((int) COG, 2);
|
||||||
|
putUnsignedInt((int) SOG, 2);
|
||||||
|
putUnsignedInt((int) apparentWindSpeed, 2);
|
||||||
|
putInt((int) apparentWindAngle, 2);
|
||||||
|
putUnsignedInt((int) trueWindSpeed, 2);
|
||||||
|
putUnsignedInt((int) trueWindDirection, 2);
|
||||||
|
putInt((int) trueWindAngle, 2);
|
||||||
|
putUnsignedInt((int) currentDrift, 2);
|
||||||
|
putUnsignedInt((int) currentSet, 2);
|
||||||
|
putInt((int) rudderAngle, 2);
|
||||||
|
|
||||||
|
writeCRC();
|
||||||
|
rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,41 +153,4 @@ public class BoatLocationMessage extends Message {
|
|||||||
public int getSize() {
|
public int getSize() {
|
||||||
return MESSAGE_SIZE;
|
return MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(SocketChannel outputStream) throws IOException{
|
|
||||||
allocateBuffer();
|
|
||||||
writeHeaderToBuffer();
|
|
||||||
|
|
||||||
long headingToSend = (long)((heading/360.0) * 65535.0);
|
|
||||||
|
|
||||||
putByte((byte) messageVersionNumber);
|
|
||||||
putInt(time, 6);
|
|
||||||
putInt((int) sourceId, 4);
|
|
||||||
putUnsignedInt((int) sequenceNum, 4);
|
|
||||||
putByte((byte) deviceType.getCode());
|
|
||||||
putInt((int) latLonToBinaryPackedLong(latitude), 4);
|
|
||||||
putInt((int) latLonToBinaryPackedLong(longitude), 4);
|
|
||||||
putInt((int) altitude, 4);
|
|
||||||
putInt(headingToSend, 2);
|
|
||||||
putInt((int) pitch, 2);
|
|
||||||
putInt((int) roll, 2);
|
|
||||||
putInt((int) boatSpeed, 2);
|
|
||||||
putUnsignedInt((int) COG, 2);
|
|
||||||
putUnsignedInt((int) SOG, 2);
|
|
||||||
putUnsignedInt((int) apparentWindSpeed, 2);
|
|
||||||
putInt((int) apparentWindAngle, 2);
|
|
||||||
putUnsignedInt((int) trueWindSpeed, 2);
|
|
||||||
putUnsignedInt((int) trueWindDirection, 2);
|
|
||||||
putInt((int) trueWindAngle, 2);
|
|
||||||
putUnsignedInt((int) currentDrift, 2);
|
|
||||||
putUnsignedInt((int) currentSet, 2);
|
|
||||||
putInt((int) rudderAngle, 2);
|
|
||||||
|
|
||||||
writeCRC();
|
|
||||||
rewind();
|
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package seng302.server.messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by kre39 on 20/07/17.
|
||||||
|
*/
|
||||||
|
public class ChatterMessage extends Message {
|
||||||
|
|
||||||
|
private final long MESSAGE_VERSION_NUMBER = 1;
|
||||||
|
private final int MESSAGE_SIZE = 3;
|
||||||
|
private int message_type;
|
||||||
|
private int message_size = 21;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public ChatterMessage(int message_type, int message_size, String message) {
|
||||||
|
this.message_type = message_type;
|
||||||
|
this.message_size = message_size;
|
||||||
|
this.message = message;
|
||||||
|
|
||||||
|
setHeader(new Header(MessageType.CHATTER_TEXT, 1, (short) getSize()));
|
||||||
|
allocateBuffer();
|
||||||
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
|
putByte((byte) MESSAGE_VERSION_NUMBER);
|
||||||
|
putInt(message_type, 1);
|
||||||
|
putInt(message_size, 1);
|
||||||
|
putBytes(message.getBytes());
|
||||||
|
|
||||||
|
writeCRC();
|
||||||
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_SIZE + message_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -43,10 +43,21 @@ public class Header {
|
|||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the buffer
|
||||||
|
*/
|
||||||
|
public void reset(){
|
||||||
|
buffPos = 0;
|
||||||
|
buff.clear();
|
||||||
|
buff.position(buffPos);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a ByteBuffer containing the message header
|
* @return a ByteBuffer containing the message header
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getByteBuffer(){
|
public ByteBuffer getByteBuffer(){
|
||||||
|
reset();
|
||||||
|
|
||||||
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte1).array(), syncByte1);
|
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte1).array(), syncByte1);
|
||||||
|
|
||||||
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte2).array(), syncByte2);
|
putInBuffer(ByteBuffer.allocate(1).put((byte)syncByte2).array(), syncByte2);
|
||||||
|
|||||||
@@ -1,32 +1,16 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
import java.util.zip.CRC32;
|
|
||||||
|
|
||||||
public class Heartbeat extends Message {
|
public class Heartbeat extends Message {
|
||||||
private final int MESSAGE_SIZE = 4;
|
private final int MESSAGE_SIZE = 4;
|
||||||
private int seqNo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Heartbeat from the AC35 Streaming data spec
|
* Heartbeat from the AC35 Streaming data spec
|
||||||
* @param seqNo Increment every time a message is sent
|
* @param seqNo Increment every time a message is sent
|
||||||
*/
|
*/
|
||||||
public Heartbeat(int seqNo){
|
public Heartbeat(int seqNo){
|
||||||
this.seqNo = seqNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return MESSAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(SocketChannel outputStream) throws IOException {
|
|
||||||
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
|
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
|
||||||
|
|
||||||
allocateBuffer();
|
allocateBuffer();
|
||||||
@@ -36,7 +20,11 @@ public class Heartbeat extends Message {
|
|||||||
|
|
||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channels;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
|
|
||||||
public class MarkRoundingMessage extends Message{
|
public class MarkRoundingMessage extends Message{
|
||||||
private final long MESSAGE_VERSION_NUMBER = 1;
|
private final long MESSAGE_VERSION_NUMBER = 1;
|
||||||
@@ -33,15 +30,6 @@ public class MarkRoundingMessage extends Message{
|
|||||||
this.markId = markId;
|
this.markId = markId;
|
||||||
|
|
||||||
setHeader(new Header(MessageType.MARK_ROUNDING, 1, (short) getSize()));
|
setHeader(new Header(MessageType.MARK_ROUNDING, 1, (short) getSize()));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return MESSAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(SocketChannel outputStream) throws IOException {
|
|
||||||
allocateBuffer();
|
allocateBuffer();
|
||||||
writeHeaderToBuffer();
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
@@ -56,7 +44,10 @@ public class MarkRoundingMessage extends Message{
|
|||||||
|
|
||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
@@ -33,11 +33,6 @@ public abstract class Message {
|
|||||||
*/
|
*/
|
||||||
public abstract int getSize();
|
public abstract int getSize();
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the message as through the outputStream
|
|
||||||
*/
|
|
||||||
public abstract void send(SocketChannel outputStream) throws IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate byte buffer to correct size
|
* Allocate byte buffer to correct size
|
||||||
*/
|
*/
|
||||||
@@ -45,6 +40,7 @@ public abstract class Message {
|
|||||||
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
|
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
bufferPosition = 0;
|
bufferPosition = 0;
|
||||||
|
buffer.position(bufferPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,10 +157,10 @@ public abstract class Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The current buffer
|
* @return The current buffer as a byte array
|
||||||
*/
|
*/
|
||||||
public ByteBuffer getBuffer(){
|
public byte[] getBuffer(){
|
||||||
return buffer;
|
return buffer.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,6 +189,25 @@ public abstract class Message {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* takes an array of up to 7 bytes in little endian format and
|
||||||
|
* returns a positive long constructed from the input bytes
|
||||||
|
*
|
||||||
|
* @return a positive long if there is less than 8 bytes -1 otherwise
|
||||||
|
*/
|
||||||
|
public static long bytesToLong(byte[] bytes){
|
||||||
|
long partialLong = 0;
|
||||||
|
int index = 0;
|
||||||
|
for (byte b: bytes){
|
||||||
|
if (index > 6){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return partialLong;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse an array of bytes
|
* Reverse an array of bytes
|
||||||
* @param data The byte[] to reverse
|
* @param data The byte[] to reverse
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ public enum MessageType {
|
|||||||
BOAT_LOCATION(37),
|
BOAT_LOCATION(37),
|
||||||
MARK_ROUNDING(38),
|
MARK_ROUNDING(38),
|
||||||
COURSE_WIND(44),
|
COURSE_WIND(44),
|
||||||
AVERAGE_WIND(47);
|
AVERAGE_WIND(47),
|
||||||
|
BOAT_ACTION(100);
|
||||||
|
|
||||||
private int code;
|
private int code;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channels;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
|
|
||||||
public class RaceStartStatusMessage extends Message {
|
public class RaceStartStatusMessage extends Message {
|
||||||
private final int MESSAGE_SIZE = 20;
|
private final int MESSAGE_SIZE = 20;
|
||||||
@@ -32,15 +29,6 @@ public class RaceStartStatusMessage extends Message {
|
|||||||
this.raceId = raceId;
|
this.raceId = raceId;
|
||||||
|
|
||||||
setHeader(new Header(MessageType.RACE_START_STATUS, 1, (short) getSize()));
|
setHeader(new Header(MessageType.RACE_START_STATUS, 1, (short) getSize()));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return MESSAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(SocketChannel outputStream) throws IOException {
|
|
||||||
allocateBuffer();
|
allocateBuffer();
|
||||||
writeHeaderToBuffer();
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
@@ -53,7 +41,11 @@ public class RaceStartStatusMessage extends Message {
|
|||||||
|
|
||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
@@ -9,12 +9,14 @@ public class RaceStatusMessage extends Message{
|
|||||||
private final MessageType MESSAGE_TYPE = MessageType.RACE_STATUS;
|
private final MessageType MESSAGE_TYPE = MessageType.RACE_STATUS;
|
||||||
private final int MESSAGE_VERSION = 2; //Always set to 1
|
private final int MESSAGE_VERSION = 2; //Always set to 1
|
||||||
private final int MESSAGE_BASE_SIZE = 24;
|
private final int MESSAGE_BASE_SIZE = 24;
|
||||||
|
private final double windDirFactor = 0x4000 / 90;
|
||||||
|
|
||||||
|
|
||||||
private long currentTime;
|
private long currentTime;
|
||||||
private long raceId;
|
private long raceId;
|
||||||
private RaceStatus raceStatus;
|
private RaceStatus raceStatus;
|
||||||
private long expectedStartTime;
|
private long expectedStartTime;
|
||||||
private WindDirection raceWindDirection;
|
private double raceWindDirection;
|
||||||
private long windSpeed;
|
private long windSpeed;
|
||||||
private long numBoatsInRace;
|
private long numBoatsInRace;
|
||||||
private RaceType raceType;
|
private RaceType raceType;
|
||||||
@@ -33,13 +35,13 @@ public class RaceStatusMessage extends Message{
|
|||||||
* @param sourceId The source of this message
|
* @param sourceId The source of this message
|
||||||
* @param boats A list of boat status sub messages
|
* @param boats A list of boat status sub messages
|
||||||
*/
|
*/
|
||||||
public RaceStatusMessage(long raceId, RaceStatus raceStatus, long expectedStartTime, WindDirection raceWindDirection,
|
public RaceStatusMessage(long raceId, RaceStatus raceStatus, long expectedStartTime, double raceWindDirection,
|
||||||
long windSpeed, long numBoatsInRace, RaceType raceType, long sourceId, List<BoatSubMessage> boats){
|
long windSpeed, long numBoatsInRace, RaceType raceType, long sourceId, List<BoatSubMessage> boats){
|
||||||
currentTime = System.currentTimeMillis();
|
currentTime = System.currentTimeMillis();
|
||||||
this.raceId = raceId;
|
this.raceId = raceId;
|
||||||
this.raceStatus = raceStatus;
|
this.raceStatus = raceStatus;
|
||||||
this.expectedStartTime = expectedStartTime;
|
this.expectedStartTime = expectedStartTime;
|
||||||
this.raceWindDirection = raceWindDirection;
|
this.raceWindDirection = raceWindDirection * windDirFactor;
|
||||||
this.windSpeed = windSpeed;
|
this.windSpeed = windSpeed;
|
||||||
this.numBoatsInRace = numBoatsInRace;
|
this.numBoatsInRace = numBoatsInRace;
|
||||||
this.raceType = raceType;
|
this.raceType = raceType;
|
||||||
@@ -47,22 +49,6 @@ public class RaceStatusMessage extends Message{
|
|||||||
crc = new CRC32();
|
crc = new CRC32();
|
||||||
|
|
||||||
setHeader(new Header(MESSAGE_TYPE, (int) sourceId, (short) getSize()));
|
setHeader(new Header(MESSAGE_TYPE, (int) sourceId, (short) getSize()));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the size of this message in bytes
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return MESSAGE_BASE_SIZE + (20 * ((int) numBoatsInRace));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send this message as a stream of bytes
|
|
||||||
* @param outputStream The output stream to send the message
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void send(SocketChannel outputStream) throws IOException {
|
|
||||||
allocateBuffer();
|
allocateBuffer();
|
||||||
writeHeaderToBuffer();
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
@@ -71,7 +57,7 @@ public class RaceStatusMessage extends Message{
|
|||||||
putInt((int) raceId, 4);
|
putInt((int) raceId, 4);
|
||||||
putByte((byte) raceStatus.getCode());
|
putByte((byte) raceStatus.getCode());
|
||||||
putInt(expectedStartTime, 6);
|
putInt(expectedStartTime, 6);
|
||||||
putInt((int) raceWindDirection.getCode(), 2);
|
putInt((int) this.raceWindDirection, 2);
|
||||||
putInt((int) windSpeed, 2);
|
putInt((int) windSpeed, 2);
|
||||||
putByte((byte) numBoatsInRace);
|
putByte((byte) numBoatsInRace);
|
||||||
putByte((byte) raceType.getCode());
|
putByte((byte) raceType.getCode());
|
||||||
@@ -82,7 +68,14 @@ public class RaceStatusMessage extends Message{
|
|||||||
|
|
||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
/**
|
||||||
|
* @return the size of this message in bytes
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return MESSAGE_BASE_SIZE + (20 * ((int) numBoatsInRace));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package seng302.server.messages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum containing the supported wind directions
|
|
||||||
*/
|
|
||||||
public enum WindDirection {
|
|
||||||
NORTH(0x0000L),
|
|
||||||
EAST(0x4000L),
|
|
||||||
SOUTH(0x8000L);
|
|
||||||
|
|
||||||
private long code;
|
|
||||||
|
|
||||||
WindDirection(long code) {
|
|
||||||
this.code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCode() {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.io.OutputStream;
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
import java.util.zip.CRC32;
|
|
||||||
|
|
||||||
public class XMLMessage extends Message{
|
public class XMLMessage extends Message{
|
||||||
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
|
private final MessageType MESSAGE_TYPE = MessageType.XML_MESSAGE;
|
||||||
@@ -35,20 +30,6 @@ public class XMLMessage extends Message{
|
|||||||
sequence = sequenceNum;
|
sequence = sequenceNum;
|
||||||
|
|
||||||
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
|
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The length of this message
|
|
||||||
*/
|
|
||||||
public int getSize(){
|
|
||||||
return MESSAGE_SIZE + content.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send this message as a stream of bytes
|
|
||||||
* @param outputStream The output stream to send the message
|
|
||||||
*/
|
|
||||||
public void send(SocketChannel outputStream) throws IOException {
|
|
||||||
allocateBuffer();
|
allocateBuffer();
|
||||||
writeHeaderToBuffer();
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
@@ -63,7 +44,12 @@ public class XMLMessage extends Message{
|
|||||||
|
|
||||||
writeCRC();
|
writeCRC();
|
||||||
rewind();
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.write(getBuffer());
|
/**
|
||||||
|
* @return The length of this message
|
||||||
|
*/
|
||||||
|
public int getSize(){
|
||||||
|
return MESSAGE_SIZE + content.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package seng302.server.simulator;
|
package seng302.server.simulator;
|
||||||
|
|
||||||
import seng302.server.simulator.mark.Corner;
|
import seng302.server.simulator.mark.Corner;
|
||||||
import seng302.server.simulator.mark.Position;
|
import seng302.utilities.GeoPoint;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
public class Boat {
|
public class Boat {
|
||||||
|
|
||||||
@@ -29,8 +30,8 @@ public class Boat {
|
|||||||
*/
|
*/
|
||||||
public void move(double heading, double duration) {
|
public void move(double heading, double duration) {
|
||||||
Double distance = speed * duration / 1000000; // convert mm to meter
|
Double distance = speed * duration / 1000000; // convert mm to meter
|
||||||
Position originPos = new Position(lat, lng);
|
GeoPoint originPos = new GeoPoint(lat, lng);
|
||||||
Position newPos = GeoUtility.getGeoCoordinate(originPos, heading, distance);
|
GeoPoint newPos = GeoUtility.getGeoCoordinate(originPos, heading, distance);
|
||||||
this.lat = newPos.getLat();
|
this.lat = newPos.getLat();
|
||||||
this.lng = newPos.getLng();
|
this.lng = newPos.getLng();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package seng302.server.simulator;
|
|||||||
|
|
||||||
import seng302.server.simulator.mark.Corner;
|
import seng302.server.simulator.mark.Corner;
|
||||||
import seng302.server.simulator.mark.Mark;
|
import seng302.server.simulator.mark.Mark;
|
||||||
import seng302.server.simulator.mark.Position;
|
|
||||||
import seng302.server.simulator.parsers.RaceParser;
|
import seng302.server.simulator.parsers.RaceParser;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Observable;
|
import java.util.Observable;
|
||||||
@@ -61,7 +62,7 @@ public class Simulator extends Observable implements Runnable {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(lapse);
|
Thread.sleep(lapse);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
System.out.println("[Simulator] interrupted exception ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,8 +78,8 @@ public class Simulator extends Observable implements Runnable {
|
|||||||
|
|
||||||
boat.move(boat.getLastPassedCorner().getBearingToNextCorner(), duration);
|
boat.move(boat.getLastPassedCorner().getBearingToNextCorner(), duration);
|
||||||
|
|
||||||
Position boatPos = new Position(boat.getLat(), boat.getLng());
|
GeoPoint boatPos = new GeoPoint(boat.getLat(), boat.getLng());
|
||||||
Position lastMarkPos = boat.getLastPassedCorner().getCompoundMark().getMark1();
|
GeoPoint lastMarkPos = boat.getLastPassedCorner().getCompoundMark().getMark1();
|
||||||
|
|
||||||
double distanceFromLastMark = GeoUtility.getDistance(boatPos, lastMarkPos);
|
double distanceFromLastMark = GeoUtility.getDistance(boatPos, lastMarkPos);
|
||||||
// if a boat passes its heading mark
|
// if a boat passes its heading mark
|
||||||
@@ -94,13 +95,13 @@ public class Simulator extends Observable implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move compensate distance for the mark just passed
|
// move compensate distance for the mark just passed
|
||||||
Position pos = GeoUtility.getGeoCoordinate(
|
GeoPoint pos = GeoUtility.getGeoCoordinate(
|
||||||
boat.getLastPassedCorner().getCompoundMark().getMark1(),
|
boat.getLastPassedCorner().getCompoundMark().getMark1(),
|
||||||
boat.getLastPassedCorner().getBearingToNextCorner(),
|
boat.getLastPassedCorner().getBearingToNextCorner(),
|
||||||
compensateDistance);
|
compensateDistance);
|
||||||
boat.setLat(pos.getLat());
|
boat.setLat(pos.getLat());
|
||||||
boat.setLng(pos.getLng());
|
boat.setLng(pos.getLng());
|
||||||
distanceFromLastMark = GeoUtility.getDistance(new Position(boat.getLat(), boat.getLng()),
|
distanceFromLastMark = GeoUtility.getDistance(new GeoPoint(boat.getLat(), boat.getLng()),
|
||||||
boat.getLastPassedCorner().getCompoundMark().getMark1());
|
boat.getLastPassedCorner().getCompoundMark().getMark1());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package seng302.server.simulator.mark;
|
package seng302.server.simulator.mark;
|
||||||
|
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class to represent general marks
|
* An abstract class to represent general marks
|
||||||
* Created by Haoming Yin (hyi25) on 17/3/17.
|
* Created by Haoming Yin (hyi25) on 17/3/17.
|
||||||
*/
|
*/
|
||||||
public class Mark extends Position {
|
public class Mark extends GeoPoint {
|
||||||
|
|
||||||
private int seqID;
|
private int seqID;
|
||||||
private String name;
|
private String name;
|
||||||
@@ -22,7 +24,7 @@ public class Mark extends Position {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("Mark%d: %s, source: %d, lat: %f, lng: %f", seqID, name, sourceID, lat, lng);
|
return String.format("Mark%d: %s, source: %d, lat: %f, lng: %f", seqID, name, sourceID, getLat(), getLng());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSeqID() {
|
public int getSeqID() {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public abstract class FileParser {
|
|||||||
doc.getDocumentElement().normalize();
|
doc.getDocumentElement().normalize();
|
||||||
return doc;
|
return doc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
System.out.println("[FileParser] Exception");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ public abstract class FileParser {
|
|||||||
doc.getDocumentElement().normalize();
|
doc.getDocumentElement().normalize();
|
||||||
return doc;
|
return doc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
System.out.println("[FileParser] Exception");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package seng302.server.simulator.mark;
|
package seng302.utilities;
|
||||||
|
|
||||||
public class Position {
|
/**
|
||||||
|
* A class represent Geo location (latitude, longitude).
|
||||||
|
* Created by Haoming on 15/5/2017
|
||||||
|
*/
|
||||||
|
public class GeoPoint {
|
||||||
|
|
||||||
double lat, lng;
|
double lat, lng;
|
||||||
|
|
||||||
public Position(double lat, double lng) {
|
public GeoPoint(double lat, double lng) {
|
||||||
this.lat = lat;
|
this.lat = lat;
|
||||||
this.lng = lng;
|
this.lng = lng;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return String.format("Position at lat:%f lng:%f.", lat, lng);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLat() {
|
public double getLat() {
|
||||||
return lat;
|
return lat;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package seng302.server.simulator;
|
package seng302.utilities;
|
||||||
|
|
||||||
import seng302.server.simulator.mark.Position;
|
import javafx.geometry.Point2D;
|
||||||
|
|
||||||
public class GeoUtility {
|
public class GeoUtility {
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ public class GeoUtility {
|
|||||||
* @param p2 second geographical position
|
* @param p2 second geographical position
|
||||||
* @return the distance in meter between two points in meters
|
* @return the distance in meter between two points in meters
|
||||||
*/
|
*/
|
||||||
public static Double getDistance(Position p1, Position p2) {
|
public static Double getDistance(GeoPoint p1, GeoPoint p2) {
|
||||||
|
|
||||||
double dLat = Math.toRadians(p2.getLat() - p1.getLat());
|
double dLat = Math.toRadians(p2.getLat() - p1.getLat());
|
||||||
double dLon = Math.toRadians(p2.getLng() - p1.getLng());
|
double dLon = Math.toRadians(p2.getLng() - p1.getLng());
|
||||||
@@ -42,7 +42,7 @@ public class GeoUtility {
|
|||||||
* (≈ Baghdad) to 35°N,135°E (≈ Osaka), you would start on a heading of 60°
|
* (≈ Baghdad) to 35°N,135°E (≈ Osaka), you would start on a heading of 60°
|
||||||
* and end up on a heading of 120°
|
* and end up on a heading of 120°
|
||||||
*/
|
*/
|
||||||
public static Double getBearing(Position p1, Position p2) {
|
public static Double getBearing(GeoPoint p1, GeoPoint p2) {
|
||||||
|
|
||||||
double dLon = Math.toRadians(p2.getLng() - p1.getLng());
|
double dLon = Math.toRadians(p2.getLng() - p1.getLng());
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class GeoUtility {
|
|||||||
* @param distance the distance in meter, from original position to the new position
|
* @param distance the distance in meter, from original position to the new position
|
||||||
* @return the new position
|
* @return the new position
|
||||||
*/
|
*/
|
||||||
public static Position getGeoCoordinate(Position origin, Double bearing, Double distance) {
|
public static GeoPoint getGeoCoordinate(GeoPoint origin, Double bearing, Double distance) {
|
||||||
double b = Math.toRadians(bearing); // bearing to radians
|
double b = Math.toRadians(bearing); // bearing to radians
|
||||||
double d = distance / 1000.0; // distance to km
|
double d = distance / 1000.0; // distance to km
|
||||||
|
|
||||||
@@ -77,6 +77,57 @@ public class GeoUtility {
|
|||||||
+ Math.atan2(Math.sin(b) * Math.sin(d / EARTH_RADIUS) * Math.cos(originLat),
|
+ Math.atan2(Math.sin(b) * Math.sin(d / EARTH_RADIUS) * Math.cos(originLat),
|
||||||
Math.cos(d / EARTH_RADIUS) - Math.sin(originLat) * Math.sin(endLat));
|
Math.cos(d / EARTH_RADIUS) - Math.sin(originLat) * Math.sin(endLat));
|
||||||
|
|
||||||
return new Position(Math.toDegrees(endLat), Math.toDegrees(endLng));
|
return new GeoPoint(Math.toDegrees(endLat), Math.toDegrees(endLng));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the line function on two points of a line and a test point to test which side of the line that point is
|
||||||
|
* on. If the return value is
|
||||||
|
* return 1, then the point is on one side of the line,
|
||||||
|
* return -1 then the point is on the other side of the line
|
||||||
|
* return 0 then the point is exactly on the line.
|
||||||
|
* @param linePoint1 One point of the line
|
||||||
|
* @param linePoint2 Second point of the line
|
||||||
|
* @param testPoint The point to test with this line
|
||||||
|
* @return A return value indicating which side of the line the point is on
|
||||||
|
*/
|
||||||
|
public static Integer lineFunction(Point2D linePoint1, Point2D linePoint2, Point2D testPoint) {
|
||||||
|
|
||||||
|
Double x = testPoint.getX();
|
||||||
|
Double y = testPoint.getY();
|
||||||
|
Double x1 = linePoint1.getX();
|
||||||
|
Double y1 = linePoint1.getY();
|
||||||
|
Double x2 = linePoint2.getX();
|
||||||
|
Double y2 = linePoint2.getY();
|
||||||
|
|
||||||
|
Double result = (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1); //Line function
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (result < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a point and a vector (angle and vector length) Will create a new point, that vector away from the origin
|
||||||
|
* point
|
||||||
|
* @param originPoint The point with which to use as the base for our vector addition
|
||||||
|
* @param angleInDeg (DEGREES) The angle at which our new point is being created (in degrees!)
|
||||||
|
* @param vectorLength The length out on this angle from the origin point to create the new point
|
||||||
|
* @return a Point2D
|
||||||
|
*/
|
||||||
|
public static Point2D makeArbitraryVectorPoint(Point2D originPoint, Double angleInDeg, Double vectorLength) {
|
||||||
|
|
||||||
|
Double endPointX = originPoint.getX() + vectorLength * Math.cos(Math.toRadians(angleInDeg));
|
||||||
|
Double endPointY = originPoint.getY() + vectorLength * Math.sin(Math.toRadians(angleInDeg));
|
||||||
|
|
||||||
|
return new Point2D(endPointX, endPointY);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -12,8 +12,8 @@
|
|||||||
</mark>
|
</mark>
|
||||||
<mark>
|
<mark>
|
||||||
<name>Start2</name>
|
<name>Start2</name>
|
||||||
<latitude>57.6706330</latitude>
|
<latitude>57.6703330</latitude>
|
||||||
<longitude>11.8281330</longitude>
|
<longitude>11.8271333</longitude>
|
||||||
<id>123</id>
|
<id>123</id>
|
||||||
</mark>
|
</mark>
|
||||||
</gate>
|
</gate>
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ Remove scroll bars
|
|||||||
|
|
||||||
.ui-table *.scroll-bar:vertical *.increment-arrow,
|
.ui-table *.scroll-bar:vertical *.increment-arrow,
|
||||||
.ui-table *.scroll-bar:vertical *.decrement-arrow {
|
.ui-table *.scroll-bar:vertical *.decrement-arrow {
|
||||||
-fx-background-color: null;
|
-fx-background-color: #0e6d6c;
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
@@ -177,7 +177,7 @@ Remove scroll bars
|
|||||||
|
|
||||||
.ui-table *.scroll-bar:vertical *.increment-button,
|
.ui-table *.scroll-bar:vertical *.increment-button,
|
||||||
.ui-table *.scroll-bar:vertical *.decrement-button {
|
.ui-table *.scroll-bar:vertical *.decrement-button {
|
||||||
-fx-background-color: null;
|
-fx-background-color: #0e6d6c;
|
||||||
-fx-background-radius: 0;
|
-fx-background-radius: 0;
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<BoatConfig>
|
||||||
|
<Modified>2015-08-28T17:32:59+0100</Modified>
|
||||||
|
<Version>12</Version>
|
||||||
|
<Snapshot>219</Snapshot>
|
||||||
|
<Settings>
|
||||||
|
<RaceBoatType Type="AC45"/>
|
||||||
|
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
|
||||||
|
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
|
||||||
|
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
|
||||||
|
</Settings>
|
||||||
|
<BoatShapes>
|
||||||
|
<BoatShape ShapeID="0">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="3" Y="25" X="0"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="14">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1"/>
|
||||||
|
<Vtx Seq="2" Y="0.75" X="-1"/>
|
||||||
|
<Vtx Seq="3" Y="0.75" X="-0.25"/>
|
||||||
|
<Vtx Seq="4" Y="3.5" X="-0.25"/>
|
||||||
|
<Vtx Seq="5" Y="4.5" X="-1"/>
|
||||||
|
<Vtx Seq="6" Y="6.5" X="-1"/>
|
||||||
|
<Vtx Seq="7" Y="7" X="-0.5"/>
|
||||||
|
<Vtx Seq="8" Y="7" X="0.5"/>
|
||||||
|
<Vtx Seq="9" Y="6.5" X="1"/>
|
||||||
|
<Vtx Seq="10" Y="4.5" X="1"/>
|
||||||
|
<Vtx Seq="11" Y="3.5" X="0.25"/>
|
||||||
|
<Vtx Seq="12" Y="0.75" X="0.25"/>
|
||||||
|
<Vtx Seq="13" Y="0.75" X="1"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="1"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="15">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-3.46"/>
|
||||||
|
<Vtx Seq="2" Y="13.449" X="-3.46"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="13.449" X="3.46"/>
|
||||||
|
<Vtx Seq="5" Y="0" X="3.46"/>
|
||||||
|
</Vertices>
|
||||||
|
<Catamaran>
|
||||||
|
<Vtx Seq="1" Y="1.769" X="-2.752"/>
|
||||||
|
<Vtx Seq="2" Y="0" X="-2.813"/>
|
||||||
|
<Vtx Seq="3" Y="0" X="-3.34"/>
|
||||||
|
<Vtx Seq="4" Y="5.351" X="-3.46"/>
|
||||||
|
<Vtx Seq="5" Y="10.544" X="-3.387"/>
|
||||||
|
<Vtx Seq="6" Y="13.449" X="-3.075"/>
|
||||||
|
<Vtx Seq="7" Y="10.851" X="-2.793"/>
|
||||||
|
<Vtx Seq="8" Y="6.669" X="-2.699"/>
|
||||||
|
<Vtx Seq="9" Y="6.669" X="2.699"/>
|
||||||
|
<Vtx Seq="10" Y="10.851" X="2.793"/>
|
||||||
|
<Vtx Seq="11" Y="13.449" X="3.075"/>
|
||||||
|
<Vtx Seq="12" Y="10.544" X="3.387"/>
|
||||||
|
<Vtx Seq="13" Y="5.351" X="3.46"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="3.34"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="2.813"/>
|
||||||
|
<Vtx Seq="16" Y="1.769" X="2.752"/>
|
||||||
|
</Catamaran>
|
||||||
|
<Bowsprit>
|
||||||
|
<Vtx Seq="1" Y="6.669" X="-0.2"/>
|
||||||
|
<Vtx Seq="2" Y="11.377" X="-0.2"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="11.377" X="0.2"/>
|
||||||
|
<Vtx Seq="5" Y="6.669" X="0.2"/>
|
||||||
|
</Bowsprit>
|
||||||
|
<Trampoline>
|
||||||
|
<Vtx Seq="1" Y="2" X="-2.699"/>
|
||||||
|
<Vtx Seq="2" Y="6.438" X="-2.699"/>
|
||||||
|
<Vtx Seq="3" Y="6.438" X="2.699"/>
|
||||||
|
<Vtx Seq="4" Y="2" X="2.699"/>
|
||||||
|
</Trampoline>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="18">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.04"/>
|
||||||
|
<Vtx Seq="2" Y="0.11" X="-1.18"/>
|
||||||
|
<Vtx Seq="3" Y="0.42" X="-1.28"/>
|
||||||
|
<Vtx Seq="4" Y="3.74" X="-1.29"/>
|
||||||
|
<Vtx Seq="5" Y="5.36" X="-1.21"/>
|
||||||
|
<Vtx Seq="6" Y="6.29" X="-1.08"/>
|
||||||
|
<Vtx Seq="7" Y="7.15" X="-0.84"/>
|
||||||
|
<Vtx Seq="8" Y="7.63" X="-0.62"/>
|
||||||
|
<Vtx Seq="9" Y="7.94" X="-0.34"/>
|
||||||
|
<Vtx Seq="10" Y="8.06" X="0"/>
|
||||||
|
<Vtx Seq="11" Y="7.94" X="0.34"/>
|
||||||
|
<Vtx Seq="12" Y="7.63" X="0.62"/>
|
||||||
|
<Vtx Seq="13" Y="7.15" X="0.84"/>
|
||||||
|
<Vtx Seq="14" Y="6.29" X="1.08"/>
|
||||||
|
<Vtx Seq="15" Y="5.36" X="1.21"/>
|
||||||
|
<Vtx Seq="16" Y="3.74" X="1.29"/>
|
||||||
|
<Vtx Seq="17" Y="0.42" X="1.28"/>
|
||||||
|
<Vtx Seq="18" Y="0.11" X="1.18"/>
|
||||||
|
<Vtx Seq="19" Y="0" X="1.04"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="24">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-2.5"/>
|
||||||
|
<Vtx Seq="2" Y="7" X="-2.5"/>
|
||||||
|
<Vtx Seq="3" Y="12.6" X="-2.2"/>
|
||||||
|
<Vtx Seq="4" Y="12.6" X="2.2"/>
|
||||||
|
<Vtx Seq="5" Y="7" X="2.5"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="2.5"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="34">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.16"/>
|
||||||
|
<Vtx Seq="2" Y="5.51" X="-1.16"/>
|
||||||
|
<Vtx Seq="3" Y="5.846" X="-0.84"/>
|
||||||
|
<Vtx Seq="4" Y="5.846" X="0.84"/>
|
||||||
|
<Vtx Seq="5" Y="5.51" X="1.16"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="1.16"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="35">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.461"/>
|
||||||
|
<Vtx Seq="2" Y="6" X="-1.461"/>
|
||||||
|
<Vtx Seq="3" Y="7" X="-1.44"/>
|
||||||
|
<Vtx Seq="4" Y="8" X="-1.38"/>
|
||||||
|
<Vtx Seq="5" Y="9" X="-1.17"/>
|
||||||
|
<Vtx Seq="6" Y="10" X="-0.76"/>
|
||||||
|
<Vtx Seq="7" Y="10.6" X="-0.34"/>
|
||||||
|
<Vtx Seq="8" Y="10.61" X="0"/>
|
||||||
|
<Vtx Seq="9" Y="10.6" X="0.34"/>
|
||||||
|
<Vtx Seq="10" Y="10" X="0.76"/>
|
||||||
|
<Vtx Seq="11" Y="9" X="1.17"/>
|
||||||
|
<Vtx Seq="12" Y="8" X="1.38"/>
|
||||||
|
<Vtx Seq="13" Y="7" X="1.44"/>
|
||||||
|
<Vtx Seq="14" Y="6" X="1.461"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="1.461"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
</BoatShapes>
|
||||||
|
<Boats>
|
||||||
|
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
|
||||||
|
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="301" ShapeID="15" StoweName="SWE" ShortName="ARTEMIS" ShorterName="SWE" BoatName="ARTEMIS RACING" HullNum="AC4517" Skipper="OUTTERIDGE" Helmsman="OUTTERIDGE" Country="SWE" PeliID="102" RadioIP="172.20.2.102">
|
||||||
|
<GPSposition Z="1.727" Y="-0.359" X="-0.0121"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
|
||||||
|
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
|
||||||
|
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="601" ShapeID="15" StoweName="FRA" ShortName="FRANCE" ShorterName="FRA" BoatName="GROUPAMA TEAM FRANCE" HullNum="AC4505" Skipper="CAMMAS" Helmsman="CAMMAS" Country="FRA" PeliID="105" RadioIP="172.20.2.105">
|
||||||
|
<GPSposition Z="1.863" Y="-0.3" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
|
||||||
|
<GPSposition Z="1.734" Y="-0.352" X="0"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
</Boats>
|
||||||
|
</BoatConfig>
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<BoatConfig>
|
||||||
|
<Modified>2015-08-28T17:32:59+0100</Modified>
|
||||||
|
<Version>12</Version>
|
||||||
|
<Snapshot>219</Snapshot>
|
||||||
|
<Settings>
|
||||||
|
<RaceBoatType Type="AC45"/>
|
||||||
|
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
|
||||||
|
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
|
||||||
|
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
|
||||||
|
</Settings>
|
||||||
|
<BoatShapes>
|
||||||
|
<BoatShape ShapeID="0">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="3" Y="25" X="0"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="14">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1"/>
|
||||||
|
<Vtx Seq="2" Y="0.75" X="-1"/>
|
||||||
|
<Vtx Seq="3" Y="0.75" X="-0.25"/>
|
||||||
|
<Vtx Seq="4" Y="3.5" X="-0.25"/>
|
||||||
|
<Vtx Seq="5" Y="4.5" X="-1"/>
|
||||||
|
<Vtx Seq="6" Y="6.5" X="-1"/>
|
||||||
|
<Vtx Seq="7" Y="7" X="-0.5"/>
|
||||||
|
<Vtx Seq="8" Y="7" X="0.5"/>
|
||||||
|
<Vtx Seq="9" Y="6.5" X="1"/>
|
||||||
|
<Vtx Seq="10" Y="4.5" X="1"/>
|
||||||
|
<Vtx Seq="11" Y="3.5" X="0.25"/>
|
||||||
|
<Vtx Seq="12" Y="0.75" X="0.25"/>
|
||||||
|
<Vtx Seq="13" Y="0.75" X="1"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="1"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="15">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-3.46"/>
|
||||||
|
<Vtx Seq="2" Y="13.449" X="-3.46"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="13.449" X="3.46"/>
|
||||||
|
<Vtx Seq="5" Y="0" X="3.46"/>
|
||||||
|
</Vertices>
|
||||||
|
<Catamaran>
|
||||||
|
<Vtx Seq="1" Y="1.769" X="-2.752"/>
|
||||||
|
<Vtx Seq="2" Y="0" X="-2.813"/>
|
||||||
|
<Vtx Seq="3" Y="0" X="-3.34"/>
|
||||||
|
<Vtx Seq="4" Y="5.351" X="-3.46"/>
|
||||||
|
<Vtx Seq="5" Y="10.544" X="-3.387"/>
|
||||||
|
<Vtx Seq="6" Y="13.449" X="-3.075"/>
|
||||||
|
<Vtx Seq="7" Y="10.851" X="-2.793"/>
|
||||||
|
<Vtx Seq="8" Y="6.669" X="-2.699"/>
|
||||||
|
<Vtx Seq="9" Y="6.669" X="2.699"/>
|
||||||
|
<Vtx Seq="10" Y="10.851" X="2.793"/>
|
||||||
|
<Vtx Seq="11" Y="13.449" X="3.075"/>
|
||||||
|
<Vtx Seq="12" Y="10.544" X="3.387"/>
|
||||||
|
<Vtx Seq="13" Y="5.351" X="3.46"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="3.34"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="2.813"/>
|
||||||
|
<Vtx Seq="16" Y="1.769" X="2.752"/>
|
||||||
|
</Catamaran>
|
||||||
|
<Bowsprit>
|
||||||
|
<Vtx Seq="1" Y="6.669" X="-0.2"/>
|
||||||
|
<Vtx Seq="2" Y="11.377" X="-0.2"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="11.377" X="0.2"/>
|
||||||
|
<Vtx Seq="5" Y="6.669" X="0.2"/>
|
||||||
|
</Bowsprit>
|
||||||
|
<Trampoline>
|
||||||
|
<Vtx Seq="1" Y="2" X="-2.699"/>
|
||||||
|
<Vtx Seq="2" Y="6.438" X="-2.699"/>
|
||||||
|
<Vtx Seq="3" Y="6.438" X="2.699"/>
|
||||||
|
<Vtx Seq="4" Y="2" X="2.699"/>
|
||||||
|
</Trampoline>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="18">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.04"/>
|
||||||
|
<Vtx Seq="2" Y="0.11" X="-1.18"/>
|
||||||
|
<Vtx Seq="3" Y="0.42" X="-1.28"/>
|
||||||
|
<Vtx Seq="4" Y="3.74" X="-1.29"/>
|
||||||
|
<Vtx Seq="5" Y="5.36" X="-1.21"/>
|
||||||
|
<Vtx Seq="6" Y="6.29" X="-1.08"/>
|
||||||
|
<Vtx Seq="7" Y="7.15" X="-0.84"/>
|
||||||
|
<Vtx Seq="8" Y="7.63" X="-0.62"/>
|
||||||
|
<Vtx Seq="9" Y="7.94" X="-0.34"/>
|
||||||
|
<Vtx Seq="10" Y="8.06" X="0"/>
|
||||||
|
<Vtx Seq="11" Y="7.94" X="0.34"/>
|
||||||
|
<Vtx Seq="12" Y="7.63" X="0.62"/>
|
||||||
|
<Vtx Seq="13" Y="7.15" X="0.84"/>
|
||||||
|
<Vtx Seq="14" Y="6.29" X="1.08"/>
|
||||||
|
<Vtx Seq="15" Y="5.36" X="1.21"/>
|
||||||
|
<Vtx Seq="16" Y="3.74" X="1.29"/>
|
||||||
|
<Vtx Seq="17" Y="0.42" X="1.28"/>
|
||||||
|
<Vtx Seq="18" Y="0.11" X="1.18"/>
|
||||||
|
<Vtx Seq="19" Y="0" X="1.04"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="24">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-2.5"/>
|
||||||
|
<Vtx Seq="2" Y="7" X="-2.5"/>
|
||||||
|
<Vtx Seq="3" Y="12.6" X="-2.2"/>
|
||||||
|
<Vtx Seq="4" Y="12.6" X="2.2"/>
|
||||||
|
<Vtx Seq="5" Y="7" X="2.5"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="2.5"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="34">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.16"/>
|
||||||
|
<Vtx Seq="2" Y="5.51" X="-1.16"/>
|
||||||
|
<Vtx Seq="3" Y="5.846" X="-0.84"/>
|
||||||
|
<Vtx Seq="4" Y="5.846" X="0.84"/>
|
||||||
|
<Vtx Seq="5" Y="5.51" X="1.16"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="1.16"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="35">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.461"/>
|
||||||
|
<Vtx Seq="2" Y="6" X="-1.461"/>
|
||||||
|
<Vtx Seq="3" Y="7" X="-1.44"/>
|
||||||
|
<Vtx Seq="4" Y="8" X="-1.38"/>
|
||||||
|
<Vtx Seq="5" Y="9" X="-1.17"/>
|
||||||
|
<Vtx Seq="6" Y="10" X="-0.76"/>
|
||||||
|
<Vtx Seq="7" Y="10.6" X="-0.34"/>
|
||||||
|
<Vtx Seq="8" Y="10.61" X="0"/>
|
||||||
|
<Vtx Seq="9" Y="10.6" X="0.34"/>
|
||||||
|
<Vtx Seq="10" Y="10" X="0.76"/>
|
||||||
|
<Vtx Seq="11" Y="9" X="1.17"/>
|
||||||
|
<Vtx Seq="12" Y="8" X="1.38"/>
|
||||||
|
<Vtx Seq="13" Y="7" X="1.44"/>
|
||||||
|
<Vtx Seq="14" Y="6" X="1.461"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="1.461"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
</BoatShapes>
|
||||||
|
<Boats>
|
||||||
|
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
|
||||||
|
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
|
||||||
|
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
|
||||||
|
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
|
||||||
|
<GPSposition Z="1.734" Y="-0.352" X="0"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
</Boats>
|
||||||
|
</BoatConfig>
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<BoatConfig>
|
||||||
|
<Modified>2015-08-28T17:32:59+0100</Modified>
|
||||||
|
<Version>12</Version>
|
||||||
|
<Snapshot>219</Snapshot>
|
||||||
|
<Settings>
|
||||||
|
<RaceBoatType Type="AC45"/>
|
||||||
|
<BoatDimension BoatLength="14.019" HullLength="13.449"/>
|
||||||
|
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="53.796"/>
|
||||||
|
<ZoneLimits Limit1="200" Limit2="100" Limit3="53.796" Limit4="0" Limit5="-100"/>
|
||||||
|
</Settings>
|
||||||
|
<BoatShapes>
|
||||||
|
<BoatShape ShapeID="0">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="3" Y="25" X="0"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="14">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1"/>
|
||||||
|
<Vtx Seq="2" Y="0.75" X="-1"/>
|
||||||
|
<Vtx Seq="3" Y="0.75" X="-0.25"/>
|
||||||
|
<Vtx Seq="4" Y="3.5" X="-0.25"/>
|
||||||
|
<Vtx Seq="5" Y="4.5" X="-1"/>
|
||||||
|
<Vtx Seq="6" Y="6.5" X="-1"/>
|
||||||
|
<Vtx Seq="7" Y="7" X="-0.5"/>
|
||||||
|
<Vtx Seq="8" Y="7" X="0.5"/>
|
||||||
|
<Vtx Seq="9" Y="6.5" X="1"/>
|
||||||
|
<Vtx Seq="10" Y="4.5" X="1"/>
|
||||||
|
<Vtx Seq="11" Y="3.5" X="0.25"/>
|
||||||
|
<Vtx Seq="12" Y="0.75" X="0.25"/>
|
||||||
|
<Vtx Seq="13" Y="0.75" X="1"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="1"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="15">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-3.46"/>
|
||||||
|
<Vtx Seq="2" Y="13.449" X="-3.46"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="13.449" X="3.46"/>
|
||||||
|
<Vtx Seq="5" Y="0" X="3.46"/>
|
||||||
|
</Vertices>
|
||||||
|
<Catamaran>
|
||||||
|
<Vtx Seq="1" Y="1.769" X="-2.752"/>
|
||||||
|
<Vtx Seq="2" Y="0" X="-2.813"/>
|
||||||
|
<Vtx Seq="3" Y="0" X="-3.34"/>
|
||||||
|
<Vtx Seq="4" Y="5.351" X="-3.46"/>
|
||||||
|
<Vtx Seq="5" Y="10.544" X="-3.387"/>
|
||||||
|
<Vtx Seq="6" Y="13.449" X="-3.075"/>
|
||||||
|
<Vtx Seq="7" Y="10.851" X="-2.793"/>
|
||||||
|
<Vtx Seq="8" Y="6.669" X="-2.699"/>
|
||||||
|
<Vtx Seq="9" Y="6.669" X="2.699"/>
|
||||||
|
<Vtx Seq="10" Y="10.851" X="2.793"/>
|
||||||
|
<Vtx Seq="11" Y="13.449" X="3.075"/>
|
||||||
|
<Vtx Seq="12" Y="10.544" X="3.387"/>
|
||||||
|
<Vtx Seq="13" Y="5.351" X="3.46"/>
|
||||||
|
<Vtx Seq="14" Y="0" X="3.34"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="2.813"/>
|
||||||
|
<Vtx Seq="16" Y="1.769" X="2.752"/>
|
||||||
|
</Catamaran>
|
||||||
|
<Bowsprit>
|
||||||
|
<Vtx Seq="1" Y="6.669" X="-0.2"/>
|
||||||
|
<Vtx Seq="2" Y="11.377" X="-0.2"/>
|
||||||
|
<Vtx Seq="3" Y="14.019" X="0"/>
|
||||||
|
<Vtx Seq="4" Y="11.377" X="0.2"/>
|
||||||
|
<Vtx Seq="5" Y="6.669" X="0.2"/>
|
||||||
|
</Bowsprit>
|
||||||
|
<Trampoline>
|
||||||
|
<Vtx Seq="1" Y="2" X="-2.699"/>
|
||||||
|
<Vtx Seq="2" Y="6.438" X="-2.699"/>
|
||||||
|
<Vtx Seq="3" Y="6.438" X="2.699"/>
|
||||||
|
<Vtx Seq="4" Y="2" X="2.699"/>
|
||||||
|
</Trampoline>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="18">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.04"/>
|
||||||
|
<Vtx Seq="2" Y="0.11" X="-1.18"/>
|
||||||
|
<Vtx Seq="3" Y="0.42" X="-1.28"/>
|
||||||
|
<Vtx Seq="4" Y="3.74" X="-1.29"/>
|
||||||
|
<Vtx Seq="5" Y="5.36" X="-1.21"/>
|
||||||
|
<Vtx Seq="6" Y="6.29" X="-1.08"/>
|
||||||
|
<Vtx Seq="7" Y="7.15" X="-0.84"/>
|
||||||
|
<Vtx Seq="8" Y="7.63" X="-0.62"/>
|
||||||
|
<Vtx Seq="9" Y="7.94" X="-0.34"/>
|
||||||
|
<Vtx Seq="10" Y="8.06" X="0"/>
|
||||||
|
<Vtx Seq="11" Y="7.94" X="0.34"/>
|
||||||
|
<Vtx Seq="12" Y="7.63" X="0.62"/>
|
||||||
|
<Vtx Seq="13" Y="7.15" X="0.84"/>
|
||||||
|
<Vtx Seq="14" Y="6.29" X="1.08"/>
|
||||||
|
<Vtx Seq="15" Y="5.36" X="1.21"/>
|
||||||
|
<Vtx Seq="16" Y="3.74" X="1.29"/>
|
||||||
|
<Vtx Seq="17" Y="0.42" X="1.28"/>
|
||||||
|
<Vtx Seq="18" Y="0.11" X="1.18"/>
|
||||||
|
<Vtx Seq="19" Y="0" X="1.04"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="24">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-2.5"/>
|
||||||
|
<Vtx Seq="2" Y="7" X="-2.5"/>
|
||||||
|
<Vtx Seq="3" Y="12.6" X="-2.2"/>
|
||||||
|
<Vtx Seq="4" Y="12.6" X="2.2"/>
|
||||||
|
<Vtx Seq="5" Y="7" X="2.5"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="2.5"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="34">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.16"/>
|
||||||
|
<Vtx Seq="2" Y="5.51" X="-1.16"/>
|
||||||
|
<Vtx Seq="3" Y="5.846" X="-0.84"/>
|
||||||
|
<Vtx Seq="4" Y="5.846" X="0.84"/>
|
||||||
|
<Vtx Seq="5" Y="5.51" X="1.16"/>
|
||||||
|
<Vtx Seq="6" Y="0" X="1.16"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
<BoatShape ShapeID="35">
|
||||||
|
<Vertices>
|
||||||
|
<Vtx Seq="1" Y="0" X="-1.461"/>
|
||||||
|
<Vtx Seq="2" Y="6" X="-1.461"/>
|
||||||
|
<Vtx Seq="3" Y="7" X="-1.44"/>
|
||||||
|
<Vtx Seq="4" Y="8" X="-1.38"/>
|
||||||
|
<Vtx Seq="5" Y="9" X="-1.17"/>
|
||||||
|
<Vtx Seq="6" Y="10" X="-0.76"/>
|
||||||
|
<Vtx Seq="7" Y="10.6" X="-0.34"/>
|
||||||
|
<Vtx Seq="8" Y="10.61" X="0"/>
|
||||||
|
<Vtx Seq="9" Y="10.6" X="0.34"/>
|
||||||
|
<Vtx Seq="10" Y="10" X="0.76"/>
|
||||||
|
<Vtx Seq="11" Y="9" X="1.17"/>
|
||||||
|
<Vtx Seq="12" Y="8" X="1.38"/>
|
||||||
|
<Vtx Seq="13" Y="7" X="1.44"/>
|
||||||
|
<Vtx Seq="14" Y="6" X="1.461"/>
|
||||||
|
<Vtx Seq="15" Y="0" X="1.461"/>
|
||||||
|
</Vertices>
|
||||||
|
</BoatShape>
|
||||||
|
</BoatShapes>
|
||||||
|
<Boats>
|
||||||
|
<Boat Type="Yacht" SourceID="201" ShapeID="15" StoweName="USA" ShortName="ORACLE" ShorterName="USA" BoatName="ORACLE TEAM USA" HullNum="AC4515" Skipper="SPITHILL" Helmsman="SPITHILL" Country="USA" PeliID="101" RadioIP="172.20.2.101">
|
||||||
|
<GPSposition Z="1.78" Y="-0.331" X="-0.006"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="301" ShapeID="15" StoweName="SWE" ShortName="ARTEMIS" ShorterName="SWE" BoatName="ARTEMIS RACING" HullNum="AC4517" Skipper="OUTTERIDGE" Helmsman="OUTTERIDGE" Country="SWE" PeliID="102" RadioIP="172.20.2.102">
|
||||||
|
<GPSposition Z="1.727" Y="-0.359" X="-0.0121"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="401" ShapeID="15" StoweName="NZL" ShortName="ETNZ" ShorterName="NZL" BoatName="EMIRATES TEAM NZ" HullNum="AC4503" Skipper="ASHBY" Helmsman="BURLING" Country="NZL" PeliID="103" RadioIP="172.20.2.103">
|
||||||
|
<GPSposition Z="1.881" Y="-0.291" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="501" ShapeID="15" StoweName="JPN" ShortName="JAPAN" ShorterName="JPN" BoatName="SOFTBANK TEAM JAPAN" HullNum="AC4504" Skipper="BARKER" Helmsman="BARKER" Country="JPN" PeliID="104" RadioIP="172.20.2.104">
|
||||||
|
<GPSposition Z="1.805" Y="-0.322" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="601" ShapeID="15" StoweName="FRA" ShortName="FRANCE" ShorterName="FRA" BoatName="GROUPAMA TEAM FRANCE" HullNum="AC4505" Skipper="CAMMAS" Helmsman="CAMMAS" Country="FRA" PeliID="105" RadioIP="172.20.2.105">
|
||||||
|
<GPSposition Z="1.863" Y="-0.3" X="-0.003"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
<Boat Type="Yacht" SourceID="701" ShapeID="15" StoweName="GBR" ShortName="GBR" ShorterName="GBR" BoatName="LAND ROVER BAR" HullNum="AC4516" Skipper="ANSLIE" Helmsman="ANSLIE" Country="GBR" PeliID="106" RadioIP="172.20.2.106">
|
||||||
|
<GPSposition Z="1.734" Y="-0.352" X="0"/>
|
||||||
|
<MastTop Z="21.496" Y="3.7" X="0"/>
|
||||||
|
<FlagPosition Z="0" Y="6.2" X="0"/>
|
||||||
|
</Boat>
|
||||||
|
</Boats>
|
||||||
|
</BoatConfig>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<BoatConfig>
|
||||||
|
<Modified>2012-05-17T07:49:40+0200</Modified>
|
||||||
|
<Version>12</Version>
|
||||||
|
<Settings>
|
||||||
|
<RaceBoatType Type="AC45" />
|
||||||
|
<BoatDimension BoatLength="14.019" HullLength="13.449" />
|
||||||
|
<ZoneSize MarkZoneSize="40.347" CourseZoneSize="40.347" />
|
||||||
|
<ZoneLimits Limit1="200" Limit2="100" Limit3="40.347" Limit4="0" Limit5="-100" />
|
||||||
|
</Settings>
|
||||||
|
<BoatShapes>
|
||||||
|
<#-- Not used -->
|
||||||
|
</BoatShapes>
|
||||||
|
<Boats>
|
||||||
|
<#list boats as boat>
|
||||||
|
<Boat Type="Yacht" SourceID="${boat.sourceId}" ShapeID="4" HullNum="${boat.hullID}" StoweName="${boat.shortName}" ShortName="${boat.shortName}"
|
||||||
|
BoatName="${boat.boatName}" Country="${boat.country}">
|
||||||
|
<GPSposition Z="0" Y="3.7" X="0" />
|
||||||
|
<MastTop Z="0" Y="6.2" X="0" />
|
||||||
|
</Boat>
|
||||||
|
</#list>
|
||||||
|
</Boats>
|
||||||
|
</BoatConfig>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Race>
|
||||||
|
<CreationTimeDate>${raceStartTime}</CreationTimeDate>
|
||||||
|
<RaceStartTime Start="${raceStartTime}" Postpone="False" />
|
||||||
|
<RaceID>15082901</RaceID>
|
||||||
|
<RaceType>Fleet</RaceType>
|
||||||
|
|
||||||
|
<Participants>
|
||||||
|
<#list boats as boat>
|
||||||
|
<Yacht SourceID="${boat.sourceId}"/>
|
||||||
|
</#list>
|
||||||
|
</Participants>
|
||||||
|
|
||||||
|
<Course>
|
||||||
|
<CompoundMark CompoundMarkID="1" Name="Mark0">
|
||||||
|
<Mark SeqID="1" Name="Start Line 1" TargetLat="57.670603" TargetLng="11.828262" SourceID="122" />
|
||||||
|
<Mark SeqID="2" Name="Start Line 2" TargetLat="57.669445" TargetLng="11.826413" SourceID="123" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="2" Name="Mark1">
|
||||||
|
<Mark SeqID="1" Name="Mark1" TargetLat="57.6675700" TargetLng="11.8359880" SourceID="131" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="3" Name="Mark2">
|
||||||
|
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="124" />
|
||||||
|
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.671629" TargetLng="11.840951" SourceID="125" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="4" Name="Mark3">
|
||||||
|
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.665316" TargetLng="11.827184" SourceID="126" />
|
||||||
|
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.664190" TargetLng="11.829576" SourceID="127" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="5" Name="Mark2">
|
||||||
|
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="124" />
|
||||||
|
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="125" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="6" Name="Mark3">
|
||||||
|
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="126" />
|
||||||
|
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="127" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="7" Name="Mark2">
|
||||||
|
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="124" />
|
||||||
|
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="125" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="8" Name="Mark3">
|
||||||
|
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="126" />
|
||||||
|
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="127" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="9" Name="Mark2">
|
||||||
|
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="124" />
|
||||||
|
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900" SourceID="125" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="10" Name="Mark3">
|
||||||
|
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="126" />
|
||||||
|
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170" SourceID="127" />
|
||||||
|
</CompoundMark>
|
||||||
|
<CompoundMark CompoundMarkID="11" Name="Mark4">
|
||||||
|
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.672350" TargetLng="11.842535" SourceID="128" />
|
||||||
|
<Mark SeqID="2" Name="Finish Line 2" TargetLat="57.6715240" TargetLng="11.8444950" SourceID="129" />
|
||||||
|
</CompoundMark>
|
||||||
|
</Course>
|
||||||
|
<CompoundMarkSequence>
|
||||||
|
<Corner SeqID="1" CompoundMarkID="1" Rounding="PS" ZoneSize="3" />
|
||||||
|
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
|
||||||
|
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3" />
|
||||||
|
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3" />
|
||||||
|
<Corner SeqID="5" CompoundMarkID="5" Rounding="SP" ZoneSize="3" />
|
||||||
|
<Corner SeqID="6" CompoundMarkID="6" Rounding="PS" ZoneSize="3" />
|
||||||
|
<Corner SeqID="7" CompoundMarkID="7" Rounding="SP" ZoneSize="3" />
|
||||||
|
<Corner SeqID="8" CompoundMarkID="8" Rounding="PS" ZoneSize="3" />
|
||||||
|
<Corner SeqID="9" CompoundMarkID="9" Rounding="SP" ZoneSize="3" />
|
||||||
|
<Corner SeqID="10" CompoundMarkID="10" Rounding="PS" ZoneSize="3" />
|
||||||
|
<Corner SeqID="11" CompoundMarkID="11" Rounding="PS" ZoneSize="3" />
|
||||||
|
</CompoundMarkSequence>
|
||||||
|
<CourseLimit>
|
||||||
|
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100" />
|
||||||
|
<Limit SeqID="2" Lat="57.6709520" Lon="11.8485010" />
|
||||||
|
<Limit SeqID="3" Lat="57.6690260" Lon="11.8472790" />
|
||||||
|
<Limit SeqID="4" Lat="57.6693140" Lon="11.8457610" />
|
||||||
|
<Limit SeqID="5" Lat="57.6665370" Lon="11.8432910" />
|
||||||
|
<Limit SeqID="6" Lat="57.6641400" Lon="11.8385840" />
|
||||||
|
<Limit SeqID="7" Lat="57.6629430" Lon="11.8332030" />
|
||||||
|
<Limit SeqID="8" Lat="57.6629480" Lon="11.8249660" />
|
||||||
|
<Limit SeqID="9" Lat="57.6686890" Lon="11.8250920" />
|
||||||
|
<Limit SeqID="10" Lat="57.6692230" Lon="11.8231430" />
|
||||||
|
<Limit SeqID="11" Lat="57.6725370" Lon="11.8272480" />
|
||||||
|
<Limit SeqID="12" Lat="57.6708220" Lon="11.8321340" />
|
||||||
|
</CourseLimit>
|
||||||
|
</Race>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RegattaConfig>
|
||||||
|
<RegattaID>${regattaId}</RegattaID>
|
||||||
|
<RegattaName>${name}</RegattaName>
|
||||||
|
<CourseName>${courseName}</CourseName>
|
||||||
|
<CentralLatitude>${latitude}</CentralLatitude>
|
||||||
|
<CentralLongitude>${longitude}</CentralLongitude>
|
||||||
|
<CentralAltitude>${altitude}</CentralAltitude>
|
||||||
|
<UtcOffset>${utcOffset}</UtcOffset>
|
||||||
|
<MagneticVariation>${magneticVariation}</MagneticVariation>
|
||||||
|
</RegattaConfig>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<GridPane fx:id="finishScreenGridPane" maxHeight="837.0" maxWidth="837.0" minHeight="837.0" minWidth="837.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="837.0" prefWidth="837.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.FinishScreenViewController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="259.0" minHeight="259.0" prefHeight="259.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="64.0" minHeight="64.0" prefHeight="64.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER" text="Race Finished!" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="40.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="CENTER" text="Race Result:" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="28.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<TableView fx:id="finishOrderTable" maxWidth="661.0" prefHeight="324.0" prefWidth="629.0" styleClass="ui-table" GridPane.halignment="CENTER" GridPane.rowIndex="2">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
|
||||||
|
<TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
|
||||||
|
<TableColumn fx:id="shortNameCol" editable="false" maxWidth="155.18472290039062" minWidth="107.0" prefWidth="155.18472290039062" resizable="false" sortable="false" text="Short Name" />
|
||||||
|
<TableColumn fx:id="countryCol" editable="false" maxWidth="258.9999694824219" minWidth="147.0" prefWidth="258.9999694824219" resizable="false" sortable="false" text="Country" />
|
||||||
|
</columns>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="50.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TableView>
|
||||||
|
<Button mnemonicParsing="false" onAction="#switchToStartScreenView" styleClass="blue-ui-btn" text="Return to Start Screen" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="TOP" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<?import javafx.geometry.*?>
|
|
||||||
<?import javafx.scene.text.*?>
|
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import java.lang.*?>
|
|
||||||
<?import javafx.scene.layout.*?>
|
|
||||||
|
|
||||||
<AnchorPane fx:id="raceResults" maxHeight="1080.0" maxWidth="1920.0" minHeight="1080.0" minWidth="1920.0" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<children>
|
|
||||||
<GridPane layoutX="444.0" layoutY="256.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
|
||||||
<columnConstraints>
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="620.1734008789062" minWidth="10.0" prefWidth="493.2829895019531" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="862.0581665039062" minWidth="10.0" prefWidth="786.7170104980469" />
|
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
</columnConstraints>
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="99.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="7.0" prefHeight="88.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="348.0" minHeight="0.0" prefHeight="278.3376770019531" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="653.0" minHeight="0.0" prefHeight="98.66232299804688" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="812.0" minHeight="10.0" prefHeight="418.4577941894531" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
|
||||||
<children>
|
|
||||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Race Results:" wrappingWidth="616.5260620117188" GridPane.rowIndex="2">
|
|
||||||
<font>
|
|
||||||
<Font size="45.0" />
|
|
||||||
</font>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Text>
|
|
||||||
<VBox fx:id="resultsVBox" prefHeight="44.0" prefWidth="571.0" GridPane.rowIndex="3">
|
|
||||||
<opaqueInsets>
|
|
||||||
<Insets />
|
|
||||||
</opaqueInsets>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
<padding>
|
|
||||||
<Insets top="60.0" />
|
|
||||||
</padding></VBox>
|
|
||||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Finish!" wrappingWidth="623.9530334472656" GridPane.rowIndex="1">
|
|
||||||
<font>
|
|
||||||
<Font size="75.0" />
|
|
||||||
</font>
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets left="50.0" />
|
|
||||||
</GridPane.margin>
|
|
||||||
</Text>
|
|
||||||
</children>
|
|
||||||
</GridPane>
|
|
||||||
</children>
|
|
||||||
</AnchorPane>
|
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<GridPane fx:id="lobbyScreen" nodeOrientation="LEFT_TO_RIGHT" prefHeight="960.0" prefWidth="1530.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.LobbyController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="171.0" minHeight="0.0" prefHeight="31.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="462.0" minHeight="10.0" prefHeight="462.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="100.0" minHeight="0.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Text fx:id="lobbyIpText" fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="Lobby: IP" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER">
|
||||||
|
<font>
|
||||||
|
<Font size="29.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<GridPane prefHeight="166.0" prefWidth="1530.0" GridPane.rowIndex="2">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Button fx:id="readyButton" focusTraversable="false" mnemonicParsing="false" onAction="#readyButtonPressed" prefWidth="101.0" text="Ready" GridPane.halignment="CENTER" />
|
||||||
|
<Button focusTraversable="false" mnemonicParsing="false" onAction="#leaveLobbyButtonPressed" text="Leave Lobby" GridPane.columnIndex="1" GridPane.halignment="CENTER" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<GridPane GridPane.rowIndex="1">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="firstListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ListView>
|
||||||
|
<ListView fx:id="secondListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ListView>
|
||||||
|
<ListView fx:id="thirdListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin></ListView>
|
||||||
|
<ListView fx:id="fourthListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ListView>
|
||||||
|
<ListView fx:id="eighthListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin></ListView>
|
||||||
|
<ListView fx:id="fifthListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin></ListView>
|
||||||
|
<ListView fx:id="sixthListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin></ListView>
|
||||||
|
<ListView fx:id="seventhListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="40.0" right="40.0" top="10.0" />
|
||||||
|
</GridPane.margin></ListView>
|
||||||
|
<ImageView fx:id="firstImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="secondImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="thirdImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="fourthImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="fifthImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="sixthImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="seventhImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
<ImageView fx:id="eighthImageView" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
@@ -7,4 +7,5 @@
|
|||||||
<?import java.lang.*?>
|
<?import java.lang.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
<AnchorPane fx:id="contentPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.Controller" />
|
|
||||||
|
<AnchorPane fx:id="contentPane" cache="true" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#keyPressed" onKeyReleased="#keyReleased" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.Controller" />
|
||||||
|
|||||||
@@ -20,26 +20,31 @@
|
|||||||
<children>
|
<children>
|
||||||
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3">
|
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3">
|
||||||
<children>
|
<children>
|
||||||
<Label layoutX="11.0" layoutY="259.0" text="Team Position" textFill="WHITE" />
|
<Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE" />
|
||||||
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
|
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
|
||||||
<Label layoutX="11.0" layoutY="14.0" text="Timer" textFill="WHITE" />
|
<Label layoutX="11.0" layoutY="41.0" text="Timer" textFill="WHITE" />
|
||||||
<Label layoutX="11.0" layoutY="88.0" text="Wind direction" textFill="WHITE" />
|
<Label layoutX="11.0" layoutY="112.0" text="Wind direction" textFill="WHITE" />
|
||||||
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="190.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
||||||
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
|
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="210.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
|
||||||
<font>
|
<font>
|
||||||
<Font name="AdobeArabic-Regular" size="55.0" />
|
<Font name="AdobeArabic-Regular" size="55.0" />
|
||||||
</font>
|
</font>
|
||||||
</Text>
|
</Text>
|
||||||
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="214.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
|
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="238.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="13.0" />
|
<Font name="System Bold" size="13.0" />
|
||||||
</font>
|
</font>
|
||||||
</Text>
|
</Text>
|
||||||
<CheckBox fx:id="toggleFps" graphicTextGap="0.0" layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS" textFill="WHITE" />
|
<Text fx:id="windSpeedText" fill="#d3d3d3" layoutX="12.0" layoutY="237.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0 Knot" textAlignment="RIGHT">
|
||||||
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="280.0" prefHeight="140.0" prefWidth="200.0" styleClass="text-white" />
|
<font>
|
||||||
<Pane layoutX="11.0" layoutY="30.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
|
<Font name="System Bold" size="13.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<CheckBox fx:id="toggleFps" focusTraversable="false" graphicTextGap="0.0" layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS" textFill="WHITE" />
|
||||||
|
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="304.0" prefHeight="116.0" prefWidth="200.0" styleClass="text-white" />
|
||||||
|
<Pane layoutX="11.0" layoutY="39.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
|
||||||
<children>
|
<children>
|
||||||
<Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0" layoutY="34.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="246.0">
|
<Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0" layoutY="51.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="246.0">
|
||||||
<font>
|
<font>
|
||||||
<Font size="25.0" />
|
<Font size="25.0" />
|
||||||
</font>
|
</font>
|
||||||
@@ -48,9 +53,9 @@
|
|||||||
</Pane>
|
</Pane>
|
||||||
<Slider fx:id="annotationSlider" blockIncrement="1.0" layoutX="38.0" layoutY="527.0" majorTickUnit="1.0" max="2.0" minorTickCount="0" prefHeight="51.0" prefWidth="170.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" styleClass="ui-slider" />
|
<Slider fx:id="annotationSlider" blockIncrement="1.0" layoutX="38.0" layoutY="527.0" majorTickUnit="1.0" max="2.0" minorTickCount="0" prefHeight="51.0" prefWidth="170.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" styleClass="ui-slider" />
|
||||||
<Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE" />
|
<Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE" />
|
||||||
<Button fx:id="selectAnnotationBtn" layoutX="35.0" layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0" styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE" />
|
<Button fx:id="selectAnnotationBtn" focusTraversable="false" layoutX="35.0" layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0" styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE" />
|
||||||
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
||||||
<ComboBox fx:id="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" />
|
<ComboBox fx:id="boatSelectionComboBox" focusTraversable="false" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" />
|
||||||
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions">
|
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions">
|
||||||
<xAxis>
|
<xAxis>
|
||||||
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
|
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
|
||||||
|
|||||||
@@ -1,60 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.*?>
|
<?import javafx.geometry.*?>
|
||||||
<?import javafx.scene.control.*?>
|
|
||||||
<?import javafx.scene.text.*?>
|
|
||||||
<?import javafx.scene.canvas.*?>
|
|
||||||
<?import java.lang.*?>
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<GridPane fx:id="gridPane" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.StartScreenController">
|
<GridPane fx:id="startScreen2" nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.StartScreenController">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="442.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="358.0" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints percentHeight="10.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="52.0" minHeight="52.0" prefHeight="52.0" vgrow="SOMETIMES" />
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="0.0" percentHeight="8.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="28.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="65.0" minHeight="36.0" prefHeight="46.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="55.0" minHeight="55.0" percentHeight="9.0" prefHeight="55.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="108.0" minHeight="72.0" prefHeight="98.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="0.0" minHeight="0.0" percentHeight="29.0" prefHeight="0.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints maxHeight="93.0" minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
|
|
||||||
<RowConstraints maxHeight="283.0" minHeight="262.0" prefHeight="283.0" vgrow="SOMETIMES" />
|
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
<Label alignment="CENTER" text="Welcome to Race Vision" textFill="WHITE" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
|
||||||
<font>
|
<font>
|
||||||
<Font size="40.0" />
|
<Font size="40.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Label text="Your live AC35 livestream" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="1">
|
<Button mnemonicParsing="false" onAction="#hostButtonPressed" prefHeight="25.0" prefWidth="175.0" text="Host" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
|
||||||
<font>
|
<Button mnemonicParsing="false" onAction="#connectButtonPressed" prefHeight="25.0" prefWidth="147.0" text="Connect" GridPane.columnIndex="1" GridPane.rowIndex="4">
|
||||||
<Font size="20.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label text="Livestream Status:" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="BOTTOM">
|
|
||||||
<font>
|
|
||||||
<Font size="28.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label fx:id="timeTillLive" text="0:00 minutes" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="4">
|
|
||||||
<font>
|
|
||||||
<Font size="27.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Button fx:id="streamButton" mnemonicParsing="false" onAction="#startStream" styleClass="blue-ui-btn" text="Click to stream" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
|
|
||||||
<Button fx:id="switchToRaceViewButton" disable="true" mnemonicParsing="false" onAction="#switchToRaceView" styleClass="blue-ui-btn" text="Watch Race" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="TOP" />
|
|
||||||
<TableView fx:id="teamList" maxWidth="661.0" prefHeight="324.0" prefWidth="629.0" styleClass="ui-table" GridPane.halignment="CENTER" GridPane.hgrow="NEVER" GridPane.rowIndex="5" GridPane.vgrow="NEVER">
|
|
||||||
<columns>
|
|
||||||
<TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
|
|
||||||
<TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
|
|
||||||
<TableColumn fx:id="shortNameCol" editable="false" maxWidth="155.18472290039062" minWidth="107.0" prefWidth="155.18472290039062" resizable="false" sortable="false" text="Short Name" />
|
|
||||||
<TableColumn fx:id="countryCol" editable="false" maxWidth="258.9999694824219" minWidth="147.0" prefWidth="258.9999694824219" resizable="false" sortable="false" text="Country" />
|
|
||||||
</columns>
|
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets top="10.0" />
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin></Button>
|
||||||
|
<TextField fx:id="ipTextField" alignment="CENTER" maxWidth="-Infinity" prefHeight="25.0" prefWidth="148.0" promptText="Host IP" text="132.181.14." GridPane.halignment="RIGHT" GridPane.rowIndex="4">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="10.0" left="5.0" right="85.0" top="10.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</TableView>
|
</TextField>
|
||||||
<Label fx:id="realTime" text="Local time" textFill="WHITE" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="BOTTOM" />
|
<Text fill="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="OR" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="3">
|
||||||
|
<font>
|
||||||
|
<Font size="21.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<TextField fx:id="portTextField" alignment="CENTER" maxWidth="75.0" prefHeight="25.0" prefWidth="55.0" promptText="Port" text="4942" GridPane.halignment="RIGHT" GridPane.rowIndex="4">
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="5.0" right="5.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</TextField>
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package seng302;
|
|||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -34,9 +35,9 @@ public class TestGeoUtils {
|
|||||||
@Test
|
@Test
|
||||||
public void testLineFunction() {
|
public void testLineFunction() {
|
||||||
|
|
||||||
Integer lineFunctionResult1 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint1);
|
Integer lineFunctionResult1 = GeoUtility.lineFunction(linePoint1, linePoint2, arbitraryPoint1);
|
||||||
Integer lineFunctionResult2 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint2);
|
Integer lineFunctionResult2 = GeoUtility.lineFunction(linePoint1, linePoint2, arbitraryPoint2);
|
||||||
Integer lineFunctionResult3 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint3);
|
Integer lineFunctionResult3 = GeoUtility.lineFunction(linePoint1, linePoint2, arbitraryPoint3);
|
||||||
|
|
||||||
//Point1 and Point2 are on opposite sides
|
//Point1 and Point2 are on opposite sides
|
||||||
assertEquals(Math.abs(lineFunctionResult1), Math.abs(lineFunctionResult2));
|
assertEquals(Math.abs(lineFunctionResult1), Math.abs(lineFunctionResult2));
|
||||||
@@ -50,13 +51,13 @@ public class TestGeoUtils {
|
|||||||
public void testMakeArbitraryVectorPoint() {
|
public void testMakeArbitraryVectorPoint() {
|
||||||
|
|
||||||
//Make a point (1,0) from point (0,0)
|
//Make a point (1,0) from point (0,0)
|
||||||
Point2D newPoint = GeometryUtils.makeArbitraryVectorPoint(linePoint1, 0d, 1d);
|
Point2D newPoint = GeoUtility.makeArbitraryVectorPoint(linePoint1, 0d, 1d);
|
||||||
Point2D expected = new Point2D(1,0);
|
Point2D expected = new Point2D(1,0);
|
||||||
|
|
||||||
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
||||||
assertEquals(expected.getY(), newPoint.getY(), 1E-6);
|
assertEquals(expected.getY(), newPoint.getY(), 1E-6);
|
||||||
|
|
||||||
newPoint = GeometryUtils.makeArbitraryVectorPoint(linePoint1, 90d, 1d);
|
newPoint = GeoUtility.makeArbitraryVectorPoint(linePoint1, 90d, 1d);
|
||||||
expected = new Point2D(0, 1);
|
expected = new Point2D(0, 1);
|
||||||
|
|
||||||
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package seng302.models;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
|
public class YachtTest {
|
||||||
|
|
||||||
|
Double windDir;
|
||||||
|
Double windSpd;
|
||||||
|
List<Yacht> yachts = new ArrayList<Yacht>();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||||
|
windDir = 90d;
|
||||||
|
windSpd = 10d;
|
||||||
|
|
||||||
|
yachts.add(new Yacht("Yacht 1", "Y1", new GeoPoint(-30.0, 20.0), 160.0));
|
||||||
|
yachts.add(new Yacht("Yacht 2", "Y2", new GeoPoint(-40.0, -20.0), 100.0));
|
||||||
|
yachts.add(new Yacht("Yacht 3", "Y3", new GeoPoint(-35.0, -15.5), 20.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package seng302.models.map;
|
package seng302.models.map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import seng302.utilities.GeoPoint;
|
||||||
|
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -11,30 +14,30 @@ import static org.junit.Assert.*;
|
|||||||
public class MercatorProjectionTest {
|
public class MercatorProjectionTest {
|
||||||
@Test
|
@Test
|
||||||
public void toMapPoint() throws Exception {
|
public void toMapPoint() throws Exception {
|
||||||
MapGeo geo1 = new MapGeo(12.485394, 19.38947);
|
GeoPoint geo1 = new GeoPoint(12.485394, 19.38947);
|
||||||
MapPoint actualPoint1 = MercatorProjection.toMapPoint(geo1);
|
javafx.geometry.Point2D actualPoint1 = MercatorProjection.toMapPoint(geo1);
|
||||||
MapPoint expectedPoint1 = new MapPoint(141.78806755555556, 119.0503853635612);
|
javafx.geometry.Point2D expectedPoint1 = new javafx.geometry.Point2D(141.78806755555556, 119.0503853635612);
|
||||||
assertEquals(expectedPoint1.getX(), actualPoint1.getX(), 0.0001);
|
assertEquals(expectedPoint1.getX(), actualPoint1.getX(), 0.0001);
|
||||||
assertEquals(expectedPoint1.getY(), actualPoint1.getY(), 0.0001);
|
assertEquals(expectedPoint1.getY(), actualPoint1.getY(), 0.0001);
|
||||||
|
|
||||||
MapGeo geo2 = new MapGeo(77.456432, -23.456462);
|
GeoPoint geo2 = new GeoPoint(77.456432, -23.456462);
|
||||||
MapPoint actualPoint2 = MercatorProjection.toMapPoint(geo2);
|
javafx.geometry.Point2D actualPoint2 = MercatorProjection.toMapPoint(geo2);
|
||||||
MapPoint expectedPoint2 = new MapPoint(111.31984924444444, 38.03143323746788);
|
javafx.geometry.Point2D expectedPoint2 = new javafx.geometry.Point2D(111.31984924444444, 38.03143323746788);
|
||||||
assertEquals(expectedPoint2.getX(), actualPoint2.getX(), 0.0001);
|
assertEquals(expectedPoint2.getX(), actualPoint2.getX(), 0.0001);
|
||||||
assertEquals(expectedPoint2.getY(), actualPoint2.getY(), 0.0001);
|
assertEquals(expectedPoint2.getY(), actualPoint2.getY(), 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void toMapGeo() throws Exception {
|
public void toMapGeo() throws Exception {
|
||||||
MapPoint point1 = new MapPoint(123.1234, 25.4565);
|
javafx.geometry.Point2D point1 = new javafx.geometry.Point2D(123.1234, 25.4565);
|
||||||
MapGeo actualGeo1 = MercatorProjection.toMapGeo(point1);
|
GeoPoint actualGeo1 = MercatorProjection.toMapGeo(point1);
|
||||||
MapGeo expectedGeo1 = new MapGeo(80.77043127275441, -6.857718749999995);
|
GeoPoint expectedGeo1 = new GeoPoint(80.77043127275441, -6.857718749999995);
|
||||||
assertEquals(expectedGeo1.getLat(), actualGeo1.getLat(), 0.0001);
|
assertEquals(expectedGeo1.getLat(), actualGeo1.getLat(), 0.0001);
|
||||||
assertEquals(expectedGeo1.getLng(), actualGeo1.getLng(), 0.0001);
|
assertEquals(expectedGeo1.getLng(), actualGeo1.getLng(), 0.0001);
|
||||||
|
|
||||||
MapPoint point2 = new MapPoint(1.235, 255.4565);
|
javafx.geometry.Point2D point2 = new javafx.geometry.Point2D(1.235, 255.4565);
|
||||||
MapGeo actualGeo2 = MercatorProjection.toMapGeo(point2);
|
GeoPoint actualGeo2 = MercatorProjection.toMapGeo(point2);
|
||||||
MapGeo expectedGeo2 = new MapGeo(-84.98475532898011, -178.26328125);
|
GeoPoint expectedGeo2 = new GeoPoint(-84.98475532898011, -178.26328125);
|
||||||
assertEquals(expectedGeo2.getLat(), actualGeo2.getLat(), 0.0001);
|
assertEquals(expectedGeo2.getLat(), actualGeo2.getLat(), 0.0001);
|
||||||
assertEquals(expectedGeo2.getLng(), actualGeo2.getLng(), 0.0001);
|
assertEquals(expectedGeo2.getLng(), actualGeo2.getLng(), 0.0001);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
package seng302.models.stream;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
|
||||||
import seng302.models.stream.packets.StreamPacket;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by ptg19 on 26/04/17.
|
|
||||||
*/
|
|
||||||
public class StreamReceiverTest {
|
|
||||||
|
|
||||||
private PriorityBlockingQueue pq;
|
|
||||||
private byte[] brokenPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type
|
|
||||||
0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp
|
|
||||||
0b00000000, 0b00010000, 0b01000000, 0b00000000, //source id
|
|
||||||
0b00100010, 0b00101000, // message length
|
|
||||||
0b00010010, 0b00010010, 0b00010010}; //random start of payload
|
|
||||||
|
|
||||||
private byte[] workingPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type
|
|
||||||
0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp
|
|
||||||
0b00000000, 0b00010000, 0b01000000, 0b00000000, //source id
|
|
||||||
0b00000010, 0b00000000, // message length
|
|
||||||
0b00010010, 0b00010010, // payload
|
|
||||||
0b00100110, (byte)0b10000111, 0b00110101, 0b01111000}; //crc
|
|
||||||
|
|
||||||
private byte[] crcMismatchPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type
|
|
||||||
0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp
|
|
||||||
0b00000000, 0b00000000, 0b01000000, 0b00000000, //source id
|
|
||||||
0b00000010, 0b00000000, // message length
|
|
||||||
0b00010010, 0b00010010, // payload
|
|
||||||
0b00100110, (byte)0b10000111, 0b00110101, 0b01111000}; //crc
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup(){
|
|
||||||
pq = new PriorityBlockingQueue<>(256, new Comparator<StreamPacket>() {
|
|
||||||
@Override
|
|
||||||
public int compare(StreamPacket s1, StreamPacket s2) {
|
|
||||||
return (int) (s1.getTimeStamp() - s2.getTimeStamp());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void connectExitsOnUnexpectedStreamEnd() throws Exception {
|
|
||||||
Socket host=mock(Socket.class);
|
|
||||||
InputStream stream = new ByteArrayInputStream(brokenPacket);
|
|
||||||
when(host.getInputStream()).thenReturn(stream);
|
|
||||||
StreamReceiver streamReceiver = new StreamReceiver(host, pq);
|
|
||||||
|
|
||||||
streamReceiver.connect();
|
|
||||||
assert pq.size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void connectReadsAPacket() throws Exception {
|
|
||||||
Socket host=mock(Socket.class);
|
|
||||||
InputStream stream = new ByteArrayInputStream(workingPacket);
|
|
||||||
when(host.getInputStream()).thenReturn(stream);
|
|
||||||
StreamReceiver streamReceiver = new StreamReceiver(host, pq);
|
|
||||||
|
|
||||||
streamReceiver.connect();
|
|
||||||
assert pq.size() == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void connectDropsAMismatchedCrc() throws Exception {
|
|
||||||
Socket host=mock(Socket.class);
|
|
||||||
InputStream stream = new ByteArrayInputStream(crcMismatchPacket);
|
|
||||||
when(host.getInputStream()).thenReturn(stream);
|
|
||||||
StreamReceiver streamReceiver = new StreamReceiver(host, pq);
|
|
||||||
|
|
||||||
streamReceiver.connect();
|
|
||||||
assert pq.size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void bytestoLongTest() {
|
|
||||||
Socket host=mock(Socket.class);
|
|
||||||
StreamReceiver streamReceiver = new StreamReceiver(host, pq);
|
|
||||||
try {
|
|
||||||
Class[] args = new Class[1];
|
|
||||||
args[0] = byte[].class;
|
|
||||||
Method bytesToLong = streamReceiver.getClass().getDeclaredMethod("bytesToLong", args);
|
|
||||||
bytesToLong.setAccessible(true);
|
|
||||||
byte[] sevenBtyeNumber = {0b01100100, 0b00110100, 0b00010100, 0b00000000, 0b00000000, 0b00000000, (byte)0b10000000};
|
|
||||||
assert bytesToLong.invoke(streamReceiver, sevenBtyeNumber).equals(36028797020288100L);
|
|
||||||
byte[] eightByteNumber = {0b01100100, 0b00110100, 0b00010100, 0b00000000, 0b00000000, 0b00000000, (byte)0b10000000, 0b00100101};
|
|
||||||
assert bytesToLong.invoke(streamReceiver, eightByteNumber).equals(-1L);
|
|
||||||
byte[] emptyArray = {};
|
|
||||||
assert bytesToLong.invoke(streamReceiver, emptyArray).equals(0L);
|
|
||||||
} catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package seng302.server.simulator;
|
package seng302.server.simulator;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.server.simulator.mark.Position;
|
import seng302.utilities.GeoPoint;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -11,10 +12,10 @@ import static org.junit.Assert.*;
|
|||||||
*/
|
*/
|
||||||
public class GeoUtilityTest {
|
public class GeoUtilityTest {
|
||||||
|
|
||||||
private Position p1 = new Position(57.670333, 11.827833);
|
private GeoPoint p1 = new GeoPoint(57.670333, 11.827833);
|
||||||
private Position p2 = new Position(57.671524, 11.844495);
|
private GeoPoint p2 = new GeoPoint(57.671524, 11.844495);
|
||||||
private Position p3 = new Position(57.670822, 11.843392);
|
private GeoPoint p3 = new GeoPoint(57.670822, 11.843392);
|
||||||
private Position p4 = new Position(25.694829, 98.392049);
|
private GeoPoint p4 = new GeoPoint(25.694829, 98.392049);
|
||||||
|
|
||||||
private double toleranceRate = 0.01;
|
private double toleranceRate = 0.01;
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ public class GeoUtilityTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getGeoCoordinate() throws Exception {
|
public void getGeoCoordinate() throws Exception {
|
||||||
Position expected, actual;
|
GeoPoint expected, actual;
|
||||||
|
|
||||||
actual = GeoUtility.getGeoCoordinate(p1, 82.0, 1000.0);
|
actual = GeoUtility.getGeoCoordinate(p1, 82.0, 1000.0);
|
||||||
expected = p2;
|
expected = p2;
|
||||||
|
|||||||