diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index bd39a80d..ac264db6 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -5,8 +5,8 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; -import seng302.models.parsers.StreamParser; -import seng302.models.parsers.StreamReceiver; +import seng302.models.stream.StreamParser; +import seng302.models.stream.StreamReceiver; import seng302.server.ServerThread; public class App extends Application { @@ -63,7 +63,6 @@ public class App extends Application { //Change the StreamReceiver in this else block to change the default data source. else{ // sr = new StreamReceiver("localhost", 4949, "RaceStream"); -// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); } diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index c5609cad..67dff4ce 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -1,5 +1,10 @@ package seng302.controllers; +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; @@ -12,18 +17,17 @@ import javafx.scene.paint.Color; import javafx.scene.text.Font; import seng302.models.BoatGroup; import seng302.models.Colors; -import seng302.models.RaceObject; import seng302.models.Yacht; -import seng302.models.mark.*; -import seng302.models.parsers.StreamParser; -import seng302.models.parsers.XMLParser; -import seng302.models.parsers.XMLParser.RaceXMLObject.CompoundMark; -import seng302.models.parsers.XMLParser.RaceXMLObject.Limit; -import seng302.models.parsers.packets.BoatPositionPacket; - -import java.text.DecimalFormat; -import java.util.*; -import java.util.concurrent.PriorityBlockingQueue; +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.XMLParser; +import seng302.models.stream.XMLParser.RaceXMLObject.Limit; +import seng302.models.stream.XMLParser.RaceXMLObject.Participant; +import seng302.models.stream.packets.BoatPositionPacket; /** * Created by ptg19 on 15/03/17. @@ -47,6 +51,7 @@ public class CanvasController { private final int RHS_BUFFER = BUFFER_SIZE + MARK_SIZE / 2; private final int TOP_BUFFER = BUFFER_SIZE; private final int BOT_BUFFER = TOP_BUFFER + MARK_SIZE / 2; + private boolean horizontalInversion = false; private double distanceScaleFactor; private ScaleDirection scaleDirection; @@ -56,16 +61,15 @@ public class CanvasController { private Mark maxLonPoint; private double referencePointX; private double referencePointY; - private double metersToPixels; - private List raceObjects = new ArrayList<>(); - private List raceMarks = new ArrayList<>(); + + private List markGroups = new ArrayList<>(); + private List boatGroups = new ArrayList<>(); //FRAME RATE - private static final double UPDATE_TIME = 0.016666; // 1 / 60 ie 60fps + private Double frameRate = 60.0; private final long[] frameTimes = new long[30]; private int frameTimeIndex = 0; private boolean arrayFilled = false; - private DecimalFormat decimalFormat2dp = new DecimalFormat("0.00"); public AnimationTimer timer; @@ -88,8 +92,6 @@ public class CanvasController { // Bind canvas size to stack pane size. canvas.widthProperty().bind(new SimpleDoubleProperty(CANVAS_WIDTH)); canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT)); - //group.minWidth(CANVAS_WIDTH); - //group.minHeight(CANVAS_HEIGHT); } public void initializeCanvas (){ @@ -101,15 +103,10 @@ public class CanvasController { gc.restore(); fitMarksToCanvas(); - Timer t = new Timer(); - t.schedule(new TimerTask() { - @Override - public void run() { - } - },1000); // TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml - drawBoats(); + initializeBoats(); + initializeMarks(); timer = new AnimationTimer() { @Override @@ -126,14 +123,14 @@ public class CanvasController { if (arrayFilled) { elapsedNanos = now - oldFrameTime ; long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ; - Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; + frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; drawFps(frameRate.intValue()); raceViewController.updateSparkLine(); } // TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay. elapsedNanos = 1000 / 60; - updateRaceObjects(); + updateGroups(); if (StreamParser.isRaceFinished()) { this.stop(); } @@ -164,7 +161,7 @@ public class CanvasController { Point2D borderPoint1 = findScaledXY(thisMark1); Point2D borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), - borderPoint2.getX(), borderPoint2.getY()); + borderPoint2.getX(), borderPoint2.getY()); xBoundaryPoints[i] = borderPoint1.getX(); yBoundaryPoints[i] = borderPoint1.getY(); } @@ -175,72 +172,28 @@ public class CanvasController { Point2D borderPoint1 = findScaledXY(thisMark1); Point2D borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), - borderPoint2.getX(), borderPoint2.getY()); + borderPoint2.getX(), borderPoint2.getY()); xBoundaryPoints[courseLimits.size()-1] = borderPoint1.getX(); yBoundaryPoints[courseLimits.size()-1] = borderPoint1.getY(); gc.setFill(Color.LIGHTBLUE); gc.fillPolygon(xBoundaryPoints,yBoundaryPoints,yBoundaryPoints.length); } - - /** - * Adds the course marks to the canvas, taken from the XMl file - * - * NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and CompoundMark which are - * named the same as those in the model package but are, however not the same, so they do not have things such as - * a type and must be derived from the number of marks in a compound mark etc.. - */ - private void addCourseMarks() { - XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML(); - ArrayList compoundMarks = raceXMLObject.getCompoundMarks(); - RaceObject markGroup; - - for (CompoundMark compoundMark : compoundMarks) { - - //If the compound mark has 2 marks then its a gate mark - if (compoundMark.getMarks().size() == 2) { - CompoundMark.Mark mark1 = compoundMark.getMarks().get(0); - CompoundMark.Mark mark2 = compoundMark.getMarks().get(1); - SingleMark singleMark1 = new SingleMark(mark1.getMarkName(), mark1.getTargetLat(), mark1.getTargetLng(), mark1.getSourceID()); - SingleMark singleMark2 = new SingleMark(mark1.getMarkName(), mark2.getTargetLat(), mark2.getTargetLng(), mark2.getSourceID()); - GateMark thisGateMark = new GateMark(compoundMark.getcMarkName(), - (compoundMark.getMarkID().equals(1)) ? MarkType.OPEN_GATE : MarkType.CLOSED_GATE, - singleMark1, - singleMark2, - singleMark1.getLatitude(), - singleMark1.getLongitude()); - - markGroup = new MarkGroup(thisGateMark, - findScaledXY(thisGateMark.getSingleMark1()), - findScaledXY(thisGateMark.getSingleMark2())); - - raceObjects.add(markGroup); - raceMarks.add(thisGateMark); - - //Otherwise its a single mark - } else { - CompoundMark.Mark singleMark = compoundMark.getMarks().get(0); - Mark thisSingleMark = new SingleMark(singleMark.getMarkName(), - singleMark.getTargetLat(), - singleMark.getTargetLng(), - singleMark.getSourceID()); - - markGroup = new MarkGroup(thisSingleMark, findScaledXY(thisSingleMark)); - raceObjects.add(markGroup); - raceMarks.add(thisSingleMark); - + private void updateGroups(){ + for (BoatGroup boatGroup : boatGroups) { + // some raceObjects will have multiple ID's (for instance gate marks) + //checking if the current "ID" has any updates associated with it + if (StreamParser.boatPositions.containsKey(boatGroup.getRaceId())) { + if (boatGroup.isStopped()) { + updateBoatGroup(boatGroup); + } } + boatGroup.move(); } - } - - private void updateRaceObjects(){ - for (RaceObject raceObject : raceObjects) { - raceObject.updatePosition(1000 / 60); - // some raceObjects will have multiply ID's (for instance gate marks) - for (long id : raceObject.getRaceIds()) { - //checking if the current "ID" has any updates associated with it - if (StreamParser.boatPositions.containsKey(id)) { - move(id, raceObject); + for (MarkGroup markGroup : markGroups) { + for (Long id : markGroup.getRaceIds()) { + if (StreamParser.markPositions.containsKey(id)) { + updateMarkGroup(id, markGroup); } } } @@ -257,33 +210,78 @@ public class CanvasController { } } - private void move(long id, RaceObject raceObject){ - PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(id); + private void updateBoatGroup(BoatGroup boatGroup) { + PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(boatGroup.getRaceId()); + // giving the movementQueue a 5 packet buffer to account for slightly out of order packets if (movementQueue.size() > 0){ - BoatPositionPacket positionPacket = movementQueue.peek(); - - //this code adds a delay to reading from the movementQueue - //in case things being put into the movement queue are slightly - //out of order - int delayTime = 1000; - int loopTime = delayTime * 10; - long timeDiff = (System.currentTimeMillis()%loopTime - positionPacket.getTimeValid()%loopTime); - if (timeDiff < 0){ - timeDiff = loopTime + timeDiff; + try { + BoatPositionPacket positionPacket = movementQueue.take(); + Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); + double heading = 360.0 / 0xffff * positionPacket.getHeading(); + boatGroup.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), positionPacket.getTimeValid(), frameRate, boatGroup.getRaceId()); + } catch (InterruptedException e){ + e.printStackTrace(); } - if (timeDiff > delayTime) { - try { - positionPacket = movementQueue.take(); - Point2D p2d = latLonToXY(positionPacket.getLat(), positionPacket.getLon()); - double heading = 360.0 / 0xffff * positionPacket.getHeading(); - raceObject.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), (int) id); - } catch (InterruptedException e){ - e.printStackTrace(); - } +// } + } + } + + void updateMarkGroup (long raceId, MarkGroup markGroup) { + PriorityBlockingQueue movementQueue = StreamParser.markPositions.get(raceId); + if (movementQueue.size() > 0){ + try { + BoatPositionPacket positionPacket = movementQueue.take(); + Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); + markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId); + } catch (InterruptedException e){ + e.printStackTrace(); } } } + /** + * Draws all the boats. + */ + private void initializeBoats() { + 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()) { + 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() { @@ -309,15 +307,13 @@ public class CanvasController { public double prefWidth(double height) { return getWidth(); } - @Override public double prefHeight(double width) { return getHeight(); } + } - - private void drawFps(int fps){ if (raceViewController.isDisplayFps()){ gc.clearRect(5,5,50,20); @@ -334,30 +330,6 @@ public class CanvasController { } } - /** - * Draws all the boats. - */ - private void drawBoats() { -// Map timelineInfos = raceViewController.getTimelineInfos(); -// List boats = raceViewController.getStartingBoats(); - Map boats = StreamParser.getBoats(); - Double startingX = raceObjects.get(0).getLayoutX(); - Double startingY = raceObjects.get(0).getLayoutY(); - Group boatAnnotations = new Group(); - - for (Yacht boat : boats.values()) { -// for (Boat boat : boats) { - boat.setColour(Colors.getColor()); - BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); - boatGroup.moveTo(startingX, startingY, 0d); - //boatGroup.setStage(raceViewController.getStage()); - raceObjects.add(boatGroup); - boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); - } - group.getChildren().add(boatAnnotations); - group.getChildren().addAll(raceObjects); - } - /** * Calculates x and y location for every marker that fits it to the canvas the race will be drawn on. */ @@ -367,9 +339,8 @@ public class CanvasController { findMinMaxPoint(); double minLonToMaxLon = scaleRaceExtremities(); calculateReferencePointLocation(minLonToMaxLon); - givePointsXY(); + //givePointsXY(); addRaceBorder(); - findMetersToPixels(); } @@ -392,16 +363,11 @@ public class CanvasController { //If the course is on a point on the earth where longitudes wrap around. Limit minLonMark = sortedPoints.get(0); Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1); - SingleMark thisMinLon = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); - SingleMark thisMaxLon = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); - // TODO: 30/03/17 cir27 - Correctly account for longitude wrapping around. - if (thisMaxLon.getLongitude() - thisMinLon.getLongitude() > 180) { - SingleMark temp = thisMinLon; - thisMinLon = thisMaxLon; - thisMaxLon = temp; + minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); + maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); + if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) { + horizontalInversion = true; } - minLonPoint = thisMinLon; - maxLonPoint = thisMaxLon; } /** @@ -432,8 +398,12 @@ public class CanvasController { referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); referencePointX += ((CANVAS_WIDTH - (LHS_BUFFER + RHS_BUFFER)) - (minLonToMaxLon * distanceScaleFactor)) / 2; } + if(horizontalInversion) { + referencePointX = CANVAS_WIDTH - RHS_BUFFER - (referencePointX - LHS_BUFFER); + } } + /** * Finds the scale factor necessary to fit all race markers within the onscreen map and assigns it to distanceScaleFactor * Returns the max horizontal distance of the map. @@ -462,43 +432,18 @@ public class CanvasController { return horiDistance; } - /** - * Give all markers in the course an x,y location relative to a given reference with a known x,y location. Distances - * are scaled according to the distanceScaleFactor variable. - */ - private void givePointsXY() { - List allPoints = new ArrayList<>(raceViewController.getRace().getCourse()); - List processed = new ArrayList<>(); - RaceObject markGroup; - - for (Mark mark : allPoints) { - if (!processed.contains(mark)) { - if (mark.getMarkType() != MarkType.SINGLE_MARK) { - GateMark gateMark = (GateMark) mark; - markGroup = new MarkGroup(mark, findScaledXY(gateMark.getSingleMark1()), findScaledXY(gateMark.getSingleMark2())); - raceObjects.add(markGroup); - } else { - markGroup = new MarkGroup(mark, findScaledXY(mark)); - raceObjects.add(markGroup); - } - processed.add(mark); - } - } - } - private Point2D findScaledXY (Mark unscaled) { - return findScaledXY (minLatPoint.getLatitude(), minLatPoint.getLongitude(), - unscaled.getLatitude(), unscaled.getLongitude()); + return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude()); } - private Point2D findScaledXY (double latA, double lonA, double latB, double lonB) { + private Point2D findScaledXY (double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; int xAxisLocation = (int) referencePointX; int yAxisLocation = (int) referencePointY; - angleFromReference = Mark.calculateHeadingRad(latA, lonA, latB, lonB); - distanceFromReference = Mark.calculateDistance(latA, lonA, latB, lonB); + angleFromReference = Mark.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon); + distanceFromReference = Mark.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon); if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); @@ -515,44 +460,13 @@ public class CanvasController { xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } + if(horizontalInversion) { + xAxisLocation = CANVAS_WIDTH - RHS_BUFFER - (xAxisLocation - LHS_BUFFER); + } return new Point2D(xAxisLocation, yAxisLocation); } - - - /** - * Find the number of meters per pixel. - */ - private void findMetersToPixels () { - Double angularDistance; - Double angle; - Double straightLineDistance; - if (scaleDirection == ScaleDirection.HORIZONTAL) { - angularDistance = Mark.calculateDistance(minLonPoint, maxLonPoint); - angle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint); - if (angle > Math.PI / 2) { - straightLineDistance = Math.cos(angle - Math.PI) * angularDistance; - } else { - straightLineDistance = Math.cos(angle) * angularDistance; - } - metersToPixels = (CANVAS_WIDTH - RHS_BUFFER - LHS_BUFFER) / straightLineDistance; - } else { - angularDistance = Mark.calculateDistance(minLatPoint, maxLatPoint); - angle = Mark.calculateHeadingRad(minLatPoint, maxLatPoint); - if (angle < Math.PI / 2) { - straightLineDistance = Math.cos(angle) * angularDistance; - } else { - straightLineDistance = Math.cos(-angle + Math.PI * 2) * angularDistance; - } - metersToPixels = (CANVAS_HEIGHT - TOP_BUFFER - BOT_BUFFER) / straightLineDistance; - } - } - - private Point2D latLonToXY (double latitude, double longitude) { - return findScaledXY(minLatPoint.getLatitude(), minLatPoint.getLongitude(), latitude, longitude); - } - - List getRaceObjects() { - return raceObjects; + List getBoatGroups() { + return boatGroups; } } \ No newline at end of file diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index a1488a84..a222cfc3 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -9,6 +9,7 @@ import javafx.scene.layout.Pane; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import seng302.models.stream.StreamParser; public class Controller implements Initializable { @@ -33,5 +34,6 @@ public class Controller implements Initializable { public void initialize(URL location, ResourceBundle resources) { contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); setContentPane("/views/StartScreenView.fxml"); + StreamParser.boatPositions.clear(); } } diff --git a/src/main/java/seng302/controllers/RaceController.java b/src/main/java/seng302/controllers/RaceController.java deleted file mode 100644 index e5e95d59..00000000 --- a/src/main/java/seng302/controllers/RaceController.java +++ /dev/null @@ -1,82 +0,0 @@ -package seng302.controllers; - -import seng302.models.Race; -import seng302.models.Yacht; -import seng302.models.parsers.ConfigParser; -import seng302.models.parsers.CourseParser; -import seng302.models.parsers.StreamParser; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.Random; - -/** - * Created by zyt10 on 17/03/17. - * run before CanvasController to initialize race events - * the CanvasController then uses the event data to make the animations - */ -public class RaceController { - Race race = null; - - public void initializeRace() { - String raceConfigFile = "/config/config.xml"; - String teamsConfigFile = "/config/teams.xml"; - - try { - race = createRace(raceConfigFile, teamsConfigFile); //These config files arent actually used - } catch (Exception e) { - System.out.println("There was an error creating the race."); - } - - if (race != null) { - race.startRace(); - } else { - System.out.println("There was an error creating the race. Exiting."); - } - } - - public Race createRace(String configFile, String teamsConfigFile) throws Exception { - Race race = new Race(); -// StreamParser.xmlObject - // Read team names from file -// TeamsParser tp = new TeamsParser(teamsConfigFile); - - // Read course from file -// ConfigParser config = new ConfigParser(configFile); - - ArrayList boatNames = new ArrayList<>(); -// ArrayList teams = tp.getBoats(); - Map teams = StreamParser.getBoatsPos(); - - //get race size - int numberOfBoats = teams.size(); - - //get time scale -// double timeScale = config.getTimeScale(); -// race.setTimeScale(timeScale); - - for (Yacht boat : teams.values()) { - boatNames.add(boat.getBoatName()); - race.addBoat(boat); - } - - // Shuffle team names - long seed = System.nanoTime(); - Collections.shuffle(boatNames, new Random(seed)); - - if (numberOfBoats > Array.getLength(boatNames.toArray())) { - return null; - } - - CourseParser course = new CourseParser("/config/course.xml"); - race.addCourse(course.getCourse()); - - return race; - } - - public Race getRace() { - return race; - } -} diff --git a/src/main/java/seng302/controllers/RaceResultController.java b/src/main/java/seng302/controllers/RaceResultController.java deleted file mode 100644 index bff697ed..00000000 --- a/src/main/java/seng302/controllers/RaceResultController.java +++ /dev/null @@ -1,37 +0,0 @@ -package seng302.controllers; - -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import seng302.models.Race; - -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by ptg19 on 20/03/17. - */ -public class RaceResultController implements Initializable{ - @FXML private AnchorPane window; - @FXML private VBox resultsVBox; - private Race race; - - RaceResultController(Race race){ - this.race = race; - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - int boatPosition = this.race.getFinishedBoats().length; - - for (int i = this.race.getFinishedBoats().length - 1; i >= 0; i--){ - resultsVBox.getChildren().add(0, new Text(boatPosition + ": " + this.race.getFinishedBoats()[i].getBoatName())); - boatPosition--; - } - - - - } -} diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 0f2fabef..71f542ae 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -29,7 +29,7 @@ import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationDelegate; import seng302.controllers.annotations.ImportantAnnotationsState; import seng302.models.*; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; import java.io.IOException; import java.util.*; @@ -63,7 +63,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private ArrayList startingBoats = new ArrayList<>(); private boolean displayFps; private Timeline timerTimeline; - private Race race; private Stage stage; private static HashMap> sparkLineData = new HashMap<>(); private ArrayList positions; @@ -123,7 +122,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel // Load FXML and set CSS fxmlLoader .setLocation(getClass().getResource("/views/importantAnnotationSelectView.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 469, 248); + Scene scene = new Scene(fxmlLoader.load(), 469, 298); scene.getStylesheets().add(getClass().getResource("/css/master.css").toString()); stage.initStyle(StageStyle.UNDECORATED); @@ -347,7 +346,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel */ private void loadRaceResultView() { FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml")); - loader.setController(new RaceResultController(race)); try { contentAnchorPane.getChildren().removeAll(); @@ -396,17 +394,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } - - public boolean isDisplayFps() { return displayFps; } - public Race getRace() { - return race; - } - - /** * Display the important annotations for a specific BoatGroup * @param bg The boat group to set the annotations for @@ -435,7 +426,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } else { bg.setWakeVisible(false); } - + //TODO fix boat annotations with new boatgroup if (importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK)) { bg.setEstTimeToNextMarkObjectVisible(true); } else { @@ -453,39 +444,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel switch (annotationLevel) { // No Annotations case 0: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - bg.setTeamNameObjectVisible(false); - bg.setVelocityObjectVisible(false); - bg.setEstTimeToNextMarkObjectVisible(false); - bg.setLegTimeObjectVisible(false); - bg.setLineGroupVisible(false); - bg.setWakeVisible(false); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + bg.setTeamNameObjectVisible(false); + bg.setVelocityObjectVisible(false); + bg.setEstTimeToNextMarkObjectVisible(false); + bg.setLegTimeObjectVisible(false); + bg.setLineGroupVisible(false); + bg.setWakeVisible(false); } break; // Important Annotations case 1: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - setBoatGroupImportantAnnotations(bg); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + setBoatGroupImportantAnnotations(bg); } break; // All Annotations case 2: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - bg.setTeamNameObjectVisible(true); - bg.setVelocityObjectVisible(true); - bg.setEstTimeToNextMarkObjectVisible(true); - bg.setLegTimeObjectVisible(true); - bg.setLineGroupVisible(true); - bg.setWakeVisible(true); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + bg.setTeamNameObjectVisible(true); + bg.setVelocityObjectVisible(true); + bg.setEstTimeToNextMarkObjectVisible(true); + bg.setLegTimeObjectVisible(true); + bg.setLineGroupVisible(true); + bg.setWakeVisible(true); } break; } @@ -498,17 +480,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel * @param yacht The yacht for which we want to view all annotations */ private void setSelectedBoat(Yacht yacht) { - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - //We need to iterate over all race groups to get the matching boat group belonging to this boat if we - //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. - if (bg.getBoat().getHullID().equals(yacht.getHullID())) { - bg.setIsSelected(true); - selectedBoat = yacht; - } else { - bg.setIsSelected(false); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + //We need to iterate over all race groups to get the matching boat group belonging to this boat if we + //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. + if (bg.getBoat().getHullID().equals(yacht.getHullID())) { + bg.setIsSelected(true); + selectedBoat = yacht; + } else { + bg.setIsSelected(false); } } } diff --git a/src/main/java/seng302/controllers/StartScreenController.java b/src/main/java/seng302/controllers/StartScreenController.java index 8a4d563b..debeb371 100644 --- a/src/main/java/seng302/controllers/StartScreenController.java +++ b/src/main/java/seng302/controllers/StartScreenController.java @@ -22,7 +22,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import seng302.models.Yacht; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; public class StartScreenController implements Initializable { @FXML @@ -58,10 +58,10 @@ public class StartScreenController implements Initializable { contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); } catch(javafx.fxml.LoadException e){ - System.err.println(e.getCause()); + e.printStackTrace(); } catch(IOException e){ - System.err.println(e); + e.printStackTrace(); } } @@ -132,6 +132,7 @@ public class StartScreenController implements Initializable { } public void switchToRaceView() { + StreamParser.boatPositions.clear(); switchedToRaceView = true; setContentPane("/views/RaceView.fxml"); } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index f6e4a16b..3e97df5e 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -1,20 +1,21 @@ package seng302.models; +import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.CacheHint; import javafx.scene.Group; +import javafx.scene.input.MouseDragEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; -import javafx.stage.Stage; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; /** * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 @@ -24,7 +25,7 @@ import java.util.List; * minimized in which case it attempts to store animations and apply them when the window is * maximised. */ -public class BoatGroup extends RaceObject { +public class BoatGroup extends Group { //Constants for drawing private static final double TEAMNAME_X_OFFSET = 10d; @@ -38,9 +39,12 @@ public class BoatGroup extends RaceObject { private static final double BOAT_HEIGHT = 15d; private static final double BOAT_WIDTH = 10d; //Variables for boat logic. - private Point2D lastPoint; - private int wakeGenerationDelay = 10; - private double distanceTravelled; + private boolean isStopped = true; + private double xIncrement; + private double yIncrement; + private long lastTimeValid = 0; + private Double lastRotation = 0.0; + private long framesToMove; //Graphical objects private Yacht boat; private Group lineGroup = new Group(); @@ -50,51 +54,69 @@ public class BoatGroup extends RaceObject { private Text estTimeToNextMarkObject; private Text legTimeObject; private Wake wake; - private boolean isSelected = true; //Boats annotations are visible by default at the start - //Handles boat moving when connecting to a stream - private boolean setToInitialLocation = false; + private Double distanceTravelled = 0.0; + private Point2D lastPoint; private boolean destinationSet; - //Variables for handling minimization - private Stage stage; - private boolean isMaximized = true; - private List lineStorage = new ArrayList<>(); - private int setCallCount = 5; + private Color textColor = Color.RED; + + private Boolean isSelected = true; //All boats are initalised as selected /** * 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) { 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. - * @param color The colour of the boat polygon and the trailing line. + * @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. + * polygon. */ 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) { + Text text = new Text(defaultText); + + text.setFill(fill); + text.setCacheHint(CacheHint.SPEED); + text.setCache(true); + + return text; + } + /** * Creates the javafx objects that will be the in the group by default. * - * @param color The colour of the boat polygon and the trailing line. + * @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. + * polygon. */ private void initChildren(Color color, double... points) { + textColor = color; + destinationSet = false; + boatPoly = new Polygon(points); boatPoly.setFill(color); boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE)); @@ -103,28 +125,8 @@ public class BoatGroup extends RaceObject { boatPoly.setCache(true); boatPoly.setCacheHint(CacheHint.SPEED); - - teamNameObject = new Text(boat.getShortName()); - teamNameObject.setCache(true); - teamNameObject.setCacheHint(CacheHint.SPEED); - velocityObject = new Text(String.valueOf(boat.getVelocity())); - DateFormat format = new SimpleDateFormat("mm:ss"); - // TODO: 21/05/17 make a nice way of checking for this to be null - estTimeToNextMarkObject = new Text("Next mark: null"); - if (boat.getEstimateTimeAtNextMark() != null) { - String timeToNextMark = format - .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); - estTimeToNextMarkObject = new Text("Next mark: " + timeToNextMark); - } - if (boat.getMarkRoundingTime() != null) { - String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); - legTimeObject = new Text("Last mark: " + elapsedTime); - } else { - legTimeObject = new Text("Last mark: -"); - } - velocityObject.setCache(true); - velocityObject.setCacheHint(CacheHint.SPEED); + teamNameObject = getTextObject(boat.getShortName(), textColor); + velocityObject = getTextObject(boat.getVelocity().toString(), textColor); teamNameObject.setX(TEAMNAME_X_OFFSET); teamNameObject.setY(TEAMNAME_Y_OFFSET); @@ -133,21 +135,28 @@ public class BoatGroup extends RaceObject { velocityObject.setX(VELOCITY_X_OFFSET); velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); - destinationSet = false; - estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); - estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); - estTimeToNextMarkObject + updateLastMarkRoundingTime(); + updateTimeTillNextMark(); + + if (estTimeToNextMarkObject != null) { + estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); + estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); + estTimeToNextMarkObject .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + } - legTimeObject.setX(LEGTIME_X_OFFSET); - legTimeObject.setY(LEGTIME_Y_OFFSET); - legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + if (legTimeObject != null) { + legTimeObject.setX(LEGTIME_X_OFFSET); + legTimeObject.setY(LEGTIME_Y_OFFSET); + legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + + } wake = new Wake(0, -BOAT_HEIGHT); super.getChildren() - .addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject, - legTimeObject); + .addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject, + legTimeObject); } /** @@ -157,9 +166,9 @@ public class BoatGroup extends RaceObject { */ private void initChildren(Color color) { initChildren(color, - -BOAT_WIDTH / 2, BOAT_HEIGHT / 2, - 0.0, -BOAT_HEIGHT / 2, - BOAT_WIDTH / 2, BOAT_HEIGHT / 2); + -BOAT_WIDTH / 2, BOAT_HEIGHT / 2, + 0.0, -BOAT_HEIGHT / 2, + BOAT_WIDTH / 2, BOAT_HEIGHT / 2); } /** @@ -169,7 +178,7 @@ public class BoatGroup extends RaceObject { * @param dx The amount to move the X coordinate by * @param dy The amount to move the Y coordinate by */ - public void moveGroupBy(double dx, double dy, double rotation) { + private void moveGroupBy(double dx, double dy) { boatPoly.setLayoutX(boatPoly.getLayoutX() + dx); boatPoly.setLayoutY(boatPoly.getLayoutY() + dy); teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx); @@ -182,20 +191,8 @@ public class BoatGroup extends RaceObject { legTimeObject.setLayoutY(legTimeObject.getLayoutY() + dy); wake.setLayoutX(wake.getLayoutX() + dx); wake.setLayoutY(wake.getLayoutY() + dy); - rotateTo(rotation + currentRotation); } - /** - * 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 @@ -203,7 +200,8 @@ public class BoatGroup extends RaceObject { * @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) { + private void moveTo(double x, double y, double rotation) { + rotateTo(rotation); boatPoly.setLayoutX(x); boatPoly.setLayoutY(y); teamNameObject.setLayoutX(x); @@ -216,40 +214,109 @@ public class BoatGroup extends RaceObject { legTimeObject.setLayoutY(y); wake.setLayoutX(x); wake.setLayoutY(y); - wake.rotate(currentRotation); + wake.rotate(rotation); + } + + private void rotateTo(double rotation) { + boatPoly.getTransforms().setAll(new Rotate(rotation)); } /** - * 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. + * Updates the time until next mark label, will create a label if one doesn't exist */ - public void updatePosition(long timeInterval) { - double dx = pixelVelocityX * timeInterval; - double dy = pixelVelocityY * timeInterval; - double rotation = rotationalVelocity * timeInterval; + private void updateTimeTillNextMark() { + if (estTimeToNextMarkObject == null) { + estTimeToNextMarkObject = getTextObject("Next mark: -", textColor); + } + if (boat.getEstimateTimeAtNextMark() != null) { + DateFormat format = new SimpleDateFormat("mm:ss"); + String timeToNextMark = format + .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); + estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); + } else { + estTimeToNextMarkObject.setText("Next mark: -"); + } + } + + /** + * Updates the time since last mark rounding, will create a label if one doesn't exist + */ + private void updateLastMarkRoundingTime() { + if (legTimeObject == null) { + legTimeObject = getTextObject("Last mark: -", textColor); + } + + if (boat.getMarkRoundingTime() != null) { + DateFormat format = new SimpleDateFormat("mm:ss"); + String elapsedTime = format + .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); + legTimeObject.setText("Last mark: " + elapsedTime); + } else { + legTimeObject.setText("Last mark: -"); + + } + } + + public void move() { + double dx = xIncrement * framesToMove; + double dy = yIncrement * framesToMove; + distanceTravelled += Math.abs(dx) + Math.abs(dy); - moveGroupBy(dx, dy, rotation); - //Draw a new section of the trail every 20 pixels of movement. - if (distanceTravelled > 20) { - distanceTravelled = 0; + moveGroupBy(xIncrement, yIncrement); + framesToMove = framesToMove - 1; + + if (framesToMove <= 0) { + isStopped = true; + } + + if (distanceTravelled > 70) { + distanceTravelled = 0d; + 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()); + l.setCache(true); + l.setCacheHint(CacheHint.SPEED); lineGroup.getChildren().add(l); } - if (destinationSet) { //Only begin drawing after the first destination is set + + if (destinationSet) { lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); } } - wake.updatePosition(timeInterval); + + wake.updatePosition(1000 / 60); + } + + /** + * Calculates the rotational velocity required to reach the rotationalGoal from the + * currentRotation. + */ + protected Double calculateRotationalVelocity(Double rotationalGoal) { + Double rotationalVelocity = 0.0; + + if (Math.abs(rotationalGoal - lastRotation) > 180) { + if (rotationalGoal - lastRotation >= 0.0) { + rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200; + } else { + rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200; + } + } else { + rotationalVelocity = (rotationalGoal - lastRotation) / 200; + } + + //Sometimes the rotation is too large to be realistic. In that case just do it instantly. + if (Math.abs(rotationalVelocity) > 1) { + rotationalVelocity = 0.0; + } + + return rotationalVelocity; } /** @@ -257,87 +324,55 @@ public class BoatGroup extends RaceObject { * * @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 raceIds RaceID of the object to move. + * @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, int... raceIds) { - if (hasRaceId(raceIds)) { - if (setToInitialLocation) { - destinationSet = true; - boat.setVelocity(groundSpeed); - double dx = newXValue - boatPoly.getLayoutX(); - double dy = newYValue - boatPoly.getLayoutY(); - - - pixelVelocityX = dx / expectedUpdateInterval; - pixelVelocityY = dy / expectedUpdateInterval; - rotationalGoal = rotation; - calculateRotationalVelocity(); - - if (Math.abs(rotationalVelocity) > 0.075) { - rotationalVelocity = 0; - rotateTo(rotationalGoal); - wake.rotate(rotationalGoal); - } - wake.setRotationalVelocity(rotationalVelocity, boat.getVelocity()); - velocityObject.setText(String.format("%.2f m/s", boat.getVelocity())); - DateFormat format = new SimpleDateFormat("mm:ss"); - // estimate time to next mark - // TODO: 21/05/17 make a nice way of checking for this to be null - estTimeToNextMarkObject.setText("Next mark: null"); - if (boat.getEstimateTimeAtNextMark() != null) { - String timeToNextMark = format - .format( - boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); - estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); - } - // elapsed time - if (boat.getMarkRoundingTime() != null) { - String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); - legTimeObject.setText("Last mark: " + elapsedTime); - } else { - legTimeObject.setText("Last mark: -"); - } - } else { - setToInitialLocation = true; - rotationalGoal = rotation; - moveTo(newXValue, newYValue, rotation); - } + double groundSpeed, long timeValid, double frameRate, long id) { + if (lastTimeValid == 0) { + lastTimeValid = timeValid - 200; + moveTo(newXValue, newYValue, rotation); } - //If minimized generate lines every 5 calls to set destination. - } + framesToMove = Math.round((frameRate / (1000.0f / (timeValid - lastTimeValid)))); + double dx = newXValue - boatPoly.getLayoutX(); + double dy = newYValue - boatPoly.getLayoutY(); + + xIncrement = dx / framesToMove; + yIncrement = dy / framesToMove; - public void setDestination(double newXValue, double newYValue, double groundSpeed, - int... raceIDs) { destinationSet = true; - if (hasRaceId(raceIDs)) { - double rotation = Math.abs( - Math.toDegrees( - Math.atan( - (newYValue - boatPoly.getLayoutY()) / (newXValue - boatPoly.getLayoutX()) - ) - ) - ); - setDestination(newXValue, newYValue, rotation, groundSpeed, raceIDs); + Double rotationalVelocity = calculateRotationalVelocity(rotation); + + updateTimeTillNextMark(); + updateLastMarkRoundingTime(); + + if (Math.abs(rotationalVelocity) > 0.075) { + rotationalVelocity = 0.0; + wake.rotate(rotation); } + + rotateTo(rotation); + wake.setRotationalVelocity(rotationalVelocity, groundSpeed); + + velocityObject.setText(String.format("%.2f m/s", groundSpeed)); + lastTimeValid = timeValid; + isStopped = false; + + lastRotation = rotation; } - public void rotateTo(double rotation) { - currentRotation = rotation; - boatPoly.getTransforms().setAll(new Rotate(rotation)); + + public void setIsSelected(Boolean isSelected) { + this.isSelected = isSelected; + setTeamNameObjectVisible(isSelected); + setVelocityObjectVisible(isSelected); + setLineGroupVisible(isSelected); + setWakeVisible(isSelected); + setEstTimeToNextMarkObjectVisible(isSelected); + setLegTimeObjectVisible(isSelected); } - public void forceRotation() { - rotateTo(rotationalGoal); - wake.rotate(rotationalGoal); - } - - public void paintBoat(Color color) { - boatPoly.setFill(color); - } public void setTeamNameObjectVisible(Boolean visible) { teamNameObject.setVisible(visible); @@ -367,46 +402,13 @@ public class BoatGroup extends RaceObject { return boat; } - /** - * This function sets the boats isSelected property AS WELL as actually acting upon the value of - * that selection. (Painting or not painting annotations) - * - * @param isSelected A Boolean indicating whether or not the boat is selected - */ - public void setIsSelected(Boolean isSelected) { - this.isSelected = isSelected; - setTeamNameObjectVisible(isSelected); - setVelocityObjectVisible(isSelected); - setLineGroupVisible(isSelected); - setWakeVisible(isSelected); - setEstTimeToNextMarkObjectVisible(isSelected); - setLegTimeObjectVisible(isSelected); - // TODO: 17/05/17 wmu16 - this should iterate over some list of annotations which we should make to easily make extensible -// paintBoat((isSelected) ? Color.WHITE : boat.getColour()); - } - - /** - * 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.getSourceID()) { - return true; - } - } - 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.getSourceID()}; + public long getRaceId() { + return boat.getSourceID(); } /** @@ -422,30 +424,12 @@ public class BoatGroup extends RaceObject { return group; } - /** - * Use this function to let the BoatGroup know about the stage it is in. If it knows about it's - * stage then it will listen to the iconified property of that stage and change it's behaviour - * upon minimization. Without setting the Stage there is guarantee that the BoatGroup will draw - * properly when the stage is minimized. - * - * @param stage The stage that the BoatGroup is added to. - */ - public void setStage(Stage stage) { - /* TODO: 4/05/17 cir27 - Find a way to get the stage to this point. Need to pass it through multiple controllers. - App.start() -> Controller.setContentPane -> RaceViewController -> CanvasController - */ - this.stage = stage; - this.stage.iconifiedProperty().addListener(e -> { - isMaximized = !stage.isIconified(); - if (!lineStorage.isEmpty()) { - lineGroup.getChildren().addAll(lineStorage); - lineStorage.clear(); - } - }); + public boolean isStopped() { + return isStopped; } @Override public String toString() { return boat.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/seng302/models/Colors.java b/src/main/java/seng302/models/Colors.java index 23ef8f4e..8d1b9c25 100644 --- a/src/main/java/seng302/models/Colors.java +++ b/src/main/java/seng302/models/Colors.java @@ -6,7 +6,7 @@ import javafx.scene.paint.Color; * Created by ryan_ on 16/03/2017. */ public enum Colors { - RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE; + RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE; static Integer index = 0; diff --git a/src/main/java/seng302/models/Race.java b/src/main/java/seng302/models/Race.java deleted file mode 100644 index e78b602a..00000000 --- a/src/main/java/seng302/models/Race.java +++ /dev/null @@ -1,198 +0,0 @@ -package seng302.models; - -import seng302.models.mark.Mark; - -import java.util.*; - -/** - * Race class containing the boats and legs in the race - * Created by mra106 on 8/3/2017. - */ -public class Race { - - private ArrayList boats; // The boats in the race - private ArrayList finishingOrder; // The order in which the boats finish the race - private HashMap events = new HashMap<>(); // The events that occur in the race - private List course; // Marks in the race - private long startTime = 0; - private double timeScale = 1; - private boolean raceFinished = false; // Race is finished - private int raceTime = -2; // Current time in the race - - /** - * Race class containing the boats and legs in the race - */ - public Race() { - this.boats = new ArrayList<>(); - this.finishingOrder = new ArrayList<>(); - this.course = new ArrayList<>(); - } - - /** - * Add a boat to the race - * - * @param boat, the boat to add - */ - public void addBoat(Yacht boat) { - boats.add(boat); - } - - /** - * Returns a list of boats in a random order - * - * @return a list of boats - */ - public Yacht[] getShuffledBoats() { - // Shuffle the list of boats - long seed = System.nanoTime(); - Collections.shuffle(this.boats, new Random(seed)); - - return boats.toArray(new Yacht[boats.size()]); - } - - /** - * Returns a list of boats in the order that they - * finished the race (position 0 is first place) - * - * @return a list of boats - */ - public Yacht[] getFinishedBoats() { - return this.finishingOrder.toArray(new Yacht[this.finishingOrder.size()]); - } - - - /** - * Returns a list of boats in the race - * - * @return a list of the boats competing in the race - */ - public Yacht[] getBoats() { - return boats.toArray(new Yacht[boats.size()]); - } - - /** - * Sets time scale - * - * @param timeScale - */ - public void setTimeScale(double timeScale) { - this.timeScale = timeScale; - } - - /** - * Generate all events that will happen during the race. - */ - private void generateEvents() { - - for (Yacht boat : this.boats) { - double totalDistance = 0; - int numberOfMarks = this.course.size(); - - for (int i = 0; i < numberOfMarks; i++) { - Double time = (totalDistance / boat.getVelocity() / timeScale); - - // If there are singleMarks after this event - if (i < numberOfMarks - 1) { - Event event = new Event(time, boat, course.get(i), course.get(i + 1), i); - - try { - events.get(boat).add(event); - - } catch (NullPointerException e) { - events.put(boat, new ArrayList<>(Arrays.asList(event))); - } - totalDistance += event.getDistanceBetweenMarks(); - //System.out.println(totalDistance); - //System.out.println(boat.getVelocity()); - } - - // There are no more marks after this event - - else{ - Event event = new Event(time, boat, course.get(i), i); - events.get(boat).add(event); - } - } - } - } - - - /** - * Starts a race and generates all events for the race. - */ - public void startRace() { - // record start time. - this.startTime = System.currentTimeMillis(); - generateEvents(); - } - - /** - * Set the race course - * @param course a list of marks in the course - */ - public void addCourse(List course) { - this.course = course; - } - - /** - * Get a list of marks in the course - * @return - */ - public List getCourse() { - return course; - } - - /** - * Get a map of the events in the race - * @return - */ - public HashMap getEvents() { - return events; - } - - /** - * Set a boat as finished - * @param boat The boat that has finished the race/home/cosc/student/wmu16 - */ - public void setBoatFinished(Yacht boat){ - this.finishingOrder.add(boat); - } - - /** - * Set the race as finished - */ - public void setRaceFinished(){ - this.raceFinished = true; - } - - /** - * Return whether or not the race is finished - * @return true if the race is finished - */ - public boolean isRaceFinished(){ - return this.raceFinished; - } - - /** - * Set the race time - * @param raceTime the race time in seconds - */ - public void setRaceTime(int raceTime){ - this.raceTime = raceTime; - } - - /** - * Return the race time - * @return the race time in seconds - */ - public int getRaceTime(){ - return this.raceTime; - } - - /** - * Increment the race time by one second - */ - public void incrementRaceTime(){ - this.raceTime += this.timeScale; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java deleted file mode 100644 index 91f02971..00000000 --- a/src/main/java/seng302/models/RaceObject.java +++ /dev/null @@ -1,87 +0,0 @@ -package seng302.models; - -import javafx.geometry.Point2D; -import javafx.scene.Group; - -/** - * RaceObject defines the behaviour that animated objects whose position is updated from a yacht race data stream must - * adhere to. - */ -public abstract class RaceObject extends Group { - - //Time between sections of race - protected static double expectedUpdateInterval = 200; - - protected double rotationalGoal; - protected double currentRotation; - protected double rotationalVelocity; - protected double pixelVelocityX; - protected double pixelVelocityY; - - public Point2D getPosition () { - return new Point2D(super.getLayoutX(), getLayoutY()); - } - - public static double getExpectedUpdateInterval() { - 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) { - this.rotationalVelocity = ((rotationalGoal - currentRotation) - 360) / expectedUpdateInterval; - } else { - this.rotationalVelocity = (360 + (rotationalGoal - currentRotation)) / expectedUpdateInterval; - } - } 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 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, double groundSpeed, 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, double groundSpeed, int... raceIds); - - public abstract void updatePosition (long timeInterval); - - public abstract void moveTo (double x, double y, double rotation); - - public abstract void moveTo (double x, double y); - - public abstract void moveGroupBy(double x, double y, double rotation); - - public abstract void rotateTo (double rotation); - - public abstract boolean hasRaceId (int... raceIds); - - public abstract int[] getRaceIds (); -} diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 630b075a..886dfba8 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -63,8 +63,8 @@ class Wake extends Group { for (int i = 1; i < numWakes; i++) { double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]); double shortestDistance = Math.atan2( - Math.sin(wakeSeparationRad), - Math.cos(wakeSeparationRad) + Math.sin(wakeSeparationRad), + Math.cos(wakeSeparationRad) ); double distDeg = Math.toDegrees(shortestDistance); diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index 22472810..b55379df 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -165,7 +165,7 @@ public class Yacht { this.colour = colour; } - public double getVelocity() { + public Double getVelocity() { return velocity; } 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 d58f3b78..c87ae174 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -1,21 +1,17 @@ package seng302.models.mark; -import javafx.geometry.Point2D; -import javafx.scene.CacheHint; -import javafx.scene.Node; -import javafx.scene.paint.Color; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import javafx.scene.transform.Rotate; -import seng302.models.RaceObject; - 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; /** * Created by CJIRWIN on 26/04/2017. */ -public class MarkGroup extends RaceObject { +public class MarkGroup extends Group { private static int MARK_RADIUS = 5; private static int LINE_THICKNESS = 2; @@ -24,14 +20,13 @@ public class MarkGroup extends RaceObject { private List marks = new ArrayList<>(); private Mark mainMark; - private double[] nodePixelVelocitiesX; - private double[] nodePixelVelocitiesY; - private Point2D[] nodeDestinations; - public MarkGroup (Mark mark, Point2D... points) { - nodePixelVelocitiesX = new double[points.length]; - nodePixelVelocitiesY = new double[points.length]; - nodeDestinations = new Point2D[points.length]; + /** + * Constructor for singleMark groups + * @param mark + * @param points + */ + public MarkGroup (SingleMark mark, Point2D points) { marks.add(mark); mainMark = mark; Color color = Color.BLACK; @@ -41,192 +36,94 @@ public class MarkGroup extends RaceObject { color = Color.RED; } Circle markCircle; - if (mark.getMarkType() == MarkType.SINGLE_MARK) { - 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()); - nodePixelVelocitiesX = new double[]{0d,0d}; - nodePixelVelocitiesY = new double[]{0d,0d}; - nodeDestinations = new Point2D[2]; + markCircle = new Circle( + points.getX(), + points.getY(), + MARK_RADIUS, + color + ); + super.getChildren().add(markCircle); + } - markCircle = new Circle( - points[0].getX(), - points[0].getY(), - MARK_RADIUS, - color - ); - nodeDestinations[0] = new Point2D(markCircle.getCenterX(), markCircle.getCenterY()); - super.getChildren().add(markCircle); - - 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[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); + 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); - public void setDestination (double x, double y, double rotation, double groundSpeed, int... raceIds) { - setDestination(x, y, 0, raceIds); - this.rotationalGoal = rotation; - calculateRotationalVelocity(); - } - - public void setDestination (double x, double y, double groundSpeed, int... raceIds) { - for (int i = 0; i < marks.size(); i++) - for (int id : raceIds) - if (id == marks.get(i).getId()) - setDestinationChild(x, y, 0, Math.max(0, i-1)); - } - - - private void setDestinationChild (double x, double y, double speed, int childIndex) { - //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; - //} - } - - public void rotateTo (double 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)); + 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 updatePosition (long timeInterval) { - Circle markCircle = (Circle) super.getChildren().get(0); + public void moveMarkTo (double x, double y, long raceId) + { + if (mainMark.getMarkType() == MarkType.SINGLE_MARK) { + 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) { - 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); - mark.setCenterY(mark.getCenterY() + y); - mark.setCenterX(mark.getCenterX() + x); - } - rotateTo(currentRotation + rotation); - } - - public void moveTo (double x, double y, double rotation) { - moveTo(x, y); - rotateTo(rotation); - } - - public void moveTo (double x, double 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); + } 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(0).getId() == raceId) { + markCircle1.setCenterX(x); + markCircle1.setCenterY(y); + connectingLine.setStartX(markCircle1.getCenterX()); + connectingLine.setStartY(markCircle1.getCenterY()); + } else if (marks.get(1).getId() == raceId) { + markCircle2.setCenterX(x); + markCircle2.setCenterY(y); + connectingLine.setEndX(markCircle2.getCenterX()); + connectingLine.setEndY(markCircle2.getCenterY()); + } } } public boolean hasRaceId (int... raceIds) { for (int id : raceIds) for (Mark mark : marks) - if (id == mark.getId()) - return true; + if (id == mark.getId()) + return true; return false; } - public static int getMarkRadius() { - return MARK_RADIUS; - } - - public static void setMarkRadius(int markRadius) { - MARK_RADIUS = markRadius; - } - - 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(); return idArray; } -} +} \ No newline at end of file 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/parsers/ConfigParser.java b/src/main/java/seng302/models/parsers/ConfigParser.java deleted file mode 100644 index 1d870c67..00000000 --- a/src/main/java/seng302/models/parsers/ConfigParser.java +++ /dev/null @@ -1,78 +0,0 @@ -package seng302.models.parsers; - - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.DoubleSummaryStatistics; - -public class ConfigParser extends FileParser { - - private Document doc; - - public ConfigParser(String path) { - super(path); - this.doc = this.parseFile(); - } - - /** - * Gets wind direction from config file. - * - * @return a double type degree, or 0 if no value or invalid value is found - */ - public double getWindDirection() { - return getDoubleByTagName("wind-direction", 0.0); - } - - /** - * Gets a non negative time scale for the race - * - * @return a double type scale, or 0 if no scale or invalid scale is found - */ - public double getTimeScale() { - return getDoubleByTagName("time-scale", 1.0); - } - - /** - * Gets a double type number by given tag name found in xml file - * - * @param tagName a string of tag name - * @param defaultVal value returned if no value or invalid value is found - * @return value found - */ - public double getDoubleByTagName(String tagName, double defaultVal) { - double val = defaultVal; - try { - Node node = this.doc.getElementsByTagName(tagName).item(0); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - val = Double.valueOf(element.getTextContent()); - } - } catch (Exception e) { - } finally { - return val; - } - } - - /** - * Gets a string by given tag name found in xml file - * - * @param tagName a string of tag name - * @param defaultVal a string returned if no value or invalid value is found - * @return string found - */ - public String getStringByTagName(String tagName, String defaultVal) { - String string = defaultVal; - try { - Node node = this.doc.getElementsByTagName(tagName).item(0); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - string = element.getTextContent(); - } - } catch (Exception e) { - } finally { - return string; - } - } -} diff --git a/src/main/java/seng302/models/parsers/CourseParser.java b/src/main/java/seng302/models/parsers/CourseParser.java deleted file mode 100644 index ae7f7856..00000000 --- a/src/main/java/seng302/models/parsers/CourseParser.java +++ /dev/null @@ -1,145 +0,0 @@ -package seng302.models.parsers; - -import org.w3c.dom.*; -import seng302.models.mark.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * parse a course xml file - * Created by Haoming Yin (hyi25) on 16/3/2017 - */ -public class CourseParser extends FileParser { - - private Document doc; - private HashMap marks = new HashMap<>(); - - public CourseParser(String path) { - super(path); - this.doc = this.parseFile(); - } - - /** - * create a mark by given node - * - * @param node - * @return a mark, or null if fails to create a mark - */ - private SingleMark generateSingleMark(Node node) { - try { - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - 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()); - 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."); - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * generate an arrayList of gates - * - * @return an arrayList of gates, or null if no gate has been found. - */ - private void generateGateMarks() { - ArrayList gateMarks = new ArrayList<>(); - - try { - NodeList nodes = doc.getElementsByTagName("gate"); - - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - String name = element.getElementsByTagName("name").item(0).getTextContent(); - SingleMark mark1 = generateSingleMark(element.getElementsByTagName("mark").item(0)); - SingleMark mark2 = generateSingleMark(element.getElementsByTagName("mark").item(1)); - GateMark gateMark; - if (name.equals("Start") || name.equals("Finish")) - gateMark = new GateMark(name, MarkType.CLOSED_GATE, mark1, mark2, mark1.getLatitude(), mark1.getLongitude()); - else - gateMark = new GateMark(name, MarkType.OPEN_GATE, mark1, mark2, mark1.getLatitude(), mark1.getLongitude()); - marks.put(name, gateMark); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * generate an arrayList of marks - * - * @return an arrayList of marks, or null if no gate has been found. - */ - private void generateSingleMarks() { - ArrayList singleMarks = new ArrayList<>(); - - try { - // find the "marks" tag - Node node = doc.getElementsByTagName("marks").item(0); - // iterate all "marks"'s children - for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { - // if node's tag name is "mark" - if (n.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) n; - if (element.getNodeName() == "mark") { - Mark mark = generateSingleMark(n); - marks.put(mark.getName(), mark); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * return the order of all the marks along a course - * - * @return an arrayList of the names of ordered course marks - */ - private ArrayList getOrder() { - ArrayList markOrder = new ArrayList<>(); - - try { - Node orderNode = doc.getElementsByTagName("order").item(0); - for (Node node = orderNode.getFirstChild(); node != null; node = node.getNextSibling()) { - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - String name = element.getTextContent(); - markOrder.add(name); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return markOrder; - } - - public ArrayList getCourse() { - generateSingleMarks(); - generateGateMarks(); - ArrayList course = new ArrayList<>(); - try { - for (String mark : getOrder()) { - course.add(marks.get(mark)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return course; - } -} diff --git a/src/main/java/seng302/models/parsers/FileParser.java b/src/main/java/seng302/models/parsers/FileParser.java deleted file mode 100644 index be162b9e..00000000 --- a/src/main/java/seng302/models/parsers/FileParser.java +++ /dev/null @@ -1,54 +0,0 @@ -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 - */ -public abstract class FileParser { - - private String filePath; - - public FileParser() {} - - public FileParser(String path) { - this.filePath = path; - } - - protected Document parseFile() { - try { - InputStream is = getClass().getResourceAsStream(this.filePath); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - // optional, in order to recover info from broken line. - doc.getDocumentElement().normalize(); - return doc; - } catch (Exception e) { - 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/TeamsParser.java b/src/main/java/seng302/models/parsers/TeamsParser.java deleted file mode 100644 index fae0a743..00000000 --- a/src/main/java/seng302/models/parsers/TeamsParser.java +++ /dev/null @@ -1,64 +0,0 @@ -//package seng302.models.parsers; -// -//import org.w3c.dom.*; -//import seng302.models.Yacht; -// -//import java.util.ArrayList; -//import java.util.NoSuchElementException; -// -//public class TeamsParser extends FileParser { -// -// private Document doc; -// -// public TeamsParser(String path) { -// super(path); -// this.doc = this.parseFile(); -// } -// -// /** -// * Create a boat instance by a given team node -// * @param node a boat node containing name, alias and velocity -// * @return an instance of Boat -// */ -// private Yacht parseBoat(Node node) { -// try { -// if (node.getNodeType() == Node.ELEMENT_NODE) { -// Element element = (Element) node; -// String name = element.getElementsByTagName("name").item(0).getTextContent(); -// String alias = element.getElementsByTagName("alias").item(0).getTextContent(); -// double velocity = Double.valueOf(element.getElementsByTagName("velocity").item(0).getTextContent()); -// int id = Integer.valueOf(element.getElementsByTagName("id").item(0).getTextContent()); -// Yacht boat = new Yacht(name, velocity, alias, id); -// return boat; -// } else { -// throw new NoSuchElementException("Cannot generate a boat by given node"); -// } -// } catch (Exception e) { -// e.printStackTrace(); -// return null; -// } -// } -// -// /** -// * Create an arraylist of boats instance. -// * @return an arraylist of boats in teams file -// */ -// public ArrayList getBoats() { -// ArrayList boats = new ArrayList<>(); -// -// try { -// NodeList nodes = this.doc.getElementsByTagName("team"); -// for (int i = 0; i < nodes.getLength(); i++) { -// Node node = nodes.item(i); -// boats.add(parseBoat(node)); -// } -// return boats; -// } catch (Exception e) { -// e.printStackTrace(); -// return null; -// } -// } -// -// -//} -// diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java similarity index 93% rename from src/main/java/seng302/models/parsers/StreamParser.java rename to src/main/java/seng302/models/stream/StreamParser.java index 837f459f..f800379c 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -1,24 +1,29 @@ -package seng302.models.parsers; +package seng302.models.stream; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import seng302.models.Yacht; -import seng302.models.parsers.packets.BoatPositionPacket; -import seng302.models.parsers.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.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; +import seng302.models.Yacht; +import seng302.models.stream.packets.BoatPositionPacket; +import seng302.models.stream.packets.StreamPacket; /** * The purpose of this class is to take in the stream of divided packets so they can be read @@ -28,6 +33,7 @@ import java.util.concurrent.PriorityBlockingQueue; */ public class StreamParser extends Thread{ + public static ConcurrentHashMap> markPositions = new ConcurrentHashMap<>(); public static ConcurrentHashMap> boatPositions = new ConcurrentHashMap<>(); private String threadName; private Thread t; @@ -68,24 +74,10 @@ public class StreamParser extends Thread{ Thread.sleep(1); } while (appRunning){ - StreamPacket packet = StreamReceiver.packetBuffer.peek(); - //this code adds a delay to reading from the packetBuffer so - //out of order packets have time to order themselves in the queue - int delayTime = 1000; - int loopTime = delayTime * 10; - long transitTime = (System.currentTimeMillis()%loopTime - packet.getTimeStamp()%loopTime); - if (transitTime < 0){ - transitTime = loopTime + transitTime; - } - if (transitTime < delayTime) { - long sleepTime = delayTime - (transitTime); - Thread.sleep(sleepTime); - } - packet = StreamReceiver.packetBuffer.take(); + StreamPacket packet = StreamReceiver.packetBuffer.take(); parsePacket(packet); Thread.sleep(1); while (StreamReceiver.packetBuffer.peek() == null) { - Thread.sleep(1); } } } catch (Exception e){ @@ -224,7 +216,6 @@ public class StreamParser extends Thread{ raceFinished = false; System.out.println("[CLIENT] Race has started"); } - //System.out.println("Time since start: " + -1 * timeTillStart + " Seconds"); timeSinceStart = timeTillStart; } @@ -233,11 +224,10 @@ public class StreamParser extends Thread{ int noBoats = payload[22]; int raceType = payload[23]; -// ArrayList boatStatuses = new ArrayList<>(); boatsPos = new TreeMap<>(); for (int i = 0; i < noBoats; i++){ - Long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); - Yacht boat = boats.get((int)(long) boatStatusSourceID); + long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); + Yacht boat = boats.get((int) boatStatusSourceID); boat.setBoatStatus((int)payload[28 + (i * 20)]); boat.setLegNumber((int)payload[29 + (i * 20)]); boat.setPenaltiesAwarded((int)payload[30 + (i * 20)]); @@ -317,6 +307,7 @@ public class StreamParser extends Thread{ boats = xmlObject.getBoatXML().getCompetingBoats(); } if (messageType == 6) { //6 is race info xml + newRaceXmlReceived = true; } } @@ -365,7 +356,6 @@ public class StreamParser extends Thread{ long subjectId = bytesToLong(Arrays.copyOfRange(payload,9,13)); long incidentId = bytesToLong(Arrays.copyOfRange(payload,13,17)); int eventId = payload[17]; -// System.out.println("eventId = " + eventId); } /** @@ -403,20 +393,33 @@ public class StreamParser extends Thread{ double groundSpeed = bytesToLong(Arrays.copyOfRange(payload,38,40))/1000.0; //type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat - if (deviceType == 1 || deviceType == 3){ + if (deviceType == 1){ BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed); //add a new priority que to the boatPositions HashMap if (!boatPositions.containsKey(boatId)){ - boatPositions.put(boatId, new PriorityBlockingQueue(256, new Comparator() { + boatPositions.put(boatId, new PriorityBlockingQueue<>(256, new Comparator() { @Override public int compare(BoatPositionPacket p1, BoatPositionPacket p2) { return (int) (p1.getTimeValid() - p2.getTimeValid()); } })); } - //Adding the boatPacket to the priority que 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/parsers/StreamReceiver.java b/src/main/java/seng302/models/stream/StreamReceiver.java similarity index 97% rename from src/main/java/seng302/models/parsers/StreamReceiver.java rename to src/main/java/seng302/models/stream/StreamReceiver.java index 65d7c525..b3303806 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/stream/StreamReceiver.java @@ -1,13 +1,11 @@ -package seng302.models.parsers; +package seng302.models.stream; -import seng302.models.parsers.packets.StreamPacket; +import seng302.models.stream.packets.StreamPacket; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; -import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; import java.util.zip.CRC32; diff --git a/src/main/java/seng302/models/parsers/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java similarity index 65% rename from src/main/java/seng302/models/parsers/XMLParser.java rename to src/main/java/seng302/models/stream/XMLParser.java index f8d30460..674a2611 100644 --- a/src/main/java/seng302/models/parsers/XMLParser.java +++ b/src/main/java/seng302/models/stream/XMLParser.java @@ -1,27 +1,29 @@ -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 seng302.models.Yacht; +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 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,57 +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 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 getsourceID() { + return sourceID; } - public Integer getMarkID() { return markID; } - public String getcMarkName() { return cMarkName; } - 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; @@ -357,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; @@ -374,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; + } } } @@ -402,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) { @@ -421,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)); } @@ -432,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 diff --git a/src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java b/src/main/java/seng302/models/stream/packets/BoatPositionPacket.java similarity index 95% rename from src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java rename to src/main/java/seng302/models/stream/packets/BoatPositionPacket.java index d6f0700d..859223e0 100644 --- a/src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java +++ b/src/main/java/seng302/models/stream/packets/BoatPositionPacket.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; public class BoatPositionPacket { private long boatId; diff --git a/src/main/java/seng302/models/parsers/packets/PacketType.java b/src/main/java/seng302/models/stream/packets/PacketType.java similarity index 96% rename from src/main/java/seng302/models/parsers/packets/PacketType.java rename to src/main/java/seng302/models/stream/packets/PacketType.java index f522dec9..0fd0be84 100644 --- a/src/main/java/seng302/models/parsers/packets/PacketType.java +++ b/src/main/java/seng302/models/stream/packets/PacketType.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; /** * Created by Kusal on 4/24/2017. @@ -48,6 +48,4 @@ public enum PacketType { } return OTHER; } - - } diff --git a/src/main/java/seng302/models/parsers/packets/StreamPacket.java b/src/main/java/seng302/models/stream/packets/StreamPacket.java similarity index 95% rename from src/main/java/seng302/models/parsers/packets/StreamPacket.java rename to src/main/java/seng302/models/stream/packets/StreamPacket.java index 4f10008c..22f2fe56 100644 --- a/src/main/java/seng302/models/parsers/packets/StreamPacket.java +++ b/src/main/java/seng302/models/stream/packets/StreamPacket.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; /** * Created by kre39 on 23/04/17. diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 970f8200..13ecaa7e 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -6,17 +6,6 @@ - - - - - - - - - - - diff --git a/src/test/java/seng302/ColorsTest.java b/src/test/java/seng302/ColorsTest.java index 6fc07b41..03bc9ad2 100644 --- a/src/test/java/seng302/ColorsTest.java +++ b/src/test/java/seng302/ColorsTest.java @@ -8,14 +8,11 @@ import seng302.models.Colors; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -/** - * Created by ryan_ on 16/03/2017. - */ public class ColorsTest { @Test public void testNextColor() { - Color expectedColors[] = {Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.PURPLE}; + Color expectedColors[] = {Color.RED, Color.PERU, Color.SEAGREEN, Color.GREEN, Color.BLUE, Color.PURPLE}; for (int i = 0; i<6; i++) { Assert.assertEquals(expectedColors[i], Colors.getColor()); diff --git a/src/test/java/seng302/RaceTest.java b/src/test/java/seng302/RaceTest.java deleted file mode 100644 index 2784cd47..00000000 --- a/src/test/java/seng302/RaceTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package seng302; - -import org.junit.Test; -import seng302.models.Race; -import seng302.models.Yacht; - -import java.lang.reflect.Array; - -import static org.junit.Assert.assertEquals; - -/** - * Unit test for the Race class. - */ -public class RaceTest { - /** - * Test that all boats were added to the race - */ - @Test - public void testAddingBoatsToRace() { - Yacht boat1 = new Yacht("Team 1"); - Yacht boat2 = new Yacht("Team 2"); - - Race race = new Race(); - race.addBoat(boat1); - race.addBoat(boat2); - - assertEquals(Array.getLength(race.getBoats()), 2); - } - - @Test - public void testGetShuffledBoats(){ - Yacht boat1 = new Yacht("Team 1"); - Yacht boat2 = new Yacht("Team 2"); - - Race race = new Race(); - race.addBoat(boat1); - race.addBoat(boat2); - - assertEquals(Array.getLength(race.getShuffledBoats()), 2); - } -} diff --git a/src/test/java/seng302/models/parsers/ConfigParserTest.java b/src/test/java/seng302/models/parsers/ConfigParserTest.java deleted file mode 100644 index 8a0c0c8c..00000000 --- a/src/test/java/seng302/models/parsers/ConfigParserTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package seng302.models.parsers; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by Haoming on 23/03/17. - */ -public class ConfigParserTest { - - private ConfigParser cp; - - @Before - public void initializeParser() throws Exception { - cp = new ConfigParser("/config/config.xml"); - } - - @Test - public void getWindDirection() throws Exception { - assertEquals(135, cp.getWindDirection(), 1e-10); - } - - @Test - public void getTimeScale() throws Exception { - assertEquals(10.0, cp.getTimeScale(), 1e-10); - } - - @Test - public void getDoubleByTagName() throws Exception { - assertEquals(6, cp.getDoubleByTagName("race-size", 0), 1e-10); - assertEquals(100, cp.getDoubleByTagName("noTag", 100), 1e-10); - } - - @Test - public void getStringByTagName() throws Exception { - assertEquals("AC35", cp.getStringByTagName("race-name", "11")); - assertEquals("oops", cp.getStringByTagName("noTag", "oops")); - } - -} \ No newline at end of file diff --git a/src/test/java/seng302/models/parsers/CourseParserTest.java b/src/test/java/seng302/models/parsers/CourseParserTest.java deleted file mode 100644 index 48bf9d92..00000000 --- a/src/test/java/seng302/models/parsers/CourseParserTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package seng302.models.parsers; - -import org.junit.Before; -import org.junit.Test; -import seng302.models.mark.*; - -import java.util.ArrayList; - -import static org.junit.Assert.*; - -/** - * To test if course parser works as expected. - * Created by Haoming on 17/03/17. - */ -public class CourseParserTest { - - private CourseParser cp; - - @Before - public void initializeParser() throws Exception { - cp = new CourseParser("/config/course.xml"); - } - - @Test - public void getGates() throws Exception { - ArrayList course = cp.getCourse(); - - - GateMark gateMark1 = (GateMark) course.get(0); - 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(57.671824, gateMark2.getSingleMark2().getLatitude(), 0.00000001); - assertEquals(11.844795, gateMark2.getSingleMark2().getLongitude(), 0.00000001); - } - - @Test - public void getMarks() throws Exception { - ArrayList course = cp.getCourse(); - assertEquals("Mid Mark", course.get(1).getName()); - } - - @Test - public void getOrder() { - ArrayList course = cp.getCourse(); - assertEquals(6, course.size()); - assertEquals("Start", course.get(0).getName()); - assertEquals("Mid Mark", course.get(1).getName()); - assertEquals("Leeward Gate", course.get(2).getName()); - assertEquals("Windward Gate", course.get(3).getName()); - assertEquals("Leeward Gate", course.get(4).getName()); - assertEquals("Finish", course.get(5).getName()); - } - -} \ No newline at end of file diff --git a/src/test/java/seng302/models/parsers/StreamReceiverTest.java b/src/test/java/seng302/models/stream/StreamReceiverTest.java similarity index 98% rename from src/test/java/seng302/models/parsers/StreamReceiverTest.java rename to src/test/java/seng302/models/stream/StreamReceiverTest.java index 698ee3c0..4d0bea44 100644 --- a/src/test/java/seng302/models/parsers/StreamReceiverTest.java +++ b/src/test/java/seng302/models/stream/StreamReceiverTest.java @@ -1,4 +1,4 @@ -package seng302.models.parsers; +package seng302.models.stream; import org.junit.Before; import org.junit.Test; @@ -8,7 +8,7 @@ import java.lang.reflect.Method; import java.net.Socket; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; -import seng302.models.parsers.packets.StreamPacket; +import seng302.models.stream.packets.StreamPacket; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/seng302/models/parsers/TeamsParserTest.java b/src/test/java/seng302/models/stream/TeamsParserTest.java similarity index 100% rename from src/test/java/seng302/models/parsers/TeamsParserTest.java rename to src/test/java/seng302/models/stream/TeamsParserTest.java