diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index b5838b8b..ecb9e4a3 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -21,7 +21,6 @@ public class App extends Application { primaryStage.setScene(new Scene(root)); primaryStage.setMaximized(true); - primaryStage.show(); primaryStage.setOnCloseRequest(e -> { StreamParser.appClose(); @@ -68,7 +67,6 @@ 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.start(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 06dc88f0..0bdeae25 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -1,6 +1,11 @@ package seng302.controllers; -import javafx.animation.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.PriorityBlockingQueue; +import javafx.animation.AnimationTimer; import javafx.beans.property.SimpleDoubleProperty; import javafx.fxml.FXML; import javafx.geometry.Point2D; @@ -10,15 +15,19 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; -import seng302.models.*; -import seng302.models.mark.*; +import seng302.models.BoatGroup; +import seng302.models.Colors; +import seng302.models.Yacht; +import seng302.models.mark.GateMark; +import seng302.models.mark.Mark; +import seng302.models.mark.MarkGroup; +import seng302.models.mark.MarkType; +import seng302.models.mark.SingleMark; import seng302.models.stream.StreamParser; -import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.XMLParser; import seng302.models.stream.XMLParser.RaceXMLObject.Limit; -import seng302.models.mark.Mark; -import java.util.*; -import java.util.concurrent.PriorityBlockingQueue; +import seng302.models.stream.XMLParser.RaceXMLObject.Participant; +import seng302.models.stream.packets.BoatPositionPacket; /** * Created by ptg19 on 15/03/17. @@ -97,6 +106,7 @@ public class CanvasController { // TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml initializeBoats(); + initializeMarks(); timer = new AnimationTimer() { @Override @@ -118,6 +128,7 @@ public class CanvasController { } // TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay. + elapsedNanos = 1000 / 60; updateGroups(); if (StreamParser.isRaceFinished()) { this.stop(); @@ -179,8 +190,8 @@ public class CanvasController { boatGroup.move(); } for (MarkGroup markGroup : markGroups) { - for (int id : markGroup.getRaceIds()) { - if (StreamParser.boatPositions.containsKey(id)) { + for (Long id : markGroup.getRaceIds()) { + if (StreamParser.markPositions.containsKey(id)) { updateMarkGroup(id, markGroup); } } @@ -214,8 +225,8 @@ public class CanvasController { } } - void updateMarkGroup (int raceId, MarkGroup markGroup) { - PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(raceId); + void updateMarkGroup (long raceId, MarkGroup markGroup) { + PriorityBlockingQueue movementQueue = StreamParser.markPositions.get(raceId); if (movementQueue.size() > 0){ try { BoatPositionPacket positionPacket = movementQueue.take(); @@ -234,16 +245,42 @@ public class CanvasController { Map boats = StreamParser.getBoats(); Group boatAnnotations = new Group(); + ArrayList participants = StreamParser.getXmlObject().getRaceXML().getParticipants(); + ArrayList participantIDs = new ArrayList<>(); + for (Participant p : participants) { + participantIDs.add(p.getsourceID()); + } + for (Yacht boat : boats.values()) { - boat.setColour(Colors.getColor()); - BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); - boatGroups.add(boatGroup); - boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); + if (participantIDs.contains(boat.getSourceID())) { + boat.setColour(Colors.getColor()); + BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); + boatGroups.add(boatGroup); + boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); + } } group.getChildren().add(boatAnnotations); group.getChildren().addAll(boatGroups); } + private void initializeMarks() { + ArrayList allMarks = StreamParser.getXmlObject().getRaceXML().getCompoundMarks(); + for (Mark mark : allMarks) { + if (mark.getMarkType() == MarkType.SINGLE_MARK) { + SingleMark sMark = (SingleMark) mark; + + MarkGroup markGroup = new MarkGroup(sMark, findScaledXY(sMark)); + markGroups.add(markGroup); + } else { + GateMark gMark = (GateMark) mark; + + MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()), findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list. + markGroups.add(markGroup); + } + } + group.getChildren().addAll(markGroups); + } + class ResizableCanvas extends Canvas { ResizableCanvas() { diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 9ce9db91..3e97df5e 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -18,13 +18,14 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; /** - * 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. The boat will update it's position onscreen everytime - * UpdatePosition is called unless the window is minimized in which case it attempts to store animations and apply them - * when the window is maximised. + * 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. The + * boat will update it's position onscreen everytime UpdatePosition is called unless the window is + * minimized in which case it attempts to store animations and apply them when the window is + * maximised. */ -public class BoatGroup extends Group{ +public class BoatGroup extends Group { //Constants for drawing private static final double TEAMNAME_X_OFFSET = 10d; @@ -62,36 +63,40 @@ public class BoatGroup extends Group{ /** * 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 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 (Yacht boat, Color color){ + public BoatGroup(Yacht boat, Color color) { this.boat = boat; initChildren(color); this.textColor = 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. + * 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. + * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat + * polygon. */ - public BoatGroup (Yacht boat, Color color, double... points) - { + public BoatGroup(Yacht boat, Color color, double... points) { this.boat = boat; initChildren(color, points); } /** * Return a text object with caching and a color applied + * * @param defaultText The default text to display * @param fill The text fill color * @return The text object */ - private Text getTextObject(String defaultText, Color fill){ + private Text getTextObject(String defaultText, Color fill) { Text text = new Text(defaultText); text.setFill(fill); @@ -103,10 +108,12 @@ public class BoatGroup extends Group{ /** * 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. + * @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) { + private void initChildren(Color color, double... points) { textColor = color; destinationSet = false; @@ -132,14 +139,14 @@ public class BoatGroup extends Group{ updateLastMarkRoundingTime(); updateTimeTillNextMark(); - if (estTimeToNextMarkObject != null){ + if (estTimeToNextMarkObject != null) { estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); estTimeToNextMarkObject - .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); } - if (legTimeObject != null){ + if (legTimeObject != null) { legTimeObject.setX(LEGTIME_X_OFFSET); legTimeObject.setY(LEGTIME_Y_OFFSET); legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); @@ -148,15 +155,16 @@ public class BoatGroup extends Group{ wake = new Wake(0, -BOAT_HEIGHT); super.getChildren() - .addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject, - legTimeObject); + .addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject, + legTimeObject); } /** * 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) { + private void initChildren(Color color) { initChildren(color, -BOAT_WIDTH / 2, BOAT_HEIGHT / 2, 0.0, -BOAT_HEIGHT / 2, @@ -164,7 +172,9 @@ public class BoatGroup extends Group{ } /** - * Moves the boat and its children annotations from its current coordinates by specified amounts. + * Moves the boat and its children annotations from its current coordinates by specified + * amounts. + * * @param dx The amount to move the X coordinate by * @param dy The amount to move the Y coordinate by */ @@ -186,6 +196,7 @@ public class BoatGroup extends Group{ /** * 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 */ @@ -213,11 +224,11 @@ public class BoatGroup extends Group{ /** * Updates the time until next mark label, will create a label if one doesn't exist */ - private void updateTimeTillNextMark(){ - if (estTimeToNextMarkObject == null){ - estTimeToNextMarkObject = getTextObject("", textColor); + private void updateTimeTillNextMark() { + if (estTimeToNextMarkObject == null) { + estTimeToNextMarkObject = getTextObject("Next mark: -", textColor); } - if (boat.getEstimateTimeAtNextMark() != null){ + if (boat.getEstimateTimeAtNextMark() != null) { DateFormat format = new SimpleDateFormat("mm:ss"); String timeToNextMark = format .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); @@ -230,18 +241,17 @@ public class BoatGroup extends Group{ /** * Updates the time since last mark rounding, will create a label if one doesn't exist */ - private void updateLastMarkRoundingTime(){ - if (legTimeObject == null){ - legTimeObject = getTextObject("", textColor); + private void updateLastMarkRoundingTime() { + if (legTimeObject == null) { + legTimeObject = getTextObject("Last mark: -", textColor); } - if (boat.getMarkRoundingTime() != null){ + if (boat.getMarkRoundingTime() != null) { DateFormat format = new SimpleDateFormat("mm:ss"); String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); + .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); legTimeObject.setText("Last mark: " + elapsedTime); - } - else{ + } else { legTimeObject.setText("Last mark: -"); } @@ -255,19 +265,19 @@ public class BoatGroup extends Group{ moveGroupBy(xIncrement, yIncrement); framesToMove = framesToMove - 1; - if (framesToMove <= 0){ + if (framesToMove <= 0) { isStopped = true; } - if (distanceTravelled > 70){ + if (distanceTravelled > 70) { distanceTravelled = 0d; - if (lastPoint != null){ + if (lastPoint != null) { Line l = new Line( - lastPoint.getX(), - lastPoint.getY(), - boatPoly.getLayoutX(), - boatPoly.getLayoutY() + lastPoint.getX(), + lastPoint.getY(), + boatPoly.getLayoutX(), + boatPoly.getLayoutY() ); l.getStrokeDashArray().setAll(3d, 7d); l.setStroke(boat.getColour()); @@ -276,56 +286,59 @@ public class BoatGroup extends Group{ lineGroup.getChildren().add(l); } - if (destinationSet){ + if (destinationSet) { lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); } } - wake.updatePosition(1000/60); + wake.updatePosition(1000 / 60); } - /** - * Calculates the rotational velocity required to reach the rotationalGoal from the currentRotation. - */ - protected Double calculateRotationalVelocity (Double rotationalGoal) { - Double rotationalVelocity = 0.0; + /** + * Calculates the rotational velocity required to reach the rotationalGoal from the + * currentRotation. + */ + protected Double calculateRotationalVelocity(Double rotationalGoal) { + Double rotationalVelocity = 0.0; - if (Math.abs(rotationalGoal - lastRotation) > 180) { - if (rotationalGoal - lastRotation >= 0.0) { - rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200; - } else { - rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200; - } - } else { - rotationalVelocity = (rotationalGoal - lastRotation) / 200; + if (Math.abs(rotationalGoal - lastRotation) > 180) { + if (rotationalGoal - lastRotation >= 0.0) { + rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200; + } else { + rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200; + } + } else { + rotationalVelocity = (rotationalGoal - lastRotation) / 200; + } + + //Sometimes the rotation is too large to be realistic. In that case just do it instantly. + if (Math.abs(rotationalVelocity) > 1) { + rotationalVelocity = 0.0; + } + + return rotationalVelocity; } - //Sometimes the rotation is too large to be realistic. In that case just do it instantly. - if (Math.abs(rotationalVelocity) > 1) { - rotationalVelocity = 0.0; - } - - return rotationalVelocity; - } - /** * Sets the destination of the boat and the headng it should have once it reaches + * * @param newXValue The X co-ordinate the boat needs to move to. * @param newYValue The Y co-ordinate the boat needs to move to. * @param rotation Rotation to move graphics to. * @param timeValid the time the position values are valid for */ - public void setDestination (double newXValue, double newYValue, double rotation, double groundSpeed, long timeValid, double frameRate, long id) { - if (lastTimeValid == 0){ + public void setDestination(double newXValue, double newYValue, double rotation, + double groundSpeed, long timeValid, double frameRate, long id) { + if (lastTimeValid == 0) { lastTimeValid = timeValid - 200; moveTo(newXValue, newYValue, rotation); } - framesToMove = Math.round((frameRate/(1000.0f/(timeValid-lastTimeValid)))); + framesToMove = Math.round((frameRate / (1000.0f / (timeValid - lastTimeValid)))); double dx = newXValue - boatPoly.getLayoutX(); double dy = newYValue - boatPoly.getLayoutY(); - xIncrement = dx/framesToMove; - yIncrement = dy/framesToMove; + xIncrement = dx / framesToMove; + yIncrement = dy / framesToMove; destinationSet = true; @@ -335,8 +348,8 @@ public class BoatGroup extends Group{ updateLastMarkRoundingTime(); if (Math.abs(rotationalVelocity) > 0.075) { - rotationalVelocity = 0.0; - wake.rotate(rotation); + rotationalVelocity = 0.0; + wake.rotate(rotation); } rotateTo(rotation); @@ -361,7 +374,6 @@ public class BoatGroup extends Group{ } - public void setTeamNameObjectVisible(Boolean visible) { teamNameObject.setVisible(visible); } @@ -400,13 +412,13 @@ public class BoatGroup extends Group{ } /** - * 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. + * 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 () { + public Group getLowPriorityAnnotations() { Group group = new Group(); group.getChildren().addAll(wake, lineGroup); return group; diff --git a/src/main/java/seng302/models/mark/GateMark.java b/src/main/java/seng302/models/mark/GateMark.java index ffcf2b51..79943114 100644 --- a/src/main/java/seng302/models/mark/GateMark.java +++ b/src/main/java/seng302/models/mark/GateMark.java @@ -39,12 +39,10 @@ public class GateMark extends Mark { } public double getLatitude(){ - //return (this.getSingleMark1().getLatitude() + this.getSingleMark2().getLatitude()) / 2; return (this.getSingleMark1().getLatitude()); } public double getLongitude(){ - //return (this.getSingleMark1().getLongitude() + this.getSingleMark2().getLongitude()) / 2; return (this.getSingleMark1().getLongitude()); } diff --git a/src/main/java/seng302/models/mark/Mark.java b/src/main/java/seng302/models/mark/Mark.java index a32ba20f..1ca1f608 100644 --- a/src/main/java/seng302/models/mark/Mark.java +++ b/src/main/java/seng302/models/mark/Mark.java @@ -10,7 +10,7 @@ public abstract class Mark { private MarkType markType; private double latitude; private double longitude; - private int id; + private long id; /** * Create a mark instance by passing its name and type @@ -125,12 +125,11 @@ public abstract class Mark { return longitude; } - public int getId() { + public long getId() { return id; } public void setId(int id) { this.id = id; } - } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index 0e886abc..c87ae174 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -1,14 +1,12 @@ package seng302.models.mark; +import java.util.ArrayList; +import java.util.List; import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; -import javafx.scene.transform.Rotate; - -import java.util.ArrayList; -import java.util.List; /** * Created by CJIRWIN on 26/04/2017. @@ -23,7 +21,12 @@ public class MarkGroup extends Group { private List marks = new ArrayList<>(); private Mark mainMark; - public MarkGroup (Mark mark, Point2D... points) { + /** + * Constructor for singleMark groups + * @param mark + * @param points + */ + public MarkGroup (SingleMark mark, Point2D points) { marks.add(mark); mainMark = mark; Color color = Color.BLACK; @@ -33,61 +36,73 @@ public class MarkGroup extends Group { color = Color.RED; } Circle markCircle; - if (mark.getMarkType() == MarkType.SINGLE_MARK) { - markCircle = new Circle( - points[0].getX(), - points[0].getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - } else { - markCircle = new Circle( - points[0].getX(), - points[0].getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - - markCircle = new Circle( - points[1].getX(), - points[1].getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - Line line = new Line( - points[0].getX(), - points[0].getY(), - points[1].getX(), - points[1].getY() - ); - line.setStrokeWidth(LINE_THICKNESS); - line.setStroke(color); - if (mark.getMarkType() == MarkType.OPEN_GATE) { - line.getStrokeDashArray().addAll(DASHED_GAP_LEN, DASHED_LINE_LEN); - } - super.getChildren().add(line); - } + markCircle = new Circle( + points.getX(), + points.getY(), + MARK_RADIUS, + color + ); + super.getChildren().add(markCircle); } - public void moveMarkTo (double x, double y, int raceId) + public MarkGroup(GateMark mark, Point2D points1, Point2D points2) { + marks.add(mark.getSingleMark1()); + marks.add(mark.getSingleMark2()); + mainMark = mark; + Color color = Color.BLACK; + if (mark.getName().equals("Start")){ + color = Color.GREEN; + } else if (mark.getName().equals("Finish")){ + color = Color.RED; + } + Circle markCircle; + markCircle = new Circle( + points1.getX(), + points1.getY(), + MARK_RADIUS, + color + ); + super.getChildren().add(markCircle); + + markCircle = new Circle( + points2.getX(), + points2.getY(), + MARK_RADIUS, + color + ); + super.getChildren().add(markCircle); + Line line = new Line( + points1.getX(), + points1.getY(), + points2.getX(), + points2.getY() + ); + line.setStrokeWidth(LINE_THICKNESS); + line.setStroke(color); + if (mark.getMarkType() == MarkType.OPEN_GATE) { + line.getStrokeDashArray().addAll(DASHED_GAP_LEN, DASHED_LINE_LEN); + } + super.getChildren().add(line); + + } + + public void moveMarkTo (double x, double y, long raceId) { if (mainMark.getMarkType() == MarkType.SINGLE_MARK) { Circle markCircle = (Circle) super.getChildren().get(0); + markCircle.setCenterX(x); markCircle.setCenterY(y); } else { Circle markCircle1 = (Circle) super.getChildren().get(0); Circle markCircle2 = (Circle) super.getChildren().get(1); Line connectingLine = (Line) super.getChildren().get(2); - if (marks.get(1).getId() == raceId) { + if (marks.get(0).getId() == raceId) { markCircle1.setCenterX(x); markCircle1.setCenterY(y); connectingLine.setStartX(markCircle1.getCenterX()); connectingLine.setStartY(markCircle1.getCenterY()); - } else if (marks.get(2).getId() == raceId) { + } else if (marks.get(1).getId() == raceId) { markCircle2.setCenterX(x); markCircle2.setCenterY(y); connectingLine.setEndX(markCircle2.getCenterX()); @@ -104,8 +119,8 @@ public class MarkGroup extends Group { return false; } - public int[] getRaceIds () { - int[] idArray = new int[marks.size()]; + public long[] getRaceIds () { + long[] idArray = new long[marks.size()]; int i = 0; for (Mark mark : marks) idArray[i++] = mark.getId(); diff --git a/src/main/java/seng302/models/mark/SingleMark.java b/src/main/java/seng302/models/mark/SingleMark.java index d4b4f3f2..9239552a 100644 --- a/src/main/java/seng302/models/mark/SingleMark.java +++ b/src/main/java/seng302/models/mark/SingleMark.java @@ -11,7 +11,6 @@ public class SingleMark extends Mark { private String name; private int id; - /** * Represents a marker * diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index f7093306..f800379c 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -1,6 +1,23 @@ package seng302.models.stream; +import java.io.IOException; +import java.io.StringReader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.PriorityBlockingQueue; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -8,19 +25,6 @@ import seng302.models.Yacht; import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.packets.StreamPacket; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.PriorityBlockingQueue; -import seng302.models.stream.XMLParser; - /** * The purpose of this class is to take in the stream of divided packets so they can be read * and parsed in by turning the byte arrays into useful data. There are two public static hashmaps @@ -29,6 +33,7 @@ import seng302.models.stream.XMLParser; */ public class StreamParser extends Thread{ + public static ConcurrentHashMap> markPositions = new ConcurrentHashMap<>(); public static ConcurrentHashMap> boatPositions = new ConcurrentHashMap<>(); private String threadName; private Thread t; @@ -302,6 +307,7 @@ public class StreamParser extends Thread{ boats = xmlObject.getBoatXML().getCompetingBoats(); } if (messageType == 6) { //6 is race info xml + newRaceXmlReceived = true; } } @@ -400,6 +406,20 @@ public class StreamParser extends Thread{ })); } boatPositions.get(boatId).put(boatPacket); + } else if (deviceType == 3){ + BoatPositionPacket markPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed); + + //add a new priority que to the boatPositions HashMap + if (!markPositions.containsKey(boatId)) { + markPositions.put(boatId, + new PriorityBlockingQueue<>(256, new Comparator() { + @Override + public int compare(BoatPositionPacket p1, BoatPositionPacket p2) { + return (int) (p1.getTimeValid() - p2.getTimeValid()); + } + })); + } + markPositions.get(boatId).put(markPacket); } } diff --git a/src/main/java/seng302/models/stream/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java index 181c1e2f..674a2611 100644 --- a/src/main/java/seng302/models/stream/XMLParser.java +++ b/src/main/java/seng302/models/stream/XMLParser.java @@ -1,27 +1,29 @@ package seng302.models.stream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import seng302.models.Yacht; +import seng302.models.mark.GateMark; +import seng302.models.mark.Mark; import seng302.models.mark.MarkType; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; +import seng302.models.mark.SingleMark; /** * Class to create an XML object from the XML Packet Messages. * * Example usage: * - * Document doc; // some xml document - * Integer xmlMessageType; // an Integer of value 5, 6, 7 - * - * xmlP = new XMLParser(doc, xmlMessageType); - * RegattaXMLObject rXmlObj = xmlP.createRegattaXML(); // creates a regattaXML object. + * Document doc; // some xml document + * Integer xmlMessageType; // an Integer of value 5, 6, 7 * + * xmlP = new XMLParser(doc, xmlMessageType); + * RegattaXMLObject rXmlObj = xmlP.createRegattaXML(); // creates a regattaXML object. */ public class XMLParser { @@ -31,10 +33,12 @@ public class XMLParser { private RegattaXMLObject regattaXML; private BoatXMLObject boatXML; - public XMLParser() {} + public XMLParser() { + } /** * Constructor for XMLParser + * * @param doc Document to create XML object. * @param messageType Defines if a message is a RegattaXML(5), RaceXML(6), BoatXML(7). */ @@ -53,13 +57,22 @@ public class XMLParser { } } - public RaceXMLObject getRaceXML() { return raceXML; } - public RegattaXMLObject getRegattaXML() { return regattaXML; } - public BoatXMLObject getBoatXML() { return boatXML; } + public RaceXMLObject getRaceXML() { + return raceXML; + } + + public RegattaXMLObject getRegattaXML() { + return regattaXML; + } + + public BoatXMLObject getBoatXML() { + return boatXML; + } /** * 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. @@ -75,6 +88,7 @@ public class XMLParser { /** * 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. @@ -90,6 +104,7 @@ public class XMLParser { /** * 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. @@ -105,9 +120,11 @@ public class XMLParser { /** * 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. + * @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); @@ -120,9 +137,11 @@ public class XMLParser { /** * 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. + * @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); @@ -135,9 +154,11 @@ public class XMLParser { /** * 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. + * @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); @@ -149,6 +170,7 @@ public class XMLParser { } public class RegattaXMLObject { + //Regatta Info private Integer regattaID; private String regattaName; @@ -160,6 +182,7 @@ public class XMLParser { /** * 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) { @@ -173,12 +196,29 @@ public class XMLParser { 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; } + 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; + } } @@ -195,13 +235,17 @@ public class XMLParser { //Non atomic race attributes private ArrayList participants; - private ArrayList course; + private ArrayList course; private ArrayList compoundMarkSequence; private ArrayList courseLimit; + // ensures there's no duplicate marks. + private List seenSourceIDs = new ArrayList(); + /** * 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) { @@ -213,8 +257,9 @@ public class XMLParser { this.creationTimeDate = getElementString(docEle, "CreationTimeDate"); Node raceStart = docEle.getElementsByTagName("RaceStartTime").item(0); - this.raceStartTime = getNodeAttributeString(raceStart, "Start") ; - this.postponeStatus = Boolean.parseBoolean(getNodeAttributeString(raceStart, "Postpone")); + this.raceStartTime = getNodeAttributeString(raceStart, "Start"); + this.postponeStatus = Boolean + .parseBoolean(getNodeAttributeString(raceStart, "Postpone")); //Participants participants = new ArrayList<>(); @@ -238,21 +283,13 @@ public class XMLParser { } //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 = createCompoundMarks(docEle); //Course Mark Sequence compoundMarkSequence = new ArrayList<>(); - NodeList cornerList = docEle.getElementsByTagName("CompoundMarkSequence").item(0).getChildNodes(); + 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")) { @@ -274,18 +311,104 @@ public class XMLParser { } } - 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; } + private ArrayList createCompoundMarks(Element docEle) { + ArrayList cMarks = 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")) { + Mark mark = createMark(cMarkNode); + if (mark != null) { + cMarks.add(mark); + } + } + } + + return cMarks; + } + + + private Mark createMark(Node compoundMark) { + + List marksList = new ArrayList<>(); + String 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")) { + + Integer sourceID = getNodeAttributeInt(markNode, "SourceID"); + String markName = getNodeAttributeString(markNode, "Name"); + Double targetLat = getNodeAttributeDouble(markNode, "TargetLat"); + Double targetLng = getNodeAttributeDouble(markNode, "TargetLng"); + + SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID); + marksList.add(mark); + } + } + + for (SingleMark mark : marksList) { + if (seenSourceIDs.contains(mark.getId())) { + return null; + } else { + seenSourceIDs.add(mark.getId()); + } + } + + if (marksList.size() == 1) { + return marksList.get(0); + } else if (marksList.size() == 2) { + return new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0), + marksList.get(1), marksList.get(0).getLatitude(), + marksList.get(0).getLongitude()); + } else { + return null; + } + + } + + 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; + } public class Participant { + Integer sourceID; String entry; @@ -294,65 +417,17 @@ public class XMLParser { this.entry = entry; } - public Integer getsourceID() { return sourceID; } - public String getEntry() { return entry; } - } - - public class CompoundMark { - private Integer markID; - private String cMarkName; - private MarkType markType; - private ArrayList marks; - - CompoundMark(Node compoundMark) { - marks = new ArrayList<>(); - this.markID = getNodeAttributeInt(compoundMark, "CompoundMarkID"); - this.cMarkName = getNodeAttributeString(compoundMark, "Name"); - NodeList childMarks = compoundMark.getChildNodes(); - if (childMarks.getLength() > 1){ - markType = MarkType.OPEN_GATE; - } else { - markType = MarkType.SINGLE_MARK; - } - - 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 getsourceID() { + return sourceID; } - public Integer getMarkID() { return markID; } - public String getcMarkName() { return cMarkName; } - public MarkType getMarkType() { return markType; } - public ArrayList getMarks() { return marks; } - - public 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; } + public String getEntry() { + return entry; } } public class Corner { + private Integer seqID; private Integer compoundMarkID; private String rounding; @@ -365,13 +440,25 @@ public class XMLParser { 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; } + public Integer getSeqID() { + return seqID; + } + + public Integer getCompoundMarkID() { + return compoundMarkID; + } + + public String getRounding() { + return rounding; + } + + public Integer getZoneSize() { + return zoneSize; + } } public class Limit { + private Integer seqID; private Double lat; private Double lng; @@ -382,9 +469,17 @@ public class XMLParser { this.lng = getNodeAttributeDouble(limitNode, "Lon"); } - public Integer getSeqID() { return seqID; } - public Double getLat() { return lat; } - public Double getLng() { return lng; } + public Integer getSeqID() { + return seqID; + } + + public Double getLat() { + return lat; + } + + public Double getLng() { + return lng; + } } } @@ -410,6 +505,7 @@ public class XMLParser { /** * 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) { @@ -429,7 +525,7 @@ public class XMLParser { 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); + String tag = String.format("Limit%d", i + 1); this.zoneLimits.add(getNodeAttributeDouble(zoneLimitsList, tag)); } @@ -440,61 +536,60 @@ public class XMLParser { if (currentBoat.getNodeName().equals("Boat")) { // Boat boat = new Boat(currentBoat); Yacht boat = new Yacht(getNodeAttributeString(currentBoat, "Type"), - getNodeAttributeInt(currentBoat, "SourceID"), - getNodeAttributeString(currentBoat, "HullNum"), - getNodeAttributeString(currentBoat, "ShortName"), - getNodeAttributeString(currentBoat, "BoatName"), - getNodeAttributeString(currentBoat, "Country")); + getNodeAttributeInt(currentBoat, "SourceID"), + getNodeAttributeString(currentBoat, "HullNum"), + getNodeAttributeString(currentBoat, "ShortName"), + getNodeAttributeString(currentBoat, "BoatName"), + getNodeAttributeString(currentBoat, "Country")); this.boats.add(boat); if (boat.getBoatType().equals("Yacht")) { competingBoats.put(boat.getSourceID(), 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; } + 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; + } + public Map getCompetingBoats() { return competingBoats; } -// public 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