From a9709c4f841c041d5c34a6327a0592a51325ba93 Mon Sep 17 00:00:00 2001 From: Haoming Yin Date: Mon, 1 May 2017 17:42:07 +1200 Subject: [PATCH] Merged the mock server to visualisation. #story[715] #story[716] --- src/main/java/seng302/App.java | 11 +- .../seng302/controllers/RaceController.java | 80 ++++++++++++ .../controllers/RaceResultController.java | 37 ++++++ src/main/java/seng302/models/Leg.java | 116 ++++++++++++++++++ .../java/seng302/models/TimelineInfo.java | 31 +++++ .../seng302/models/parsers/ConfigParser.java | 78 ++++++++++++ .../seng302/models/parsers/FileParser.java | 54 ++++++++ src/main/resources/views/CanvasView.fxml | 7 ++ src/main/resources/views/FinishView.fxml | 55 +++++++++ src/test/java/seng302/LegTest.java | 54 ++++++++ src/test/java/seng302/RaceTest.java | 41 +++++++ src/test/java/seng302/TestRaceTimer.java | 25 ++++ .../models/parsers/ConfigParserTest.java | 42 +++++++ .../models/parsers/TeamsParserTest.java | 35 ++++++ 14 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seng302/controllers/RaceController.java create mode 100644 src/main/java/seng302/controllers/RaceResultController.java create mode 100644 src/main/java/seng302/models/Leg.java create mode 100644 src/main/java/seng302/models/TimelineInfo.java create mode 100644 src/main/java/seng302/models/parsers/ConfigParser.java create mode 100644 src/main/java/seng302/models/parsers/FileParser.java create mode 100644 src/main/resources/views/CanvasView.fxml create mode 100644 src/main/resources/views/FinishView.fxml create mode 100644 src/test/java/seng302/LegTest.java create mode 100644 src/test/java/seng302/RaceTest.java create mode 100644 src/test/java/seng302/TestRaceTimer.java create mode 100644 src/test/java/seng302/models/parsers/ConfigParserTest.java create mode 100644 src/test/java/seng302/models/parsers/TeamsParserTest.java diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index a66be5a9..468b5cdd 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -7,6 +7,7 @@ import javafx.scene.Scene; import javafx.stage.Stage; import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamReceiver; +import seng302.server.ServerThread; public class App extends Application { @@ -22,12 +23,20 @@ public class App extends Application public static void main(String[] args) { StreamReceiver sr; + new ServerThread("Racevision Test Server"); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (args.length > 1){ sr = new StreamReceiver("localhost", 8085, "TestThread1"); } else{ //StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1"); - sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread1"); + //sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread1"); + sr = new StreamReceiver("localhost", 8085, "TestThread1"); } sr.start(); diff --git a/src/main/java/seng302/controllers/RaceController.java b/src/main/java/seng302/controllers/RaceController.java new file mode 100644 index 00000000..b5fa2847 --- /dev/null +++ b/src/main/java/seng302/controllers/RaceController.java @@ -0,0 +1,80 @@ +package seng302.controllers; + +import seng302.models.Boat; +import seng302.models.Race; +import seng302.models.parsers.ConfigParser; +import seng302.models.parsers.CourseParser; +import seng302.models.parsers.TeamsParser; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collections; +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); + } 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(); + + // 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(); + + //get race size + int numberOfBoats = teams.size(); + + //get time scale + double timeScale = config.getTimeScale(); + race.setTimeScale(timeScale); + + for (Boat boat : teams) { + boatNames.add(boat.getTeamName()); + 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 new file mode 100644 index 00000000..7378fa68 --- /dev/null +++ b/src/main/java/seng302/controllers/RaceResultController.java @@ -0,0 +1,37 @@ +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].getTeamName())); + boatPosition--; + } + + + + } +} diff --git a/src/main/java/seng302/models/Leg.java b/src/main/java/seng302/models/Leg.java new file mode 100644 index 00000000..8f21a6ea --- /dev/null +++ b/src/main/java/seng302/models/Leg.java @@ -0,0 +1,116 @@ +package seng302.models; + +import seng302.models.mark.SingleMark; + +/** +* Represents the leg of a race. +*/ +public class Leg { + private int heading; + private int distance; + private boolean isFinishingLeg; + private SingleMark startingSingleMark; + + /** + * Create a new leg + * + * @param heading, the magnetic heading of this leg + * @param distance, the total distance of this leg in meters + * @param singleMark, the singleMark this leg starts on + */ + public Leg(int heading, int distance, SingleMark singleMark) { + this.heading = heading; + this.distance = distance; + this.startingSingleMark = singleMark; + this.isFinishingLeg = false; + } + + /** + * Create a new leg + * + * @param heading, the magnetic heading of this leg + * @param distance, the total distance of this leg in meters + * @param markerName, the name of the marker this leg starts on + */ + public Leg(int heading, int distance, String markerName) { + this.heading = heading; + this.distance = distance; + this.startingSingleMark = new SingleMark(markerName); + this.isFinishingLeg = false; + } + + /** + * Get the heading of this leg + * @return int + */ + public int getHeading() { + return this.heading; + } + + /** + * Set the heading for this leg + * @param heading + */ + public void setHeading(int heading) { + this.heading = heading; + } + + /** + * Get the total distance of this leg in meters + * @return int + */ + public int getDistance() { + return this.distance; + } + + /** + * Set the distance of this leg in meters + * @param distance + */ + public void setDistance(int distance) { + this.distance = distance; + } + + /** + * Returns the marker this leg started on + * @return SingleMark + */ + public SingleMark getMarker() { + return this.startingSingleMark; + } + + /** + * Set the singleMark this leg starts on + * @param singleMark + */ + public void setMarker(SingleMark singleMark) { + this.startingSingleMark = singleMark; + } + + /** + * Returns the name of the marker this leg started on + * @return String + */ + public String getMarkerLabel() { + return this.startingSingleMark.getName(); + } + + + + /** + * Specify whether or not the race finishes on this leg + * + * @param isFinishingLeg whether or not the race finishes on this leg + */ + public void setFinishingLeg(boolean isFinishingLeg) { + this.isFinishingLeg = isFinishingLeg; + } + + /** + * Returns whether or not the race finishes after this leg + * @return true if this the race finishes after this leg + */ + public boolean getIsFinishingLeg() { + return this.isFinishingLeg; + } +} \ No newline at end of file diff --git a/src/main/java/seng302/models/TimelineInfo.java b/src/main/java/seng302/models/TimelineInfo.java new file mode 100644 index 00000000..867ce67b --- /dev/null +++ b/src/main/java/seng302/models/TimelineInfo.java @@ -0,0 +1,31 @@ +package seng302.models; + +import javafx.animation.Timeline; +import javafx.beans.property.DoubleProperty; + + +/** + * Created by zyt10 on 17/03/17. + * this class is literally just to associate a timeline with a DoubleProperty x and y + */ +public class TimelineInfo { + private Timeline timeline; + private DoubleProperty x; + private DoubleProperty y; + + public TimelineInfo(Timeline timeline, DoubleProperty x, DoubleProperty y) { + this.timeline = timeline; + this.x = x; + this.y = y; + } + + public Timeline getTimeline() { + return timeline; + } + public DoubleProperty getX() { + return x; + } + public DoubleProperty getY() { + return y; + } +} diff --git a/src/main/java/seng302/models/parsers/ConfigParser.java b/src/main/java/seng302/models/parsers/ConfigParser.java new file mode 100644 index 00000000..1d870c67 --- /dev/null +++ b/src/main/java/seng302/models/parsers/ConfigParser.java @@ -0,0 +1,78 @@ +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/FileParser.java b/src/main/java/seng302/models/parsers/FileParser.java new file mode 100644 index 00000000..be162b9e --- /dev/null +++ b/src/main/java/seng302/models/parsers/FileParser.java @@ -0,0 +1,54 @@ +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/resources/views/CanvasView.fxml b/src/main/resources/views/CanvasView.fxml new file mode 100644 index 00000000..bc16ad7e --- /dev/null +++ b/src/main/resources/views/CanvasView.fxml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/views/FinishView.fxml b/src/main/resources/views/FinishView.fxml new file mode 100644 index 00000000..debdea26 --- /dev/null +++ b/src/main/resources/views/FinishView.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/seng302/LegTest.java b/src/test/java/seng302/LegTest.java new file mode 100644 index 00000000..9bb64b6c --- /dev/null +++ b/src/test/java/seng302/LegTest.java @@ -0,0 +1,54 @@ +package seng302; + +import org.junit.Test; +import seng302.models.Leg; +import seng302.models.mark.SingleMark; + +import static org.junit.Assert.assertEquals; + +/** + * Unit test for the Leg class. + */ +public class LegTest { + + /** + * Test creation of the leg by specifying a string + * for the marker label + */ + @Test + public void testLegCreationUsingMarkerLabel() { + Leg leg = new Leg(010, 100, "SingleMark"); + + assertEquals(leg.getHeading(), 010); + assertEquals(leg.getDistance(), 100); + assertEquals(leg.getMarkerLabel(), "SingleMark"); + assertEquals(leg.getIsFinishingLeg(), false); + } + + /** + * Test creation of the leg by providing a + * SingleMark object + */ + @Test + public void testLegCreation() { + Leg leg = new Leg(010, 100, new SingleMark("SingleMark")); + + assertEquals(leg.getHeading(), 010); + assertEquals(leg.getDistance(), 100); + assertEquals(leg.getMarkerLabel(), "SingleMark"); + assertEquals(leg.getIsFinishingLeg(), false); + } + + /** + * Test changing whether or not a + * leg is the finishing leg + */ + @Test + public void testSetFinishLeg() { + Leg leg = new Leg(010, 100, "SingleMark"); + + leg.setFinishingLeg(true); + assertEquals(leg.getIsFinishingLeg(), true); + } + +} diff --git a/src/test/java/seng302/RaceTest.java b/src/test/java/seng302/RaceTest.java new file mode 100644 index 00000000..ab318331 --- /dev/null +++ b/src/test/java/seng302/RaceTest.java @@ -0,0 +1,41 @@ +package seng302; + +import org.junit.Test; +import seng302.models.Boat; +import seng302.models.Race; + +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() { + Boat boat1 = new Boat("Team 1"); + Boat boat2 = new Boat("Team 2"); + + Race race = new Race(); + race.addBoat(boat1); + race.addBoat(boat2); + + assertEquals(Array.getLength(race.getBoats()), 2); + } + + @Test + public void testGetShuffledBoats(){ + Boat boat1 = new Boat("Team 1"); + Boat boat2 = new Boat("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/TestRaceTimer.java b/src/test/java/seng302/TestRaceTimer.java new file mode 100644 index 00000000..542405b1 --- /dev/null +++ b/src/test/java/seng302/TestRaceTimer.java @@ -0,0 +1,25 @@ +package seng302; + +import org.junit.Test; +import seng302.controllers.RaceViewController; + +import static org.junit.Assert.assertTrue; + + +public class TestRaceTimer { + @Test + public void testPositiveTimeString(){ + RaceViewController controller = new RaceViewController(); + String result = controller.convertTimeToMinutesSeconds(61); + + assertTrue(result.equals("01:01")); + } + + @Test + public void testNegativeTimeString(){ + RaceViewController controller = new RaceViewController(); + String result = controller.convertTimeToMinutesSeconds(-61); + + assertTrue(result.equals("-01:01")); + } +} diff --git a/src/test/java/seng302/models/parsers/ConfigParserTest.java b/src/test/java/seng302/models/parsers/ConfigParserTest.java new file mode 100644 index 00000000..8a0c0c8c --- /dev/null +++ b/src/test/java/seng302/models/parsers/ConfigParserTest.java @@ -0,0 +1,42 @@ +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/TeamsParserTest.java b/src/test/java/seng302/models/parsers/TeamsParserTest.java new file mode 100644 index 00000000..3c31b519 --- /dev/null +++ b/src/test/java/seng302/models/parsers/TeamsParserTest.java @@ -0,0 +1,35 @@ +package seng302.models.parsers; + +import org.junit.Before; +import org.junit.Test; +import seng302.models.Boat; + +import java.util.ArrayList; + +import static org.junit.Assert.*; + +/** + * Created by Haoming on 18/03/17. + */ +public class TeamsParserTest { + + private TeamsParser tp; + @Before + public void readFile() { + tp = new TeamsParser("/config/teams.xml"); + } + + @Test + public void getBoats() throws Exception { + ArrayList boats = tp.getBoats(); + + assertEquals(6, boats.size(), 1e-10); + + assertEquals("Oracle Team USA", boats.get(0).getTeamName()); + //assertEquals(30.9, boats.get(0).getVelocity(), 1e-10); + + assertEquals("Groupama Team France", boats.get(5).getTeamName()); + //assertEquals(45.6, boats.get(5).getVelocity(), 1e-10); + } + +} \ No newline at end of file