diff --git a/pom.xml b/pom.xml
index ea43245e..8d10c6fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,12 +25,6 @@
commons-io
1.3.2
-
-
- com.opencsv
- opencsv
- 3.9
-
com.googlecode.json-simple
json-simple
diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java
index c53a4300..8806308b 100644
--- a/src/main/java/seng302/App.java
+++ b/src/main/java/seng302/App.java
@@ -56,7 +56,7 @@ public class App extends Application {
sr = new StreamReceiver("localhost", 4949, "RaceStream");
break;
case "staffserver":
- sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
+ sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream");
break;
case "official":
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
@@ -67,7 +67,7 @@ public class App extends Application {
else {
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
-// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
+// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream");
}
sr.start();
diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java
index 36d56507..6e37b682 100644
--- a/src/main/java/seng302/controllers/CanvasController.java
+++ b/src/main/java/seng302/controllers/CanvasController.java
@@ -264,7 +264,7 @@ public class CanvasController {
}
private void initializeMarks() {
- ArrayList allMarks = StreamParser.getXmlObject().getRaceXML().getCompoundMarks();
+ List allMarks = StreamParser.getXmlObject().getRaceXML().getNonDupCompoundMarks();
for (Mark mark : allMarks) {
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
SingleMark sMark = (SingleMark) mark;
diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java
index 754da2eb..16e05352 100644
--- a/src/main/java/seng302/controllers/RaceViewController.java
+++ b/src/main/java/seng302/controllers/RaceViewController.java
@@ -6,6 +6,7 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
+import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
@@ -29,7 +30,9 @@ import seng302.models.*;
import seng302.models.mark.GateMark;
import seng302.models.mark.Mark;
import seng302.models.mark.MarkGroup;
+import seng302.models.mark.SingleMark;
import seng302.models.stream.StreamParser;
+import seng302.models.stream.XMLParser;
import java.io.IOException;
import java.util.*;
@@ -185,37 +188,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
updateWindDirection();
updateOrder();
updateBoatSelectionComboBox();
-
- for (Yacht yacht : StreamParser.getBoatsPos().values()) {
-
- if (yacht.getNextMark() != null){
- System.out.println("next Mark: " + yacht.getNextMark().getName());
- for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
-
- Boolean isUpwindLeg = null;
- // Can only calc leg direction if there is a next mark and it is a gate mark
- Mark nextMark = bg.getBoat().getNextMark();
- if (!(nextMark == null || !(nextMark instanceof GateMark))) {
- isUpwindLeg = bg.isUpwindLeg(includedCanvasController);
- }
-
- for (MarkGroup mg : includedCanvasController.getMarkGroups()) {
- if (mg.getMainMark().equals(nextMark)) {
-
- }
- }
- if (isUpwindLeg != null) {
- if (isUpwindLeg) {
-
- }
- }
-
-
- }
-
- }
- }
-
+ updateLaylines();
})
);
@@ -224,6 +197,43 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
+ /**
+ * Iterates over all corners until ones SeqID matches with the boats current leg number.
+ * Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
+ * Returns null if no next mark found.
+ * @param bg The BoatGroup to find the next mark of
+ * @return The next Mark or null if none found
+ */
+ private Mark getNextMark(BoatGroup bg) {
+ Integer legNumber = bg.getBoat().getLegNumber();
+
+ System.out.println("Leg Number: " + legNumber);
+ List markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence();
+
+ if (legNumber == 0) {
+ System.out.println("PreStart");
+ return null;
+ } else if (legNumber == markSequence.size() - 2) {
+ System.out.println("Finishing");
+ return null;
+ }
+
+ for (XMLParser.RaceXMLObject.Corner corner : markSequence) {
+ if (legNumber + 2 == corner.getSeqID()) {
+ Integer thisCompoundMarkID = corner.getCompoundMarkID();
+
+ for (Mark mark : StreamParser.getXmlObject().getRaceXML().getAllCompoundMarks()) {
+ if (mark.getCompoundMarkID() == thisCompoundMarkID) {
+ return mark;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+
/**
* Updates the wind direction arrow and text as from info from the StreamParser
*/
@@ -285,6 +295,53 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}
+ private void updateLaylines() {
+
+ for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
+ System.out.println("========" + bg.getBoat().getBoatName() + "=========");
+ Mark nextMark = getNextMark(bg);
+ Boolean isUpwind = null;
+ // Can only calc leg direction if there is a next mark and it is a gate mark
+ if (nextMark != null) {
+ System.out.println("Next Mark: " + nextMark.getName());
+ if (nextMark instanceof GateMark) {
+ if (bg.isUpwindLeg(includedCanvasController, nextMark)) {
+ isUpwind = true;
+ System.out.println(bg.getBoat().getBoatName() + " is on an upwind leg");
+ } else {
+ isUpwind = false;
+ System.out.println(bg.getBoat().getBoatName() + " is on a downwind leg");
+ }
+
+ for(MarkGroup mg : includedCanvasController.getMarkGroups()) {
+ if (mg.getMainMark().getId() == nextMark.getId()) {
+
+ SingleMark singleMark1 = ((GateMark) nextMark).getSingleMark1();
+ SingleMark singleMark2 = ((GateMark) nextMark).getSingleMark2();
+ Point2D markPoint1 = includedCanvasController.findScaledXY(singleMark1.getLatitude(), singleMark1.getLongitude());
+ Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
+ HashMap angleAndSpeed;
+ if (isUpwind) {
+ angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed());
+ } else {
+ angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed());
+ }
+
+ Double resultingAngle = angleAndSpeed.keySet().iterator().next();
+
+ mg.addLayLine(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
+ mg.addLayLine(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
+
+ }
+ }
+ }
+ }
+
+ System.out.println();
+ }
+ }
+
+
/**
* Initialised the combo box with any boats currently in the race and adds the required listener
* for the combobox to take action upon selection
diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java
index 41d02d08..32df62fa 100644
--- a/src/main/java/seng302/models/BoatGroup.java
+++ b/src/main/java/seng302/models/BoatGroup.java
@@ -375,8 +375,7 @@ public class BoatGroup extends Group {
* going up wind, if they are on different sides of the gate, then the boat is going downwind
* @param canvasController
*/
- public Boolean isUpwindLeg(CanvasController canvasController) {
- Mark nextMark = boat.getNextMark();
+ public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) {
Double windAngle = StreamParser.getWindDirection();
GateMark thisGateMark = (GateMark) nextMark;
@@ -398,8 +397,6 @@ public class BoatGroup extends Group {
boat is travelling into the wind. thus upwind. Otherwise if they are on different sides, then the boat is going
with the wind.
*/
- System.out.println("Boat Line func result: " + boatLineFuncResult);
- System.out.println("Wind Line func result: " + windLineFuncResult);
if (boatLineFuncResult == windLineFuncResult) {
return true;
} else {
diff --git a/src/main/java/seng302/models/PolarTable.java b/src/main/java/seng302/models/PolarTable.java
index 578a3a7b..168d2291 100644
--- a/src/main/java/seng302/models/PolarTable.java
+++ b/src/main/java/seng302/models/PolarTable.java
@@ -1,6 +1,7 @@
package seng302.models;
import java.io.*;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -113,4 +114,50 @@ public final class PolarTable {
return downwindOptimal;
}
+
+ /**
+ * Will raise an exception if a polar table has just one row of data
+ * @param thisWindSpeed The current wind speed
+ * @return HashMap containing just the optimal upwind angle and resulting boat speed
+ */
+ public static HashMap getOptimalUpwindVMG(Double thisWindSpeed) {
+
+ Double polarWindSpeed = getClosestMatch(thisWindSpeed);
+ return upwindOptimal.get(polarWindSpeed);
+ }
+
+
+ /**
+ * Will raise an exception if a polar table has just one row of data
+ * @param thisWindSpeed The current wind speed
+ * @return HashMap containing just the optimal downwind angle and resulting boat speed
+ */
+ public static HashMap getOptimalDownwindVMG(Double thisWindSpeed) {
+
+ Double polarWindSpeed = getClosestMatch(thisWindSpeed);
+ return downwindOptimal.get(polarWindSpeed);
+ }
+
+
+ private static Double getClosestMatch(Double thisWindSpeed) {
+
+ ArrayList windValues = new ArrayList<>(polarTable.keySet());
+
+ Double lowerVal = windValues.get(0);
+ 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;
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java
index 8c4e716b..9e04399b 100644
--- a/src/main/java/seng302/models/mark/MarkGroup.java
+++ b/src/main/java/seng302/models/mark/MarkGroup.java
@@ -58,7 +58,7 @@ public class MarkGroup extends Group {
* @param layLineAngle The angle the laylines point
* @param baseAngle The reference angle
*/
- private void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){
+ public void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){
Point2D ep1 = getPointRotation(startPoint, 50.0, baseAngle + -layLineAngle);
Point2D ep2 = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
@@ -117,8 +117,8 @@ public class MarkGroup extends Group {
//Laylines
// if (mark.)
- addLayLine(points1, 12.0, 90.0);
- addLayLine(points2, 12.0, 90.0);
+// addLayLine(points1, 12.0, 90.0);
+// addLayLine(points2, 12.0, 90.0);
}
public void moveMarkTo (double x, double y, long raceId)
diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java
index 00bfed25..dd82c250 100644
--- a/src/main/java/seng302/models/stream/StreamParser.java
+++ b/src/main/java/seng302/models/stream/StreamParser.java
@@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
-import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
@@ -24,7 +23,6 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.models.Yacht;
import seng302.models.mark.Mark;
-import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
import seng302.models.stream.packets.BoatPositionPacket;
import seng302.models.stream.packets.StreamPacket;
@@ -49,10 +47,15 @@ public class StreamParser extends Thread{
private static Map boats = new ConcurrentHashMap<>();
private static Map boatsPos = new ConcurrentSkipListMap<>();
private static double windDirection = 0;
+ private static Double windSpeed = 0d;
private static Long currentTimeLong;
private static String currentTimeString;
private static boolean appRunning;
+
+ //CONVERSION CONSTANTS
+ private static final Double MS_TO_KNOTS = 1.94384;
+
/**
* Used to initialise the thread name and stream parser object so a thread can be executed
*
@@ -196,7 +199,7 @@ public class StreamParser extends Thread{
int raceStatus = payload[11];
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
- long windSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
+ long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
currentTimeLong = currentTime;
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
@@ -224,6 +227,7 @@ public class StreamParser extends Thread{
double windDirFactor = 0x4000 / 90; //0x4000 is 90 degrees, 0x8000 is 180 degrees, etc...
windDirection = windDir / windDirFactor;
+ windSpeed = rawWindSpeed / 1000 * MS_TO_KNOTS;
int noBoats = payload[22];
int raceType = payload[23];
@@ -445,25 +449,10 @@ public class StreamParser extends Thread{
// assign mark rounding time to boat
boats.get((int)subjectId).setMarkRoundingTime(timeStamp);
- for (Mark mark : xmlObject.getRaceXML().getCompoundMarks()) {
+ for (Mark mark : xmlObject.getRaceXML().getAllCompoundMarks()) {
if (mark.getCompoundMarkID() == markId) {
boats.get((int)subjectId).setLastMarkRounded(mark);
-
- List markSequence = xmlObject.getRaceXML().getCompoundMarkSequence();
-
- for (int i = 0; i < markSequence.size() - 1; i++){
- Corner corner = markSequence.get(i);
-
- if (corner.getCompoundMarkID().equals(mark.getCompoundMarkID()) && (i + 1) < markSequence.size()){
- Corner nextCorner = markSequence.get(i+1);
- for (Mark m : xmlObject.getRaceXML().getCompoundMarks()){
- if (m.getCompoundMarkID() == nextCorner.getCompoundMarkID()){
- boats.get((int)subjectId).setNextMark(m);
- }
- }
- }
- }
- }
+ }
}
}
@@ -593,6 +582,15 @@ public class StreamParser extends Thread{
return windDirection;
}
+
+ /**
+ * Returns the wind speed in knots
+ * @return A double indicating the wind speed in knots
+ */
+ public static Double getWindSpeed() {
+ return windSpeed;
+ }
+
/**
* returns stream time in formatted string format
*
diff --git a/src/main/java/seng302/models/stream/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java
index 4bda96cf..99ce72c8 100644
--- a/src/main/java/seng302/models/stream/XMLParser.java
+++ b/src/main/java/seng302/models/stream/XMLParser.java
@@ -235,7 +235,8 @@ public class XMLParser {
//Non atomic race attributes
private ArrayList participants;
- private ArrayList course;
+ private ArrayList allMarks;
+ private ArrayList nonDuplicateMarks;
private ArrayList compoundMarkSequence;
private ArrayList courseLimit;
@@ -283,7 +284,9 @@ public class XMLParser {
}
//Course
- course = createCompoundMarks(docEle);
+ allMarks = new ArrayList<>();
+ nonDuplicateMarks = new ArrayList<>();
+ createCompoundMarks(docEle);
//Course Mark Sequence
compoundMarkSequence = new ArrayList<>();
@@ -312,26 +315,21 @@ public class XMLParser {
}
- private ArrayList createCompoundMarks(Element docEle) {
- ArrayList cMarks = new ArrayList<>();
+ private void createCompoundMarks(Element docEle) {
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")) {
- Mark mark = createMark(cMarkNode);
- if (mark != null) {
- cMarks.add(mark);
- }
+ createAndAddMark(cMarkNode);
}
}
-
- return cMarks;
}
- private Mark createMark(Node compoundMark) {
+ private void createAndAddMark(Node compoundMark) {
+ Boolean markSeen = false;
List marksList = new ArrayList<>();
Integer compoundMarkID = getNodeAttributeInt(compoundMark, "CompoundMarkID");
String cMarkName = getNodeAttributeString(compoundMark, "Name");
@@ -354,20 +352,26 @@ public class XMLParser {
for (SingleMark mark : marksList) {
if (seenSourceIDs.contains(mark.getId())) {
- return null;
+ markSeen = true;
} else {
seenSourceIDs.add(mark.getId());
}
}
+
if (marksList.size() == 1) {
- return marksList.get(0);
+ if (!markSeen) {
+ nonDuplicateMarks.add(marksList.get(0));
+ }
+ allMarks.add(marksList.get(0));
} else if (marksList.size() == 2) {
- return new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0),
+ GateMark thisGateMark = new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0),
marksList.get(1), marksList.get(0).getLatitude(),
marksList.get(0).getLongitude(), compoundMarkID);
- } else {
- return null;
+ if(!markSeen) {
+ nonDuplicateMarks.add(thisGateMark);
+ }
+ allMarks.add(thisGateMark);
}
}
@@ -396,8 +400,18 @@ public class XMLParser {
return participants;
}
- public ArrayList getCompoundMarks() {
- return course;
+ /**
+ * @return Returns ALL compound marks as stated in the RaceXML (INCLUDING DUPLICATE MARKS)
+ */
+ public List getAllCompoundMarks() {
+ return allMarks;
+ }
+
+ /**
+ * @return Returns Marks from the race XML without any duplicates
+ */
+ public List getNonDupCompoundMarks() {
+ return nonDuplicateMarks;
}
public ArrayList getCompoundMarkSequence() {