diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java
index f6630e7c..dc6de280 100644
--- a/src/main/java/seng302/App.java
+++ b/src/main/java/seng302/App.java
@@ -12,21 +12,28 @@ public class App extends Application
{
@Override
public void start(Stage primaryStage) throws Exception {
-
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
primaryStage.setTitle("RaceVision");
primaryStage.setScene(new Scene(root));
primaryStage.show();
-// StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1");
- StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread1");
+ }
+
+ public static void main(String[] args) {
+ StreamReceiver sr;
+
+ if (args.length > 1){
+ sr = new StreamReceiver("localhost", 8085, "TestThread1");
+ }
+ else{
+// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1");
+ sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread1");
+ }
+
sr.start();
StreamParser streamParser = new StreamParser("TestThread2");
streamParser.start();
- }
-
- public static void main(String[] args) {
launch(args);
}
}
diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java
index ad4e8c36..6ee934b1 100644
--- a/src/main/java/seng302/controllers/CanvasController.java
+++ b/src/main/java/seng302/controllers/CanvasController.java
@@ -22,6 +22,7 @@ import seng302.models.parsers.StreamPacket;
import seng302.models.parsers.StreamParser;
import seng302.models.parsers.packets.BoatPositionPacket;
+import java.sql.Time;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.PriorityBlockingQueue;
@@ -102,9 +103,6 @@ public class CanvasController {
fitMarksToCanvas();
drawBoats();
timer = new AnimationTimer() {
- private int countdown = 60;
- private int[] currentRaceMarker = {1, 1, 1, 1, 1, 1};
- List marks = raceViewController.getRace().getCourse();
@Override
public void handle(long now) {
@@ -116,19 +114,22 @@ public class CanvasController {
if (frameTimeIndex == 0) {
arrayFilled = true ;
}
+ long elapsedNanos;
if (arrayFilled) {
- long elapsedNanos = now - oldFrameTime ;
+ elapsedNanos = now - oldFrameTime ;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ;
Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ;
drawFps(frameRate.intValue());
}
+ // TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay.
+ elapsedNanos = 1000 / 60;
updateRaceObjects();
}
};
for (Mark m : raceViewController.getRace().getCourse()) {
-// System.out.println(m.getName());
+ System.out.println(m.getName());
}
//timer.start();
}
@@ -175,7 +176,7 @@ public class CanvasController {
class ResizableCanvas extends Canvas {
- public ResizableCanvas() {
+ ResizableCanvas() {
// Redraw canvas when size changes.
widthProperty().addListener(evt -> draw());
heightProperty().addListener(evt -> draw());
@@ -232,39 +233,14 @@ public class CanvasController {
for (Boat boat : boats) {
BoatGroup boatGroup = new BoatGroup(boat, Colors.getColor());
boatGroup.moveTo(startingX, startingY, 0d);
-// boatGroup.setDestination(firstMarkX, firstMarkY);
boatGroup.forceRotation();
- //group.getChildren().add(boatGroup);
raceObjects.add(boatGroup);
boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations());
-// drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading());
}
group.getChildren().add(boatAnnotations);
group.getChildren().addAll(raceObjects);
}
-
- /**
- * Inner class for creating point so that you can rotate it around origin point.
- */
- class Point {
-
- double x, y;
-
- Point (double x, double y) {
- this.x = x;
- this.y = y;
- }
-
- void rotate(double angle) {
- double oldX = x;
- double oldY = y;
- this.x = oldX * Math.cos(angle) - oldY * Math.sin(angle);
- this.y = oldX * Math.sin(angle) + oldY * Math.cos(angle);
-
- }
- }
-
/**
* Calculates x and y location for every marker that fits it to the canvas the race will be drawn on.
*/
diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java
index a788054a..d5d662b2 100644
--- a/src/main/java/seng302/controllers/Controller.java
+++ b/src/main/java/seng302/controllers/Controller.java
@@ -1,14 +1,29 @@
package seng302.controllers;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+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.Pane;
+import javafx.scene.paint.Color;
+import seng302.models.Boat;
+import seng302.models.parsers.StreamParser;
+
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
+import java.util.Timer;
+import java.util.TimerTask;
/**
* Created by michaelrausch on 21/03/17.
@@ -16,6 +31,20 @@ import java.util.ResourceBundle;
public class Controller implements Initializable {
@FXML
private AnchorPane contentPane;
+ @FXML
+ private Label timeTillLive;
+ @FXML
+ private Button streamButton;
+ @FXML
+ private Button switchToRaceViewButton;
+ @FXML
+ private TableView teamList;
+ @FXML
+ private TableColumn boatNameCol;
+ @FXML
+ private TableColumn shortNameCol;
+ @FXML
+ private TableColumn countryCol;
private void setContentPane(String jfxUrl){
try{
@@ -33,5 +62,71 @@ public class Controller implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
+
+ }
+
+ /**
+ * Running a timer to update the livestream status on welcome screen. Update interval is 500 miliseconds.
+ */
+ public void startStream() {
+ if (StreamParser.isStreamStatus()) {
+ streamButton.setVisible(false);
+ timeTillLive.setVisible(true);
+ timeTillLive.setTextFill(Color.GREEN);
+ timeTillLive.setText("Connecting...");
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ Platform.runLater(() -> {
+ if (StreamParser.isRaceFinished()) {
+ timeTillLive.setTextFill(Color.RED);
+ timeTillLive.setText("Race finished! Waiting for new race...");
+ switchToRaceViewButton.setDisable(true);
+ } else if (StreamParser.getTimeSinceStart() > 0 && StreamParser.getTimeSinceStart() % 10 == 0) {
+ updateTeamList();
+ timeTillLive.setTextFill(Color.RED);
+ switchToRaceViewButton.setDisable(false);
+ Long timerMinute = StreamParser.getTimeSinceStart() / 60;
+ Long timerSecond = StreamParser.getTimeSinceStart() % 60;
+ String timerString = "-" + timerMinute + "." + timerSecond + " minutes";
+ timeTillLive.setText(timerString);
+ } else if (StreamParser.getTimeSinceStart() % 10 == 0) {
+ updateTeamList();
+ timeTillLive.setTextFill(Color.BLACK);
+ switchToRaceViewButton.setDisable(false);
+ Long timerMinute = -1 * StreamParser.getTimeSinceStart() / 60;
+ Long timerSecond = -1 * StreamParser.getTimeSinceStart() % 60;
+ String timerString = timerMinute + "." + timerSecond + " minutes";
+ timeTillLive.setText(timerString);
+ }
+ });
+ }
+ }, 0, 500);
+ } else {
+ timeTillLive.setText("Stream not available.");
+ timeTillLive.setTextFill(Color.RED);
+ }
+ }
+
+ public void switchToRaceView() {
+ setContentPane("/views/RaceView.fxml");
+ }
+
+ private void updateTeamList() {
+ ObservableList data = FXCollections.observableArrayList();
+ teamList.setItems(data);
+ boatNameCol.setCellValueFactory(
+ new PropertyValueFactory("boatName")
+ );
+ shortNameCol.setCellValueFactory(
+ new PropertyValueFactory("shortName")
+ );
+ countryCol.setCellValueFactory(
+ new PropertyValueFactory("country")
+ );
+ for (Boat boat : StreamParser.getBoats()) {
+ data.add(boat);
+ }
}
}
diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java
index c064d225..43509252 100644
--- a/src/main/java/seng302/controllers/RaceViewController.java
+++ b/src/main/java/seng302/controllers/RaceViewController.java
@@ -9,13 +9,17 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.CheckBox;
+import javafx.scene.control.Slider;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
+import javafx.util.StringConverter;
import seng302.models.*;
import seng302.models.parsers.ConfigParser;
+import seng302.models.parsers.StreamParser;
import java.io.IOException;
import java.util.*;
@@ -27,7 +31,7 @@ public class RaceViewController extends Thread{
@FXML
private VBox positionVbox;
@FXML
- private CheckBox toggleAnnotation, toggleFps;
+ private CheckBox toggleFps;
@FXML
private Text timerLabel;
@FXML
@@ -35,10 +39,11 @@ public class RaceViewController extends Thread{
@FXML
private Text windArrowText, windDirectionText;
@FXML
+ private Slider annotationSlider;
+ @FXML
private CanvasController includedCanvasController;
private ArrayList startingBoats = new ArrayList<>();
- private boolean displayAnnotations;
private boolean displayFps;
private Timeline timerTimeline;
private Map timelineInfos = new HashMap<>();
@@ -62,7 +67,7 @@ public class RaceViewController extends Thread{
includedCanvasController.setup(this);
includedCanvasController.initializeCanvas();
- //initializeTimer();
+ initializeTimer();
initializeSettings();
//set wind direction!!!!!!! can't find another place to put my code --haoming
@@ -74,22 +79,50 @@ public class RaceViewController extends Thread{
- private void initializeSettings(){
- displayAnnotations = true;
+ private void initializeSettings() {
displayFps = true;
- toggleAnnotation.selectedProperty().addListener(new ChangeListener() {
- @Override
- public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
- displayAnnotations = !displayAnnotations;
- }
- });
toggleFps.selectedProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
displayFps = !displayFps;
}
});
+
+ //SLIFER STUFF BELOW
+ annotationSlider.setLabelFormatter(new StringConverter() {
+ @Override
+ public String toString(Double n) {
+ if (n == 0) return "None";
+ if (n == 1) return "Low";
+ if (n == 2) return "Medium";
+ if (n == 3) return "All";
+
+ return "All";
+ }
+
+ @Override
+ public Double fromString(String s) {
+ switch (s) {
+ case "None":
+ return 0d;
+ case "Low":
+ return 1d;
+ case "Medium":
+ return 2d;
+ case "All":
+ return 3d;
+
+ default:
+ return 3d;
+ }
+ }
+ });
+
+ annotationSlider.valueProperty().addListener((obs, oldval, newVal) ->
+ setAnnotations((int)annotationSlider.getValue()));
+
+ annotationSlider.setValue(3);
}
private void initializeTimer(){
@@ -99,12 +132,11 @@ public class RaceViewController extends Thread{
timerTimeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
event -> {
- // Stop timer if race is finished
- if (this.race.isRaceFinished()) {
- this.timerTimeline.stop();
+ if (StreamParser.isRaceFinished()) {
+ timerLabel.setFill(Color.RED);
+ timerLabel.setText("Race Finished!");
} else {
- timerLabel.setText(convertTimeToMinutesSeconds(race.getRaceTime()));
- this.race.incrementRaceTime();
+ timerLabel.setText(currentTimer());
}
})
);
@@ -259,6 +291,20 @@ public class RaceViewController extends Thread{
return String.format("%02d:%02d", time / 60, time % 60);
}
+ private String currentTimer() {
+ String timerString = "0:00 minutes";
+ if (StreamParser.getTimeSinceStart() > 0 && StreamParser.getTimeSinceStart() % 10 == 0) {
+ Long timerMinute = StreamParser.getTimeSinceStart() / 60;
+ Long timerSecond = StreamParser.getTimeSinceStart() % 60;
+ timerString = "-" + timerMinute + "." + timerSecond + " minutes";
+ } else if (StreamParser.getTimeSinceStart() % 10 == 0) {
+ Long timerMinute = -1 * StreamParser.getTimeSinceStart() / 60;
+ Long timerSecond = -1 * StreamParser.getTimeSinceStart() % 60;
+ timerString = timerMinute + "." + timerSecond + " minutes";
+ }
+ return timerString;
+ }
+
public void stopTimer() {
timerTimeline.stop();
}
@@ -270,10 +316,6 @@ public class RaceViewController extends Thread{
return displayFps;
}
- public boolean isDisplayAnnotations() {
- return displayAnnotations;
- }
-
public Race getRace() {
return race;
}
@@ -286,10 +328,54 @@ public class RaceViewController extends Thread{
return startingBoats;
}
- @FXML
- private void toggleAnnotations () {
- for (RaceObject ro : includedCanvasController.getRaceObjects()) {
- ro.toggleAnnotations();
+
+ private void setAnnotations(Integer annotationLevel) {
+ switch (annotationLevel) {
+ case 0:
+ for (RaceObject ro : includedCanvasController.getRaceObjects()) {
+ if(ro instanceof BoatGroup) {
+ BoatGroup bg = (BoatGroup) ro;
+ bg.setTeamNameObjectVisible(false);
+ bg.setVelocityObjectVisible(false);
+ bg.setLineGroupVisible(false);
+ bg.setWakeVisible(false);
+ }
+ }
+ break;
+ case 1:
+ for (RaceObject ro : includedCanvasController.getRaceObjects()) {
+ if(ro instanceof BoatGroup) {
+ BoatGroup bg = (BoatGroup) ro;
+ bg.setTeamNameObjectVisible(true);
+ bg.setVelocityObjectVisible(false);
+ bg.setLineGroupVisible(false);
+ bg.setWakeVisible(false);
+ }
+ }
+ break;
+ case 2:
+ for (RaceObject ro : includedCanvasController.getRaceObjects()) {
+ if(ro instanceof BoatGroup) {
+ BoatGroup bg = (BoatGroup) ro;
+ bg.setTeamNameObjectVisible(true);
+ bg.setVelocityObjectVisible(false);
+ bg.setLineGroupVisible(true);
+ bg.setWakeVisible(false);
+ }
+ }
+ break;
+ case 3:
+ for (RaceObject ro : includedCanvasController.getRaceObjects()) {
+ if(ro instanceof BoatGroup) {
+ BoatGroup bg = (BoatGroup) ro;
+ bg.setTeamNameObjectVisible(true);
+ bg.setVelocityObjectVisible(true);
+ bg.setLineGroupVisible(true);
+ bg.setWakeVisible(true);
+ }
+ }
+ break;
}
}
+
}
\ No newline at end of file
diff --git a/src/main/java/seng302/models/Boat.java b/src/main/java/seng302/models/Boat.java
index 04ef14ab..dc3aa7b7 100644
--- a/src/main/java/seng302/models/Boat.java
+++ b/src/main/java/seng302/models/Boat.java
@@ -21,6 +21,10 @@ public class Boat {
private int markLastPast;
private String shortName;
private int id;
+ // new attributes to boat
+ private int sourceID;
+ private String boatName;
+ private String country;
public Boat(String teamName) {
this.teamName = teamName;
@@ -45,6 +49,21 @@ public class Boat {
this.id = id;
}
+ /**
+ * New instance created by BoatsParser.
+ *
+ * @param sourceID source ID of the boat
+ * @param boatName full name of the boat
+ * @param shortName short name of the boat
+ * @param country country of the boat
+ */
+ public Boat(int sourceID, String boatName, String shortName, String country) {
+ this.sourceID = sourceID;
+ this.boatName = boatName;
+ this.shortName = shortName;
+ this.country = country;
+ }
+
/**
* Returns the name of the team sailing the boat
*
@@ -141,4 +160,15 @@ public class Boat {
return id;
}
+ public int getSourceID() {
+ return sourceID;
+ }
+
+ public String getBoatName() {
+ return boatName;
+ }
+
+ public String getCountry() {
+ return country;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java
index 8171bdeb..618a3b59 100644
--- a/src/main/java/seng302/models/BoatGroup.java
+++ b/src/main/java/seng302/models/BoatGroup.java
@@ -10,7 +10,9 @@ import javafx.scene.transform.Rotate;
import seng302.models.parsers.StreamParser;
/**
- * Created by CJIRWIN on 25/04/2017.
+ * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 dimensional boat.
+ * It contains a single polygon for the boat, a group of lines to show it's path, a wake object and two text labels to
+ * annotate the boat teams name and the boats velocity.
*/
public class BoatGroup extends RaceObject{
@@ -18,37 +20,50 @@ public class BoatGroup extends RaceObject{
private static final double TEAMNAME_Y_OFFSET = -15d;
private static final double VELOCITY_X_OFFSET = 10d;
private static final double VELOCITY_Y_OFFSET = -5d;
- private static final double VELOCITY_WAKE_RATIO = 2d;
private static final double BOAT_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d;
- private static final int LINE_INTERVAL = 180;
private static double expectedUpdateInterval = 200;
- private static int WAKE_FRAME_INTERVAL = 30;
- private double framesForNewLine = 0;
private boolean destinationSet;
private Point2D lastPoint;
- private int wakeGenerationDelay;
+ private int wakeGenerationDelay = 10;
+ private double distanceTravelled;
private Boat boat;
- private int wakeCounter = WAKE_FRAME_INTERVAL;
private Group lineGroup = new Group();
- private Group wakeGroup = new Group();
private Polygon boatPoly;
- private Polygon wakePoly;
private Text teamNameObject;
private Text velocityObject;
private Wake wake;
+ /**
+ * Creates a BoatGroup with the default triangular boat polygon.
+ * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used to tell which
+ * BoatGroup to update.
+ * @param color The colour of the boat polygon and the trailing line.
+ */
public BoatGroup (Boat boat, Color color){
this.boat = boat;
initChildren(color);
}
+ /**
+ * Creates a BoatGroup with the boat being the default polygon. The head of the boat should be at point (0,0).
+ * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used to tell which
+ * BoatGroup to update.
+ * @param color The colour of the boat polygon and the trailing line.
+ * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat polygon.
+ */
public BoatGroup (Boat boat, Color color, double... points)
{
+ this.boat = boat;
initChildren(color, points);
}
+ /**
+ * Creates the javafx objects that will be the in the group by default.
+ * @param color The colour of the boat polygon and the trailing line.
+ * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat polygon.
+ */
private void initChildren (Color color, double... points) {
boatPoly = new Polygon(points);
boatPoly.setFill(color);
@@ -65,16 +80,19 @@ public class BoatGroup extends RaceObject{
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
destinationSet = false;
- wake = new Wake(0, 0);
- wakeGenerationDelay = wake.numWakes;
+ wake = new Wake(0, -BOAT_HEIGHT);
super.getChildren().addAll(teamNameObject, velocityObject, boatPoly);
}
+ /**
+ * Creates the javafx objects that will be the in the group by default.
+ * @param color The colour of the boat polygon and the trailing line.
+ */
private void initChildren (Color color) {
- initChildren(color,
- -BOAT_WIDTH / 2, BOAT_HEIGHT,
- 0.0, 0.0,
- BOAT_WIDTH / 2, BOAT_HEIGHT);
+ initChildren(color,
+ -BOAT_WIDTH / 2, BOAT_HEIGHT / 2,
+ 0.0, -BOAT_HEIGHT / 2,
+ BOAT_WIDTH / 2, BOAT_HEIGHT / 2);
}
/**
@@ -98,12 +116,18 @@ public class BoatGroup extends RaceObject{
* Moves the boat and its children annotations to coordinates specified
* @param x The X coordinate to move the boat to
* @param y The Y coordinate to move the boat to
+ * @param rotation The heading in degrees from north the boat should rotate to.
*/
public void moveTo (double x, double y, double rotation) {
rotateTo(rotation);
moveTo(x, y);
}
+ /**
+ * Moves the boat and its children annotations to coordinates specified
+ * @param x The X coordinate to move the boat to
+ * @param y The Y coordinate to move the boat to
+ */
public void moveTo (double x, double y) {
boatPoly.setLayoutX(x);
boatPoly.setLayoutY(y);
@@ -116,15 +140,20 @@ public class BoatGroup extends RaceObject{
wake.rotate(currentRotation);
}
+ /**
+ * Updates the position of all graphics in the BoatGroup based off of the given time interval.
+ * @param timeInterval The interval, in milliseconds, the boat should update it's position based on.
+ */
public void updatePosition (long timeInterval) {
+ //Calculate the movement of the boat.
double dx = pixelVelocityX * timeInterval;
double dy = pixelVelocityY * timeInterval;
- double rotation = 0d;
-
+ double rotation = rotationalVelocity * timeInterval;
+ distanceTravelled += Math.abs(dx) + Math.abs(dy);
moveGroupBy(dx, dy, rotation);
-
- if (framesForNewLine-- == 0) {
- framesForNewLine = LINE_INTERVAL;
+ //Draw a new section of the trail every 20 pixels of movement.
+ if (distanceTravelled > 20) {
+ distanceTravelled = 0;
if (lastPoint != null) {
Line l = new Line(
lastPoint.getX(),
@@ -132,37 +161,69 @@ public class BoatGroup extends RaceObject{
boatPoly.getLayoutX(),
boatPoly.getLayoutY()
);
- l.getStrokeDashArray().setAll(4d, 6d);
+ l.getStrokeDashArray().setAll(3d, 7d);
l.setStroke(boatPoly.getFill());
lineGroup.getChildren().add(l);
}
- if (destinationSet){
+ if (destinationSet){ //Only begin drawing after the first destination is set
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
}
- if (lineGroup.getChildren().size() > 100)
- lineGroup.getChildren().remove(0);
}
wake.updatePosition(timeInterval);
}
+ /**
+ * Sets the destination of the boat and the headng it should have once it reaches
+ * @param newXValue
+ * @param newYValue
+ * @param rotation Rotation to move graphics to.
+ * @param raceIds RaceID of the object to move.
+ */
public void setDestination (double newXValue, double newYValue, double rotation, int... raceIds) {
- moveGroupBy(newXValue - boatPoly.getLayoutX(), newYValue - boatPoly.getLayoutY(), rotation);
-
-// destinationSet = true;
-// boat.setVelocity(StreamParser.boatSpeeds.get((long)boat.getId()));
-// if (hasRaceId(raceIds)) {
-// this.pixelVelocityX = (newXValue - boatPoly.getLayoutX()) / expectedUpdateInterval;
-// this.pixelVelocityY = (newYValue - boatPoly.getLayoutY()) / expectedUpdateInterval;
-// this.rotationalGoal = rotation;
-// calculateRotationalVelocity();
-// rotateTo(rotation);
-// if (wakeGenerationDelay > 0) {
-// wake.rotate(rotationalGoal);
-// wakeGenerationDelay--;
+ if (hasRaceId(raceIds)) {
+ destinationSet = true;
+ boat.setVelocity(StreamParser.boatSpeeds.get((long)boat.getId()));
+ if (currentRotation < 0)
+ currentRotation = 360 - currentRotation;
+ double dx = newXValue - boatPoly.getLayoutX();
+ if ((dx > 0 && pixelVelocityX < 0) || (dx < 0 && pixelVelocityX > 0)) {
+ pixelVelocityX = 0;
+ } else {
+ pixelVelocityX = dx / expectedUpdateInterval;
+ }
+ double dy = newYValue - boatPoly.getLayoutY();
+ //Check movement is reasonable. Assumes a 1000 * 1000 canvas
+ if (Math.abs(dx) > 50 || Math.abs(dy) > 50) {
+// System.out.println("dx = " + dx);
+// System.out.println("dy = " + dy);
+ dx = 0;
+ dy = 0;
+ moveTo(newXValue, newYValue);
+ }
+ //Slight delay on changing X/Y direction that could help jitter. Disabled since there was an issue with
+ //packets that might be causing it.
+// if ((dx > 0 && pixelVelocityX < 0) || (dx < 0 && pixelVelocityX > 0)) {
+// pixelVelocityX = 0;
// } else {
-// wake.setRotationalVelocity(rotationalVelocity, rotationalGoal, pixelVelocityX, pixelVelocityY);
+// pixelVelocityX = dx / expectedUpdateInterval;
// }
-// }
+// if ((dy > 0 && pixelVelocityY < 0) || (dy < 0 && pixelVelocityY > 0)) {
+// pixelVelocityY = 0;
+// } else {
+// pixelVelocityY = dy / expectedUpdateInterval;
+// }
+ pixelVelocityX = dx / expectedUpdateInterval;
+ pixelVelocityY = dy / expectedUpdateInterval;
+ rotationalGoal = rotation;
+ calculateRotationalVelocity();
+ if (wakeGenerationDelay > 0) {
+ wake.rotate(rotationalGoal);
+ wakeGenerationDelay--;
+ } else {
+ wake.setRotationalVelocity(rotationalVelocity, currentRotation, boat.getVelocity());
+ }
+ velocityObject.setText(String.format("%.2f m/s", boat.getVelocity()));
+ }
}
public void setDestination (double newXValue, double newYValue, int... raceIDs) {
@@ -180,45 +241,43 @@ public class BoatGroup extends RaceObject{
}
}
- void resizeWake(){
- velocityObject.setText(String.valueOf(boat.getVelocity()));
- super.getChildren().remove(wakePoly);
- wakePoly = new Polygon(
- 5.0,0.0,
- 10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO,
- 0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO
- );
- wakePoly.setLayoutX(boatPoly.getLayoutX());
- wakePoly.setLayoutY(boatPoly.getLayoutY());
- wakePoly.setFill(Color.DARKBLUE);
- super.getChildren().add(wakePoly);
-
- }
-
public void rotateTo (double rotation) {
currentRotation = rotation;
boatPoly.getTransforms().clear();
boatPoly.getTransforms().add(new Rotate(rotation));
}
-
-
public void forceRotation () {
rotateTo (rotationalGoal);
wake.rotate(rotationalGoal);
}
- public void toggleAnnotations () {
- teamNameObject.setVisible(!teamNameObject.isVisible());
- velocityObject.setVisible(!velocityObject.isVisible());
- lineGroup.setVisible(!lineGroup.isVisible());
- wake.setVisible(!wake.isVisible());
+ public void setTeamNameObjectVisible(Boolean visible) {
+ teamNameObject.setVisible(visible);
+ }
+
+ public void setVelocityObjectVisible(Boolean visible) {
+ velocityObject.setVisible(visible);
+ }
+
+ public void setLineGroupVisible(Boolean visible) {
+ lineGroup.setVisible(visible);
+ }
+
+ public void setWakeVisible(Boolean visible) {
+ wake.setVisible(visible);
}
public Boat getBoat() {
return boat;
}
+ /**
+ * Returns true if this BoatGroup contains at least one of the given IDs.
+ *
+ * @param raceIds The ID's to check the BoatGroup for.
+ * @return True if the BoatGroup contains at east one of the given IDs, false otherwise.
+ */
public boolean hasRaceId (int... raceIds) {
for (int id : raceIds) {
if (id == boat.getId())
@@ -227,10 +286,22 @@ public class BoatGroup extends RaceObject{
return false;
}
+ /**
+ * Returns all raceIds associated with this group. For BoatGroups the ID's are for the boat.
+ *
+ * @return An array containing all ID's associated with this RaceObject.
+ */
public int[] getRaceIds () {
return new int[] {boat.getId()};
}
+ /**
+ * Due to javaFX limitations annotations associated with a boat that you want to appear below 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);
diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java
index 30ca28f3..af29cb5c 100644
--- a/src/main/java/seng302/models/RaceObject.java
+++ b/src/main/java/seng302/models/RaceObject.java
@@ -26,10 +26,16 @@ public abstract class RaceObject extends Group {
return expectedUpdateInterval;
}
+ /**
+ *
+ */
public static void setExpectedUpdateInterval(double expectedUpdateInterval) {
RaceObject.expectedUpdateInterval = expectedUpdateInterval;
}
+ /**
+ * Calculates the rotational velocity required to reach the rotationalGoal from the currentRotation.
+ */
protected void calculateRotationalVelocity () {
if (Math.abs(rotationalGoal - currentRotation) > 180) {
if (rotationalGoal - currentRotation >= 0) {
@@ -40,18 +46,29 @@ public abstract class RaceObject extends Group {
} else {
this.rotationalVelocity = (rotationalGoal - currentRotation) / expectedUpdateInterval;
}
+ //Sometimes the rotation is too large to be realistic. In that case just do it instantly.
+ if (Math.abs(rotationalVelocity) > 1) {
+ rotationalVelocity = 0;
+ rotateTo(rotationalGoal);
+ }
}
/**
* Sets the destination of everything within the RaceObject that has an ID in the array raceIds. The destination is
* set to the co-ordinates (x, y) with the given rotation.
- * @param x
- * @param y
- * @param rotation
- * @param raceIds
+ * @param x X co-ordinate to move the graphics to.
+ * @param y Y co-ordinate to move the graphics to.
+ * @param rotation Rotation to move graphics to.
+ * @param raceIds RaceID of the object to move.
*/
public abstract void setDestination (double x, double y, double rotation, int... raceIds);
-
+ /**
+ * Sets the destination of everything within the RaceObject that has an ID in the array raceIds. The destination is
+ * set to the co-ordinates (x, y).
+ * @param x X co-ordinate to move the graphic to.
+ * @param y Y co-ordinate to move the graphic to.
+ * @param raceIds RaceID to the object to move.
+ */
public abstract void setDestination (double x, double y, int... raceIds);
public abstract void updatePosition (long timeInterval);
@@ -67,6 +84,4 @@ public abstract class RaceObject extends Group {
public abstract boolean hasRaceId (int... raceIds);
public abstract int[] getRaceIds ();
-
- public abstract void toggleAnnotations ();
}
diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java
index 3c79d3a9..d389e8e7 100644
--- a/src/main/java/seng302/models/Wake.java
+++ b/src/main/java/seng302/models/Wake.java
@@ -15,12 +15,13 @@ import javafx.scene.transform.Rotate;
*/
class Wake extends Group {
- final int numWakes = 5;
+ private int numWakes = 5;
private double[] velocities = new double[13];
private Arc[] arcs = new Arc[numWakes];
private double[] rotations = new double[numWakes];
private int[] velocityIndices = new int[numWakes];
private double sum = 0;
+ private static double max;
/**
* Create a wake at the given location.
@@ -46,11 +47,19 @@ class Wake extends Group {
* Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses
* the latest given velocity.
* @param rotationalVelocity The rotationalVelocity the wake should move at.
+ * @param rotationGoal Where the wake will rotate to if the wake is calculated to be on a straight section. This is
+ * used to prevent desynchronisation with the Boat polygon.
+ * @param velocity The real world velocity of the boat in m/s.
*/
- void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocityX, double velocityY) {
- sum -= Math.abs(velocities[velocityIndices[0]]);
+ void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocity) {
+// if (Math.abs(rotationalVelocity) > 0.5) {
+// rotationalVelocity = 0;
+// }
+ sum -= Math.abs(velocities[(velocityIndices[0] + 10) % 13]);
sum += Math.abs(rotationalVelocity);
- if (sum < 0.0001)
+// System.out.println("sum = " + sum);
+ max = Math.max(max, rotationalVelocity);
+ if (sum < max)
rotate (rotationGoal); //In relatively straight segments the wake snaps to match the boats current position.
//This stops the wake from eventually becoming out of sync with the boat.
@@ -61,14 +70,14 @@ class Wake extends Group {
for (int i = 1; i < numWakes; i++)
velocityIndices[i] = (velocityIndices[0] + 3 * i) % 13;
- //Scale wakes based on velocity. Assumes boats are always moving at a decent pace.
- double scaleFactor = Math.abs(Math.log10(Math.abs(velocityX) + Math.abs(velocityY)));
- double baseRad = 30;
+ //Scale wakes based on velocity.
+ double baseRad = 20;
+ double rad;
for (Arc arc :arcs) {
- double rad = Math.min(baseRad + 4 * scaleFactor, baseRad + 12);
+ rad = baseRad + velocity;
arc.setRadiusX(rad);
arc.setRadiusY(rad);
- baseRad += 10;
+ baseRad += 5 + (velocity / 2);
}
}
diff --git a/src/main/java/seng302/models/mark/Mark.java b/src/main/java/seng302/models/mark/Mark.java
index 2c086b80..a32ba20f 100644
--- a/src/main/java/seng302/models/mark/Mark.java
+++ b/src/main/java/seng302/models/mark/Mark.java
@@ -17,10 +17,10 @@ public abstract class Mark {
* @param name the name of the mark
* @param markType the type of mark. either GATE_MARK or SINGLE_MARK.
*/
- public Mark (String name, MarkType markType) {
+ public Mark (String name, MarkType markType, int id) {
this.name = name;
this.markType = markType;
- id = 0;
+ this.id = id;
}
public Mark(String name, MarkType markType, double latitude, double longitude) {
diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java
index ab189034..b0ef4768 100644
--- a/src/main/java/seng302/models/mark/MarkGroup.java
+++ b/src/main/java/seng302/models/mark/MarkGroup.java
@@ -28,6 +28,9 @@ public class MarkGroup extends RaceObject {
private Point2D[] nodeDestinations;
public MarkGroup (Mark mark, Point2D... points) {
+ nodePixelVelocitiesX = new double[points.length];
+ nodePixelVelocitiesY = new double[points.length];
+ nodeDestinations = new Point2D[points.length];
marks.add(mark);
mainMark = mark;
Color color = Color.BLACK;
@@ -36,33 +39,58 @@ public class MarkGroup extends RaceObject {
} else if (mark.getName().equals("Finish")){
color = Color.RED;
}
- System.out.println("HERE ARE THE CHILDREN LOL");
+ Circle markCircle;
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
- super.getChildren().add(new Circle(0, 0, MARK_RADIUS, color));
+ markCircle = new Circle(
+ points[0].getX(),
+ points[0].getY(),
+ MARK_RADIUS,
+ color
+ );
+ nodeDestinations = new Point2D[]{
+ new Point2D(markCircle.getCenterX(), markCircle.getCenterY()
+ )
+ };
+ super.getChildren().add(markCircle);
} else {
marks.add(((GateMark) mark).getSingleMark1());
marks.add(((GateMark) mark).getSingleMark2());
- super.getChildren().add(
- new Circle(
- (points[1].getX() - points[0].getX()) / 2d,
- (points[1].getY() - points[0].getY()) / 2d,
- MARK_RADIUS,
- color
- )
+ nodePixelVelocitiesX = new double[]{0d,0d};
+ nodePixelVelocitiesY = new double[]{0d,0d};
+ nodeDestinations = new Point2D[2];
+// markCircle = new Circle(
+// (points[1].getX() - points[0].getX()) / 2d,
+// (points[1].getY() - points[0].getY()) / 2d,
+// MARK_RADIUS,
+// color
+//
+ markCircle = new Circle(
+ points[0].getX(),
+ points[0].getY(),
+ MARK_RADIUS,
+ color
);
- super.getChildren().add(
- new Circle(
- -(points[1].getX() - points[0].getX()) / 2d,
- -(points[1].getY() - points[0].getY()) / 2d,
- MARK_RADIUS,
- color
- )
+ nodeDestinations[0] = new Point2D(markCircle.getCenterX(), markCircle.getCenterY());
+ super.getChildren().add(markCircle);
+// markCircle = new Circle(
+// -(points[1].getX() - points[0].getX()) / 2d,
+// -(points[1].getY() - points[0].getY()) / 2d,
+// MARK_RADIUS,
+// color
+// );
+ markCircle = new Circle(
+ points[1].getX(),
+ points[1].getY(),
+ MARK_RADIUS,
+ color
);
+ nodeDestinations[1] = new Point2D(markCircle.getCenterX(), markCircle.getCenterY());
+ super.getChildren().add(markCircle);
Line line = new Line(
- (points[1].getX() - points[0].getX()) / 2d,
- (points[1].getY() - points[0].getY()) / 2d,
- -(points[1].getX() - points[0].getX()) / 2d,
- -(points[1].getY() - points[0].getY()) / 2d
+ points[0].getX(),
+ points[0].getY(),
+ points[1].getX(),
+ points[1].getY()
);
line.setStrokeWidth(LINE_THICKNESS);
line.setStroke(color);
@@ -70,14 +98,8 @@ public class MarkGroup extends RaceObject {
line.getStrokeDashArray().addAll(DASHED_GAP_LEN, DASHED_LINE_LEN);
}
super.getChildren().add(line);
- nodePixelVelocitiesX = new double[]{0d,0d};
- nodePixelVelocitiesY = new double[]{0d,0d};
- nodeDestinations = new Point2D[]{
- new Point2D(super.getChildren().get(0).getLayoutX(), super.getChildren().get(0).getLayoutY()),
- new Point2D(super.getChildren().get(1).getLayoutX(), super.getChildren().get(1).getLayoutY())
- };
}
- moveTo(points[0].getX(), points[0].getY());
+ //moveTo(points[0].getX(), points[0].getY());
}
public void setDestination (double x, double y, double rotation, int... raceIds) {
@@ -87,88 +109,90 @@ public class MarkGroup extends RaceObject {
}
public void setDestination (double x, double y, int... raceIds) {
- int childrenIndex = -1;
- for (Mark mark : marks) {
+ for (int i = 0; i < marks.size(); i++)
for (int id : raceIds)
- if (id == mark.getId() && childrenIndex != -1)
- setDestinationChild(x, y, childrenIndex);
- else if (id == mark.getId())
- setDestinationGroup(x, y);
- childrenIndex++;
- }
+ if (id == marks.get(i).getId())
+ setDestinationChild(x, y, Math.max(0, i-1));
}
private void setDestinationChild (double x, double y, int childIndex) {
- double relativeX = x - super.getLayoutX();
- double relativeY = y - super.getLayoutY();
- this.nodeDestinations[childIndex] = new Point2D(relativeX, relativeY);
- this.nodePixelVelocitiesX[childIndex] = (relativeX - super.getChildren().get(childIndex).getLayoutX()) / expectedUpdateInterval;
- this.nodePixelVelocitiesY[childIndex] = (relativeY - super.getChildren().get(childIndex).getLayoutY()) / expectedUpdateInterval;
+ //double relativeX = x - super.getLayoutX();
+ //double relativeY = y - super.getLayoutY();
+ Circle markCircle = (Circle) super.getChildren().get(childIndex);
+ this.nodeDestinations[childIndex] = new Point2D(x, y);
+ //if (Math.abs(relativeX - markCircle.getCenterX()) > 30 && Math.abs(relativeY - markCircle.getCenterY()) > 30) {
+ this.nodePixelVelocitiesX[childIndex] = (x - markCircle.getCenterX()) / expectedUpdateInterval;
+ this.nodePixelVelocitiesY[childIndex] = (y - markCircle.getCenterY()) / expectedUpdateInterval;
+ //}
}
- private void setDestinationGroup (double x, double y) {
- pixelVelocityX = (x - super.getLayoutX()) / expectedUpdateInterval;
- pixelVelocityY = (y - super.getLayoutY()) / expectedUpdateInterval;
- }
-
-
public void rotateTo (double rotation) {
- super.getTransforms().clear();
- super.getTransforms().add(new Rotate(rotation));
+ if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
+ Line line = (Line) super.getChildren().get(2);
+ double xCenter = Math.abs(line.getEndX() - line.getStartX());
+ double yCenter = Math.abs(line.getEndY() - line.getStartY());
+ super.getTransforms().setAll(new Rotate(rotation, xCenter, yCenter));
+ }
}
public void updatePosition (long timeInterval) {
- double x = pixelVelocityX * timeInterval;
- double y = pixelVelocityY * timeInterval;
- double rotation = rotationalVelocity * timeInterval;
- moveGroupBy(x, y, rotation);
- updateChildren(timeInterval);
+ Circle markCircle = (Circle) super.getChildren().get(0);
+
+ if (nodePixelVelocitiesX[0] > 0 && markCircle.getCenterX() > nodeDestinations[0].getX() ||
+ nodePixelVelocitiesX[0] < 0 && markCircle.getCenterX() < nodeDestinations[0].getY())
+ nodePixelVelocitiesX[0] = 0;
+ else if (nodePixelVelocitiesX[0] != 0)
+ markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[0] * timeInterval);
+
+ if (nodePixelVelocitiesY[0] > 0 && markCircle.getCenterY() > nodeDestinations[0].getY() ||
+ nodePixelVelocitiesY[0] < 0 && markCircle.getCenterY() < nodeDestinations[0].getY())
+ nodePixelVelocitiesY[0] = 0;
+ else if (nodePixelVelocitiesY[0] != 0)
+ markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[0] * timeInterval);
+
+ if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
+
+ Line line = (Line) super.getChildren().get(2);
+ line.setStartX(markCircle.getCenterX());
+ line.setStartY(markCircle.getCenterY());
+
+ markCircle = (Circle) super.getChildren().get(1);
+
+ if (nodePixelVelocitiesX[1] > 0 && markCircle.getCenterX() >= nodeDestinations[1].getX() ||
+ nodePixelVelocitiesX[1] < 0 && markCircle.getCenterX() <= nodeDestinations[1].getX())
+ nodePixelVelocitiesX[1] = 0;
+ else if (nodePixelVelocitiesX[1] != 0)
+ markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[1] * timeInterval);
+
+ if (nodePixelVelocitiesY[1] > 0 && markCircle.getCenterY() > nodeDestinations[1].getY() ||
+ nodePixelVelocitiesY[1] < 0 && markCircle.getCenterY() < nodeDestinations[1].getY())
+ nodePixelVelocitiesY[1] = 0;
+ else if (nodePixelVelocitiesY[1] != 0)
+ markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[1] * timeInterval);
+ line.setEndX(markCircle.getCenterX());
+ line.setEndY(markCircle.getCenterY());
+ }
}
public void moveGroupBy (double x, double y, double rotation) {
- super.setLayoutX(super.getLayoutX() + x);
- super.setLayoutY(super.getLayoutY() + y);
- rotateTo(rotation);
- }
-
- private void updateChildren (double timeInterval) {
if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
+ Line line = (Line) super.getChildren().get(2);
+ for (int childIndex = 0; childIndex < 2; childIndex++){
+ Circle mark = (Circle) super.getChildren().get(childIndex);
+ mark.setCenterY(mark.getCenterY() + y);
+ mark.setCenterX(mark.getCenterX() + x);
+ }
+ line.setStartX(line.getStartX() + x);
+ line.setStartY(line.getStartY() + y);
+ line.setEndX(line.getEndX() + x);
+ line.setEndY(line.getEndY() + y);
+ } else {
Circle mark = (Circle) super.getChildren().get(0);
- if (nodePixelVelocitiesX[0] > 0 && mark.getLayoutX() >= nodeDestinations[0].getX()) {
- nodePixelVelocitiesX[0] = 0;
- } else if (nodePixelVelocitiesX[0] < 0 && mark.getLayoutX() <= nodeDestinations[0].getX()) {
- nodePixelVelocitiesX[0] = 0;
- } else {
- mark.setLayoutX(mark.getLayoutX() + nodePixelVelocitiesX[0] * timeInterval);
- mark.setLayoutY(mark.getLayoutY() + nodePixelVelocitiesY[0] * timeInterval);
- }
- if (nodePixelVelocitiesY[0] >= 0 && mark.getLayoutY() > nodeDestinations[0].getY()) {
- nodePixelVelocitiesY[0] = 0;
- } else if (nodePixelVelocitiesY[0] < 0 && mark.getLayoutY() <= nodeDestinations[0].getY()) {
- nodePixelVelocitiesY[0] = 0;
- } else {
- mark.setLayoutX(mark.getLayoutX() + nodePixelVelocitiesX[0] * timeInterval);
- mark.setLayoutY(mark.getLayoutY() + nodePixelVelocitiesY[0] * timeInterval);
- }
- mark = (Circle) super.getChildren().get(1);
- if (nodePixelVelocitiesX[1] > 0 && mark.getLayoutX() >= nodeDestinations[1].getX()) {
- nodePixelVelocitiesX[1] = 0;
- } else if (nodePixelVelocitiesX[1] < 0 && mark.getLayoutX() <= nodeDestinations[1].getX()) {
- nodePixelVelocitiesX[1] = 0;
- } else {
- mark.setLayoutX(mark.getLayoutX() + nodePixelVelocitiesX[1] * timeInterval);
- mark.setLayoutY(mark.getLayoutY() + nodePixelVelocitiesY[1] * timeInterval);
- }
- if (nodePixelVelocitiesY[1] >= 0 && mark.getLayoutY() > nodeDestinations[1].getY()) {
- nodePixelVelocitiesY[1] = 0;
- } else if (nodePixelVelocitiesY[1] < 0 && mark.getLayoutY() <= nodeDestinations[1].getY()) {
- nodePixelVelocitiesY[1] = 0;
- } else {
- mark.setLayoutX(mark.getLayoutX() + nodePixelVelocitiesX[1] * timeInterval);
- mark.setLayoutY(mark.getLayoutY() + nodePixelVelocitiesY[1] * timeInterval);
- }
+ mark.setCenterY(mark.getCenterY() + y);
+ mark.setCenterX(mark.getCenterX() + x);
}
+ rotateTo(currentRotation + rotation);
}
public void moveTo (double x, double y, double rotation) {
@@ -177,8 +201,19 @@ public class MarkGroup extends RaceObject {
}
public void moveTo (double x, double y) {
- super.setLayoutX(x);
- super.setLayoutY(y);
+ Circle markCircle = (Circle) super.getChildren().get(0);
+ markCircle.setCenterX(x);
+ markCircle.setCenterY(y);
+ if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
+ markCircle = (Circle) super.getChildren().get(1);
+ markCircle.setCenterX(x);
+ markCircle.setCenterY(y);
+ Line line = (Line) super.getChildren().get(2);
+ line.setStartX(x);
+ line.setStartY(y);
+ line.setEndX(x);
+ line.setEndY(y);
+ }
}
public boolean hasRaceId (int... raceIds) {
@@ -188,9 +223,6 @@ public class MarkGroup extends RaceObject {
return true;
return false;
}
- public void toggleAnnotations () {
-
- }
public static int getMarkRadius() {
return MARK_RADIUS;
@@ -207,5 +239,4 @@ public class MarkGroup extends RaceObject {
idArray[i++] = mark.getId();
return idArray;
}
-
}
diff --git a/src/main/java/seng302/models/mark/SingleMark.java b/src/main/java/seng302/models/mark/SingleMark.java
index 81f6f0b4..d4b4f3f2 100644
--- a/src/main/java/seng302/models/mark/SingleMark.java
+++ b/src/main/java/seng302/models/mark/SingleMark.java
@@ -9,6 +9,7 @@ public class SingleMark extends Mark {
private double lat;
private double lon;
private String name;
+ private int id;
/**
@@ -18,10 +19,11 @@ public class SingleMark extends Mark {
* @param lat, the latitude of the marker
* @param lon, the longitude of the marker
*/
- public SingleMark(String name, double lat, double lon) {
- super(name, MarkType.SINGLE_MARK);
+ public SingleMark(String name, double lat, double lon, int id) {
+ super(name, MarkType.SINGLE_MARK, id);
this.lat = lat;
this.lon = lon;
+ this.id = id;
}
/**
@@ -30,9 +32,10 @@ public class SingleMark extends Mark {
* @param name, the name of the marker
*/
public SingleMark(String name) {
- super(name, MarkType.SINGLE_MARK);
+ super(name, MarkType.SINGLE_MARK, 0);
this.lat = 0;
this.lon = 0;
+ this.id = 0;
}
public double getLatitude() {
diff --git a/src/main/java/seng302/models/parsers/BoatsParser.java b/src/main/java/seng302/models/parsers/BoatsParser.java
new file mode 100644
index 00000000..8180bde8
--- /dev/null
+++ b/src/main/java/seng302/models/parsers/BoatsParser.java
@@ -0,0 +1,77 @@
+package seng302.models.parsers;
+
+import org.w3c.dom.*;
+import org.xml.sax.InputSource;
+import seng302.models.Boat;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Created by ryan_ on 30/04/2017.
+ */
+public class BoatsParser extends FileParser {
+ private Document doc;
+
+ public BoatsParser(String xmlString) {
+ this.doc = this.parseFile(xmlString);
+ }
+
+ /**
+ * Create a boat instance from a given node if 'Type' is 'Yacht'
+ *
+ * @param node a boat node
+ * @return an instance of Boat
+ */
+ private Boat parseBoat(Node node) {
+ try {
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ if (element.getAttribute("Type").equals("Yacht")) {
+ String sourceID = element.getAttribute("SourceID");
+ String boatName = element.getAttribute("BoatName");
+ String shortName = element.getAttribute("ShortName");
+ String stoweName = element.getAttribute("StoweName");
+ String country = element.getAttribute("Country");
+ Boat boat = new Boat(Integer.parseInt(sourceID), boatName, shortName, country);
+ return boat;
+ }
+ } else {
+ throw new NoSuchElementException("Cannot generate a boat by given node");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of boats from the xml.
+ *
+ * @return a list of boats
+ */
+ public List getBoats() {
+ ArrayList boats = new ArrayList<>();
+
+ try {
+ NodeList nodes = this.doc.getElementsByTagName("Boat");
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ Boat boat = parseBoat(node);
+ if (!(boat == null)) {
+ boats.add(boat);
+ }
+ }
+ return boats;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/seng302/models/parsers/CourseParser.java b/src/main/java/seng302/models/parsers/CourseParser.java
index 04a40e0a..ae7f7856 100644
--- a/src/main/java/seng302/models/parsers/CourseParser.java
+++ b/src/main/java/seng302/models/parsers/CourseParser.java
@@ -35,7 +35,8 @@ public class CourseParser extends FileParser {
String name = element.getElementsByTagName("name").item(0).getTextContent();
double lat = Double.valueOf(element.getElementsByTagName("latitude").item(0).getTextContent());
double lon = Double.valueOf(element.getElementsByTagName("longitude").item(0).getTextContent());
- SingleMark singleMark = new SingleMark(name, lat, lon);
+ int id = Integer.valueOf(element.getElementsByTagName("id").item(0).getTextContent());
+ SingleMark singleMark = new SingleMark(name, lat, lon, id);
return singleMark;
} else {
throw new NoSuchElementException("Cannot generate a mark by given node.");
diff --git a/src/main/java/seng302/models/parsers/FileParser.java b/src/main/java/seng302/models/parsers/FileParser.java
index b3d66b05..be162b9e 100644
--- a/src/main/java/seng302/models/parsers/FileParser.java
+++ b/src/main/java/seng302/models/parsers/FileParser.java
@@ -1,12 +1,14 @@
package seng302.models.parsers;
import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.StringReader;
/**
* Created by Haoming Yin (hyi25) on 16/3/2017
@@ -15,6 +17,8 @@ public abstract class FileParser {
private String filePath;
+ public FileParser() {}
+
public FileParser(String path) {
this.filePath = path;
}
@@ -32,6 +36,19 @@ public abstract class FileParser {
e.printStackTrace();
return null;
}
+ }
+ protected Document parseFile(String xmlString) {
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
+ // optional, in order to recover info from broken line.
+ doc.getDocumentElement().normalize();
+ return doc;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
}
}
diff --git a/src/main/java/seng302/models/parsers/StreamPacket.java b/src/main/java/seng302/models/parsers/StreamPacket.java
index 1b0d7f94..5c2c0706 100644
--- a/src/main/java/seng302/models/parsers/StreamPacket.java
+++ b/src/main/java/seng302/models/parsers/StreamPacket.java
@@ -22,7 +22,7 @@ public class StreamPacket {
// if (this.type == PacketType.XML_MESSAGE){
// //System.out.println("--------");
// System.out.println(new String(payload));
-// StreamParser.parsePacket(this);
+// //StreamParser.parsePacket(this);
// }
}
diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java
index 4a2ed055..34b76b80 100644
--- a/src/main/java/seng302/models/parsers/StreamParser.java
+++ b/src/main/java/seng302/models/parsers/StreamParser.java
@@ -32,10 +32,10 @@ public class StreamParser extends Thread{
private String threadName;
private Thread t;
private static boolean raceStarted = false;
-
-
-
-
+ private static boolean raceFinished = false;
+ private static boolean streamStatus = false;
+ private static long timeSinceStart = -1;
+ private static List boats = new ArrayList<>();
public StreamParser(String threadName){
this.threadName = threadName;
@@ -48,6 +48,7 @@ public class StreamParser extends Thread{
public void run(){
try {
System.out.println("START OF STREAM");
+ streamStatus = true;
while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) {
Thread.sleep(1);
}
@@ -158,16 +159,21 @@ public class StreamParser extends Thread{
format.setTimeZone(TimeZone.getTimeZone("UTC"));
long timeTillStart = ((new Date (expectedStartTime)).getTime() - (new Date (currentTime)).getTime())/1000;
if (timeTillStart > 0 && timeTillStart % 10 == 0) {
+ timeSinceStart = timeTillStart;
System.out.println("Time till start: " + timeTillStart + " Seconds");
} else {
if (raceStatus == 4 || raceStatus == 8){
+ raceFinished = true;
+ raceStarted = false;
System.out.println("RACE HAS FINISHED");
} else if (!raceStarted){
raceStarted = true;
+ raceFinished = false;
System.out.println("RACE HAS STARTED");
}
if (timeTillStart % 10 == 0){
- //System.out.println("Time since start: " + -1 * timeTillStart + " Seconds");
+ System.out.println("Time since start: " + -1 * timeTillStart + " Seconds");
+ timeSinceStart = timeTillStart;
}
}
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
@@ -184,7 +190,6 @@ public class StreamParser extends Thread{
boatStatus += "\nEstTimeAtNextMark: " + extractTimeStamp(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20)), 6);
boatStatus += "\nEstTimeAtFinish: " + extractTimeStamp(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20)), 6);
boatStatuses.add(boatStatus);
-// System.out.println("boatStatus = " + boatStatus);
}
}
@@ -228,19 +233,25 @@ public class StreamParser extends Thread{
//Converts XML message to string to be parsed
int currentChar;
while (payloadStream.available() > 0 && (currentChar = payloadStream.read()) != 0) {
- xmlMessage += (char)currentChar;
+ xmlMessage += (char)currentChar;
+ }
+
+ // Parse boat xml from server
+ if (xmlMessageSubType == 7) {
+ BoatsParser boatsParser = new BoatsParser(xmlMessage);
+ boats = boatsParser.getBoats();
}
//Create XML document Object
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- DocumentBuilder db = null;
- try {
- db = dbf.newDocumentBuilder();
- Document doc = db.parse(new InputSource(new StringReader(xmlMessage)));
- // TODO: 25/04/17 ajm412: Check that the object matches expected structure and return Document object.
- } catch (ParserConfigurationException | IOException | SAXException e) {
- e.printStackTrace();
- }
+// DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+// DocumentBuilder db = null;
+// try {
+// db = dbf.newDocumentBuilder();
+// Document doc = db.parse(new InputSource(new StringReader(xmlMessage)));
+// // TODO: 25/04/17 ajm412: Check that the object matches expected structure and return Document object.
+// } catch (ParserConfigurationException | IOException | SAXException e) {
+// e.printStackTrace();
+// }
}
@@ -417,5 +428,50 @@ public class StreamParser extends Thread{
}
return partialLong;
}
+
+ /**
+ * returns false if race not started, true otherwise
+ *
+ * @return race started status
+ */
+ public static boolean isRaceStarted() {
+ return raceStarted;
+ }
+
+ /**
+ * returns false if stream not connected, true otherwise
+ *
+ * @return stream started status
+ */
+ public static boolean isStreamStatus() {
+ return streamStatus;
+ }
+
+ /**
+ * returns race timer
+ *
+ * @return race timer in long
+ */
+ public static long getTimeSinceStart() {
+ return timeSinceStart;
+ }
+
+ /**
+ * return false if race not finished, true otherwise
+ *
+ * @return race finished status
+ */
+ public static boolean isRaceFinished() {
+ return raceFinished;
+ }
+
+ /**
+ * return list of boats from the server
+ *
+ * @return list of boats
+ */
+ public static List getBoats() {
+ return boats;
+ }
}
diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/parsers/StreamReceiver.java
index 0d50315a..4c16991e 100644
--- a/src/main/java/seng302/models/parsers/StreamReceiver.java
+++ b/src/main/java/seng302/models/parsers/StreamReceiver.java
@@ -6,6 +6,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.zip.CRC32;
@@ -15,8 +17,8 @@ import java.util.zip.Checksum;
public class StreamReceiver extends Thread {
private InputStream stream;
private Socket host;
- private ByteArrayOutputStream crcBuffer;
- private Thread thread;
+ private ByteArrayOutputStream crcBuffer;
+ private Thread t;
private String threadName;
public static PriorityBlockingQueue packetBuffer;
@@ -31,24 +33,31 @@ public class StreamReceiver extends Thread {
}
public void run(){
- packetBuffer = new PriorityBlockingQueue<>(256, new Comparator() {
+ PriorityBlockingQueue pq = new PriorityBlockingQueue<>(256, new Comparator() {
@Override
public int compare(StreamPacket s1, StreamPacket s2) {
return (int) (s1.getTimeStamp() - s2.getTimeStamp());
}
});
+ packetBuffer = pq;
connect();
}
public void start () {
System.out.println("Starting " + threadName );
- if (thread == null) {
- thread = new Thread (this, threadName);
- thread.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();
@@ -120,8 +129,8 @@ public class StreamReceiver extends Thread {
}
/**
- * takes an array of up to 7 bytes and returns a positive
- * long constructed from the input bytes
+ * 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
*/
@@ -137,4 +146,13 @@ public class StreamReceiver extends Thread {
}
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();
+
+ }
}
diff --git a/src/main/java/seng302/models/parsers/XMLParser.java b/src/main/java/seng302/models/parsers/XMLParser.java
new file mode 100644
index 00000000..ef65ea38
--- /dev/null
+++ b/src/main/java/seng302/models/parsers/XMLParser.java
@@ -0,0 +1,461 @@
+package seng302.models.parsers;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+
+/**
+ * Class to create an XML object from the XML Packet Messages.
+ */
+class XMLParser {
+
+ /**
+ * Creates a Regatta XML Object from the data in a Regatta XML Message
+ * @param doc XML Document Object
+ * @return A new RegattaXMLObject from the input Document.
+ */
+ RegattaXMLObject createRegattaXML(Document doc) {
+ return new RegattaXMLObject(doc);
+ }
+
+ /**
+ * Creates a Race XML Object from the data in a Regatta XML Message
+ * @param doc XML Document Object
+ * @return A new RaceXMLObject from the input Document.
+ */
+ RaceXMLObject createRaceXML(Document doc) {
+ return new RaceXMLObject(doc);
+ }
+
+ /**
+ * Creates a Boat XML Object from the data in a Regatta XML Message
+ * @param doc XML Document Object
+ * @return A new BoatXMLObject from the input Document.
+ */
+ BoatXMLObject createBoatXML(Document doc) {
+ return new BoatXMLObject(doc);
+ }
+
+ /**
+ * Returns the text content of a given child element tag, assuming it exists, as an Integer.
+ * @param ele Document Element with child elements.
+ * @param tag Tag to find in document elements child elements.
+ * @return Text content from tag if found, null otherwise.
+ */
+ private static Integer getElementInt(Element ele, String tag) {
+ NodeList tagList = ele.getElementsByTagName(tag);
+ if (tagList.getLength() > 0) {
+ return Integer.parseInt(tagList.item(0).getTextContent());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the text content of a given child element tag, assuming it exists, as an String.
+ * @param ele Document Element with child elements.
+ * @param tag Tag to find in document elements child elements.
+ * @return Text content from tag if found, null otherwise.
+ */
+ private static String getElementString(Element ele, String tag) {
+ NodeList tagList = ele.getElementsByTagName(tag);
+ if (tagList.getLength() > 0) {
+ return tagList.item(0).getTextContent();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the text content of a given child element tag, assuming it exists, as a Double.
+ * @param ele Document Element with child elements.
+ * @param tag Tag to find in document elements child elements.
+ * @return Text content from tag if found, null otherwise.
+ */
+ private static Double getElementDouble(Element ele, String tag) {
+ NodeList tagList = ele.getElementsByTagName(tag);
+ if (tagList.getLength() > 0) {
+ return Double.parseDouble(tagList.item(0).getTextContent());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the text content of an attribute of a given Node, assuming it exists, as a String.
+ * @param n A node object that should have some attributes
+ * @param attr The attribute you want to get from the given node.
+ * @return The String representation of the text content of an attribute in the given node, else returns null.
+ */
+ private static String getNodeAttributeString(Node n, String attr) {
+ Node attrItem = n.getAttributes().getNamedItem(attr);
+ if (attrItem != null) {
+ return attrItem.getTextContent();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the text content of an attribute of a given Node, assuming it exists, as an Integer.
+ * @param n A node object that should have some attributes
+ * @param attr The attribute you want to get from the given node.
+ * @return The Integer representation of the text content of an attribute in the given node, else returns null.
+ */
+ private static Integer getNodeAttributeInt(Node n, String attr) {
+ Node attrItem = n.getAttributes().getNamedItem(attr);
+ if (attrItem != null) {
+ return Integer.parseInt(attrItem.getTextContent());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the text content of an attribute of a given Node, assuming it exists, as a Double.
+ * @param n A node object that should have some attributes
+ * @param attr The attribute you want to get from the given node.
+ * @return The Double representation of the text content of an attribute in the given node, else returns null.
+ */
+ private static Double getNodeAttributeDouble(Node n, String attr) {
+ Node attrItem = n.getAttributes().getNamedItem(attr);
+ if (attrItem != null) {
+ return Double.parseDouble(attrItem.getTextContent());
+ } else {
+ return null;
+ }
+ }
+
+ class RegattaXMLObject {
+ //Regatta Info
+ private Integer regattaID;
+ private String regattaName;
+ private String courseName;
+ private Double centralLat;
+ private Double centralLng;
+ private Integer utcOffset;
+
+ /**
+ * Constructor for a RegattaXMLObject.
+ * Takes the information from a Document object and creates a more usable format.
+ * @param doc XML Document Object
+ */
+ RegattaXMLObject(Document doc) {
+ Element docEle = doc.getDocumentElement();
+
+ this.regattaID = getElementInt(docEle, "RegattaID");
+ this.regattaName = getElementString(docEle, "RegattaName");
+ this.courseName = getElementString(docEle, "CourseName");
+ this.centralLat = getElementDouble(docEle, "CentralLatitude");
+ this.centralLng = getElementDouble(docEle, "CentralLongitude");
+ this.utcOffset = getElementInt(docEle, "UtcOffset");
+ }
+
+ public Integer getRegattaID() { return regattaID; }
+ public String getRegattaName() { return regattaName; }
+ public String getCourseName() { return courseName; }
+ public Double getCentralLat() { return centralLat; }
+ public Double getCentralLng() { return centralLng; }
+ public Integer getUtcOffset() { return utcOffset; }
+
+ }
+
+ class RaceXMLObject {
+
+ // Race Info
+ private Integer raceID;
+ private String raceType;
+ private String creationTimeDate; // XML Creation Time
+
+ //Race Start Details
+ private String raceStartTime;
+ private Boolean postponeStatus;
+
+ //Non atomic race attributes
+ private ArrayList participants;
+ private ArrayList course;
+ private ArrayList compoundMarkSequence;
+ private ArrayList courseLimit;
+
+ /**
+ * Constructor for a RaceXMLObject.
+ * Takes the information from a Document object and creates a more usable format.
+ * @param doc XML Document Object
+ */
+ RaceXMLObject(Document doc) {
+ Element docEle = doc.getDocumentElement();
+
+ //Atomic and Semi-Atomic Elements
+ this.raceID = getElementInt(docEle, "RaceID");
+ this.raceType = getElementString(docEle, "RaceType");
+ this.creationTimeDate = getElementString(docEle, "CreationTimeDate");
+
+ Node raceStart = docEle.getElementsByTagName("RaceStartTime").item(0);
+ this.raceStartTime = getNodeAttributeString(raceStart, "Start") ;
+ this.postponeStatus = Boolean.parseBoolean(getNodeAttributeString(raceStart, "Postpone"));
+
+ //Participants
+ participants = new ArrayList<>();
+
+ NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes();
+ for (int i = 0; i < pList.getLength(); i++) {
+ Node pNode = pList.item(i);
+ String entry;
+ if (pNode.getNodeName().equals("Yacht")) {
+ Integer sourceID = getNodeAttributeInt(pNode, "SourceID");
+
+ if (pNode.getAttributes().getLength() == 2) {
+ entry = getNodeAttributeString(pNode, "Entry");
+ } else {
+ entry = null;
+ }
+
+ Participant pa = new Participant(sourceID, entry);
+ participants.add(pa);
+ }
+ }
+
+ //Course
+ course = new ArrayList<>();
+
+ NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
+ for (int i = 0; i < cMarkList.getLength(); i++) {
+ Node cMarkNode = cMarkList.item(i);
+ if (cMarkNode.getNodeName().equals("CompoundMark")) {
+ CompoundMark cMark = new CompoundMark(cMarkNode);
+ course.add(cMark);
+ }
+ }
+
+ //Course Mark Sequence
+ compoundMarkSequence = new ArrayList<>();
+
+ NodeList cornerList = docEle.getElementsByTagName("CompoundMarkSequence").item(0).getChildNodes();
+ for (int i = 0; i < cornerList.getLength(); i++) {
+ Node cornerNode = cornerList.item(i);
+ if (cornerNode.getNodeName().equals("Corner")) {
+ Corner corner = new Corner(cornerNode);
+ compoundMarkSequence.add(corner);
+ }
+ }
+
+ //Course Limits
+ courseLimit = new ArrayList<>();
+
+ NodeList limitList = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes();
+ for (int i = 0; i < limitList.getLength(); i++) {
+ Node limitNode = limitList.item(i);
+ if (limitNode.getNodeName().equals("Limit")) {
+ Limit limit = new Limit(limitNode);
+ courseLimit.add(limit);
+ }
+ }
+ }
+
+ public Integer getRaceID() { return raceID; }
+ public String getRaceType() { return raceType; }
+ public String getCreationTimeDate() { return creationTimeDate; }
+ public String getRaceStartTime() { return raceStartTime; }
+ public Boolean getPostponeStatus() { return postponeStatus; }
+
+ public ArrayList getParticipants() { return participants; }
+ public ArrayList getCompoundMarks() { return course; }
+ public ArrayList getCompoundMarkSequence() { return compoundMarkSequence; }
+ public ArrayList getCourseLimit() { return courseLimit; }
+
+ class Participant {
+ Integer sourceID;
+ String entry;
+
+ Participant(Integer sourceID, String entry) {
+ this.sourceID = sourceID;
+ this.entry = entry;
+ }
+
+ public Integer getsourceID() { return sourceID; }
+ public String getEntry() { return entry; }
+ }
+
+ class CompoundMark {
+ private Integer markID;
+ private String cMarkName;
+ private ArrayList marks;
+
+ CompoundMark(Node compoundMark) {
+ marks = new ArrayList<>();
+ this.markID = getNodeAttributeInt(compoundMark, "CompoundMarkID");
+ this.cMarkName = getNodeAttributeString(compoundMark, "Name");
+ NodeList childMarks = compoundMark.getChildNodes();
+ for (int i = 0; i < childMarks.getLength(); i++) {
+ Node markNode = childMarks.item(i);
+ if (markNode.getNodeName().equals("Mark")) {
+ Mark mark = new Mark(markNode);
+ marks.add(mark);
+ }
+ }
+ }
+
+ public Integer getMarkID() { return markID; }
+ public String getcMarkName() { return cMarkName; }
+ public ArrayList getMarks() { return marks; }
+
+ class Mark {
+ private Integer seqID;
+ private Integer sourceID;
+ private String markName;
+ private Double targetLat;
+ private Double targetLng;
+
+ Mark(Node markNode) {
+
+ this.seqID = getNodeAttributeInt(markNode, "SeqID");
+ this.sourceID = getNodeAttributeInt(markNode, "SourceID");
+ this.markName = getNodeAttributeString(markNode, "Name");
+ this.targetLat = getNodeAttributeDouble(markNode, "TargetLat");
+ this.targetLng = getNodeAttributeDouble(markNode, "TargetLng");
+
+ }
+
+ public Integer getSeqID() { return seqID; }
+ public Integer getSourceID() { return sourceID; }
+ public String getMarkName() { return markName; }
+ public Double getTargetLat() { return targetLat; }
+ public Double getTargetLng() { return targetLng; }
+ }
+ }
+
+ class Corner {
+ private Integer seqID;
+ private Integer compoundMarkID;
+ private String rounding;
+ private Integer zoneSize;
+
+ Corner(Node cornerNode) {
+ this.seqID = getNodeAttributeInt(cornerNode, "SeqID");
+ this.compoundMarkID = getNodeAttributeInt(cornerNode, "CompoundMarkID");
+ this.rounding = getNodeAttributeString(cornerNode, "Rounding");
+ this.zoneSize = getNodeAttributeInt(cornerNode, "ZoneSize");
+ }
+
+ public Integer getSeqID() { return seqID; }
+ public Integer getCompoundMarkID() { return compoundMarkID; }
+ public String getRounding() { return rounding; }
+ public Integer getZoneSize() { return zoneSize; }
+ }
+
+ class Limit {
+ private Integer seqID;
+ private Double lat;
+ private Double lng;
+
+ Limit(Node limitNode) {
+ this.seqID = getNodeAttributeInt(limitNode, "SeqID");
+ this.lat = getNodeAttributeDouble(limitNode, "Lat");
+ this.lng = getNodeAttributeDouble(limitNode, "Lon");
+ }
+
+ public Integer getSeqID() { return seqID; }
+ public Double getLat() { return lat; }
+ public Double getLng() { return lng; }
+ }
+
+ }
+
+ class BoatXMLObject {
+
+ private String lastModified;
+ private Integer version;
+
+ //Settings for the boat type in the race. This may end up having to be reworked if multiple boat types compete.
+ private String boatType;
+ private Double boatLength;
+ private Double hullLength;
+ private Double markZoneSize;
+ private Double courseZoneSize;
+ private ArrayList zoneLimits;// will only contain 5 elements. Limits 1-5
+
+ //Boats
+ ArrayList boats;
+
+ /**
+ * Constructor for a BoatXMLObject.
+ * Takes the information from a Document object and creates a more usable format.
+ * @param doc XML Document Object
+ */
+ BoatXMLObject(Document doc) {
+
+ Element docEle = doc.getDocumentElement();
+
+ this.lastModified = getElementString(docEle, "Modified");
+ this.version = getElementInt(docEle, "Version");
+
+ NodeList settingsList = docEle.getElementsByTagName("Settings").item(0).getChildNodes();
+ this.boatType = getNodeAttributeString(settingsList.item(1), "Type");
+ this.boatLength = getNodeAttributeDouble(settingsList.item(3), "BoatLength");
+ this.hullLength = getNodeAttributeDouble(settingsList.item(3), "HullLength");
+ this.markZoneSize = getNodeAttributeDouble(settingsList.item(5), "MarkZoneSize");
+ this.courseZoneSize = getNodeAttributeDouble(settingsList.item(5), "CourseZoneSize");
+
+ Node zoneLimitsList = settingsList.item(7);
+ this.zoneLimits = new ArrayList<>();
+ for (int i = 0; i < zoneLimitsList.getAttributes().getLength(); i++) {
+ String tag = String.format("Limit%d", i+1);
+ this.zoneLimits.add(getNodeAttributeDouble(zoneLimitsList, tag));
+ }
+
+ this.boats = new ArrayList<>();
+ NodeList boatsList = docEle.getElementsByTagName("Boats").item(0).getChildNodes();
+ for (int i = 0; i < boatsList.getLength(); i++) {
+ Node currentBoat = boatsList.item(i);
+ if (currentBoat.getNodeName().equals("Boat")) {
+ Boat boat = new Boat(currentBoat);
+ this.boats.add(boat);
+ }
+ //System.out.println(this.getBoats());
+ }
+
+ }
+
+ public String getLastModified() { return lastModified; }
+ public Integer getVersion() { return version; }
+ public String getBoatType() { return boatType; }
+ public Double getBoatLength() { return boatLength; }
+ public Double getHullLength() { return hullLength; }
+ public Double getMarkZoneSize() { return markZoneSize; }
+ public Double getCourseZoneSize() { return courseZoneSize; }
+ public ArrayList getZoneLimits() { return zoneLimits; }
+ public ArrayList getBoats() { return boats; }
+
+ class Boat {
+
+ private String boatType;
+ private Integer sourceID;
+ private String hullID; //matches HullNum in the XML spec.
+ private String shortName;
+ private String boatName;
+ private String country;
+
+ Boat(Node boatNode) {
+ this.boatType = getNodeAttributeString(boatNode, "Type");
+ this.sourceID = getNodeAttributeInt(boatNode, "SourceID");
+ this.hullID = getNodeAttributeString(boatNode, "HullNum");
+ this.shortName = getNodeAttributeString(boatNode, "ShortName");
+ this.boatName = getNodeAttributeString(boatNode, "BoatName");
+ this.country = getNodeAttributeString(boatNode, "Country");
+ }
+
+ public String getBoatType() { return boatType; }
+ public Integer getSourceID() { return sourceID; }
+ public String getHullID() { return hullID; }
+ public String getShortName() { return shortName; }
+ public String getBoatName() { return boatName; }
+ public String getCountry() { return country; }
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/config/course.xml b/src/main/resources/config/course.xml
index e11a24cf..6e1a72fb 100644
--- a/src/main/resources/config/course.xml
+++ b/src/main/resources/config/course.xml
@@ -8,17 +8,20 @@
Start1
57.6703330
11.8278330
+ 122
Start2
57.6706330
11.8281330
+ 123
Mid Mark
57.6675700
11.8359880
+ 131
Leeward Gate
@@ -26,11 +29,13 @@
Leeward Gate1
57.6708220
11.8433900
+ 124
Leeward Gate2
57.6711220
11.8436900
+ 125
@@ -39,11 +44,13 @@
Windward Gate1
57.6650170
11.8279170
+ 126
Windward Gate2
57.6653170
11.8282170
+ 127
@@ -52,11 +59,13 @@
Finish1
57.6715240
11.8444950
+ 128
Finish2
57.6718240
11.8447950
+ 129
diff --git a/src/main/resources/views/MainView.fxml b/src/main/resources/views/MainView.fxml
index 4a3d7298..ebea61a2 100644
--- a/src/main/resources/views/MainView.fxml
+++ b/src/main/resources/views/MainView.fxml
@@ -1,11 +1,62 @@
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml
index 6de979d4..c046b7d4 100644
--- a/src/main/resources/views/RaceView.fxml
+++ b/src/main/resources/views/RaceView.fxml
@@ -1,5 +1,10 @@
+
+
+
+
+
@@ -12,7 +17,7 @@
-
+
@@ -40,8 +45,7 @@
-
-
+
@@ -52,6 +56,8 @@
+
+
diff --git a/src/test/java/seng302/EventTest.java b/src/test/java/seng302/EventTest.java
index 0be0fc98..2d53c8a3 100644
--- a/src/test/java/seng302/EventTest.java
+++ b/src/test/java/seng302/EventTest.java
@@ -23,7 +23,7 @@ public class EventTest {
@Test
public void testBoatHeading() throws Exception {
Boat boat = new Boat("testBoat");
- Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1), new SingleMark("mark2", 121.9,99.2), 0);
+ Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1, 1), new SingleMark("mark2", 121.9,99.2, 2), 0);
assertEquals(event.getBoatHeading(), 228.0266137055349, 1e-15);
}
@@ -31,7 +31,7 @@ public class EventTest {
@Test
public void testDistanceBetweenMarks() throws Exception {
Boat boat = new Boat("testBoat");
- Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1), new SingleMark("mark2", 121.9,99.2), 0);
+ Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1, 1), new SingleMark("mark2", 121.9,99.2, 2), 0);
assertEquals(event.getDistanceBetweenMarks(), 339059.653830461, 1e-15);
}
diff --git a/src/test/java/seng302/models/mark/MarkTest.java b/src/test/java/seng302/models/mark/MarkTest.java
index b48f5818..3db92540 100644
--- a/src/test/java/seng302/models/mark/MarkTest.java
+++ b/src/test/java/seng302/models/mark/MarkTest.java
@@ -16,8 +16,8 @@ public class MarkTest {
@Before
public void setUp() throws Exception {
- this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342);
- this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352);
+ this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342, 1);
+ this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352, 2);
this.gateMark = new GateMark("testMark_GM", MarkType.OPEN_GATE, singleMark1, singleMark2, singleMark1.getLatitude(), singleMark2.getLongitude());
}
diff --git a/src/test/java/seng302/models/parsers/CourseParserTest.java b/src/test/java/seng302/models/parsers/CourseParserTest.java
index 201c1b4b..48bf9d92 100644
--- a/src/test/java/seng302/models/parsers/CourseParserTest.java
+++ b/src/test/java/seng302/models/parsers/CourseParserTest.java
@@ -25,18 +25,17 @@ public class CourseParserTest {
public void getGates() throws Exception {
ArrayList course = cp.getCourse();
- assertTrue(MarkType.OPEN_GATE == course.get(0).getMarkType());
GateMark gateMark1 = (GateMark) course.get(0);
- assertEquals(32.293771, gateMark1.getSingleMark2().getLatitude(), 0.00000001);
- assertEquals(-64.855242, gateMark1.getSingleMark2().getLongitude(), 0.00000001);
+ assertEquals(57.670633, gateMark1.getSingleMark2().getLatitude(), 0.00000001);
+ assertEquals(11.8281330, gateMark1.getSingleMark2().getLongitude(), 0.00000001);
GateMark gateMark2 = (GateMark) course.get(5);
assertEquals("Finish1", gateMark2.getSingleMark1().getName());
assertEquals("Finish2", gateMark2.getSingleMark2().getName());
- assertEquals(32.317257, gateMark2.getSingleMark2().getLatitude(), 0.00000001);
- assertEquals(-64.83626, gateMark2.getSingleMark2().getLongitude(), 0.00000001);
+ assertEquals(57.671824, gateMark2.getSingleMark2().getLatitude(), 0.00000001);
+ assertEquals(11.844795, gateMark2.getSingleMark2().getLongitude(), 0.00000001);
}
@Test
diff --git a/src/test/java/seng302/models/parsers/StreamReceiverTest.java b/src/test/java/seng302/models/parsers/StreamReceiverTest.java
new file mode 100644
index 00000000..c7951e3b
--- /dev/null
+++ b/src/test/java/seng302/models/parsers/StreamReceiverTest.java
@@ -0,0 +1,105 @@
+package seng302.models.parsers;
+
+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 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() {
+ @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){
+ System.out.println("");
+ }
+ }
+
+}
\ No newline at end of file