From f5b9160304475ccd6c5698d53cec2e7183856051 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Thu, 27 Apr 2017 17:22:46 +1200 Subject: [PATCH 01/25] Started parsing the different types of XML messages to Map objects so that we can extract the relevant data for the visualizer. #story[820] --- .../seng302/models/parsers/StreamPacket.java | 2 +- .../seng302/models/parsers/StreamParser.java | 89 ++++++++++++++++++- .../models/parsers/StreamReceiver.java | 4 +- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/main/java/seng302/models/parsers/StreamPacket.java b/src/main/java/seng302/models/parsers/StreamPacket.java index 80e2936e..85711db2 100644 --- a/src/main/java/seng302/models/parsers/StreamPacket.java +++ b/src/main/java/seng302/models/parsers/StreamPacket.java @@ -19,7 +19,7 @@ public class StreamPacket { this.payload = payload; // System.out.println("type = " + type); //switch the packet type to deal with what ever specific packet you want to deal with - if (this.type == PacketType.AVG_WIND){ + if (this.type == PacketType.XML_MESSAGE){ StreamParser.parsePacket(this); } } diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index 6f526ba3..24c3752a 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -2,6 +2,9 @@ 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 org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -13,6 +16,8 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; /** * Created by kre39 on 23/04/17. @@ -127,6 +132,7 @@ public class StreamParser { while (payloadStream.available() > 0 && (currentChar = payloadStream.read()) != 0) { xmlMessage += (char)currentChar; } + if (xmlMessageSubType == 6) System.out.println(xmlMessage); //Create XML document Object DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -134,7 +140,12 @@ public class StreamParser { try { db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader(xmlMessage))); - // TODO: 25/04/17 ajm412: Check that the object matches expected structure and return Document object. + switch(xmlMessageSubType) { + case 5: parseRegattaXML(doc); + case 6: parseRaceXML(doc); + case 7: parseBoatXML(doc); + } + } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { @@ -145,6 +156,82 @@ public class StreamParser { } + private static void parseRegattaXML(Document doc) { + Element docEle = doc.getDocumentElement(); + String[] regattaElements = {"RegattaID", "RegattaName", "CourseName", "CentralLatitude", "CentralLongitude", + "CentralAltitude", "UtcOffset", "MagneticVariation", "ShorelineName"}; + Map outputMap = parseAtomicElements(docEle, regattaElements); // Regatta contains only atomic elements + + System.out.println(outputMap); + //return outputMap; + } + + private static void parseRaceXML(Document doc) { + Element docEle = doc.getDocumentElement(); + String[] atomicRaceElements = {"RaceID", "RaceType", "CreationTimeDate"}; + Map outputMap = parseAtomicElements(docEle, atomicRaceElements); + + //Race Start Time + Map raceStartMap = new HashMap<>(); + Node raceStartTime = docEle.getElementsByTagName("RaceStartTime").item(0); + raceStartMap.put("Start", getNodeNamedAttribute(raceStartTime, "Start")); + raceStartMap.put("Postpone", getNodeNamedAttribute(raceStartTime, "Postpone")); + outputMap.put("RaceStartTime", raceStartMap); + + //participants + Map participantMap = new HashMap<>(); + NodeList participants = docEle.getElementsByTagName("Participants").item(0).getChildNodes(); + for (int i = 0; i < participants.getLength(); i++) { + Integer sourceID = null; + String entry = null; + Node participant = participants.item(i); + if (participant.getNodeName().equals("Yacht")) { + //sourceID = Integer.parseInt(participant.getAttributes().getNamedItem("SourceID").getTextContent()); + sourceID = Integer.parseInt(getNodeNamedAttribute(participant, "SourceID")); + if (participant.getAttributes().getLength() == 2) { + entry = getNodeNamedAttribute(participant, "Entry"); + } + participantMap.put(sourceID, entry); + } + } + outputMap.put("Participants", participantMap); + + //Course + + System.out.println(outputMap); + } + + private static void parseBoatXML(Document doc) { + // TODO: 27/04/17 ajm412 + } + + private static String getNodeNamedAttribute(Node n, String attr) { + return n.getAttributes().getNamedItem(attr).getTextContent(); + } + + private static Map parseAtomicElements(Element docEle, String[] elements) { + Map outputMap = new HashMap<>(); + for (String element : elements) { + Object elementValue = null; + if (docEle.getElementsByTagName(element).getLength() == 1) { + String elementText = docEle.getElementsByTagName(element).item(0).getTextContent(); + // TODO: 27/04/17 ajm412: this seems messy, trying to parse values as ints/doubles to the map rather than as Strings. Possibly use RegEx. + try { + elementValue = Integer.parseInt(elementText); + } catch (NumberFormatException nfe1) { + try { + elementValue = Double.parseDouble(elementText); + } catch (NumberFormatException nfe2) { + elementValue = elementText; + } + } + } + outputMap.put(element, elementValue); + } + + return outputMap; + } + private static void extractRaceStartStatus(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/parsers/StreamReceiver.java index d5127ce4..128683a3 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/parsers/StreamReceiver.java @@ -4,8 +4,6 @@ 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; @@ -129,7 +127,7 @@ public class StreamReceiver { } }); StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, pq); -// StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, pq); + //StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, pq); sr.connect(); } } From e1de5e0989d90d235f511140c8b50ff8a74d1c80 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Thu, 27 Apr 2017 18:31:13 +1200 Subject: [PATCH 02/25] Parsed more course data from XML messages #story[820] --- .../seng302/models/parsers/StreamParser.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index 24c3752a..0c982ec6 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -14,10 +14,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * Created by kre39 on 23/04/17. @@ -130,7 +127,7 @@ public class StreamParser { //Converts XML message to string to be parsed int currentChar; while (payloadStream.available() > 0 && (currentChar = payloadStream.read()) != 0) { - xmlMessage += (char)currentChar; + xmlMessage += (char)currentChar; } if (xmlMessageSubType == 6) System.out.println(xmlMessage); @@ -167,6 +164,7 @@ public class StreamParser { } private static void parseRaceXML(Document doc) { + // TODO: 27/04/17 ajm412 This is an extremely long method. Needs to be broken down. Element docEle = doc.getDocumentElement(); String[] atomicRaceElements = {"RaceID", "RaceType", "CreationTimeDate"}; Map outputMap = parseAtomicElements(docEle, atomicRaceElements); @@ -196,7 +194,37 @@ public class StreamParser { } outputMap.put("Participants", participantMap); - //Course + //Course - Order matters. + Map courseMap = new TreeMap<>(); + NodeList course = docEle.getElementsByTagName("Course").item(0).getChildNodes(); + for (int i = 0; i < course.getLength(); i++) { + Integer compoundMarkID = null; + String name = null; + Map compoundMarkMap = new TreeMap<>(); + Node compoundMark = course.item(i); + if (compoundMark.getNodeName().equals("CompoundMark")) { + compoundMarkID = Integer.parseInt(getNodeNamedAttribute(compoundMark, "CompoundMarkID")); + name = getNodeNamedAttribute(compoundMark, "Name"); + //get marks for compound mark + NodeList marks = compoundMark.getChildNodes(); + for (int j = 0; j < marks.getLength(); j++) { + Map markMap = new TreeMap<>(); + Node mark = marks.item(j); + if (mark.getNodeName().equals("Mark")) { + markMap.put("Name", getNodeNamedAttribute(mark, "Name")); + markMap.put("TargetLat", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLat"))); + markMap.put("TargetLng", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLng"))); + markMap.put("SourceID", Integer.parseInt(getNodeNamedAttribute(mark, "SourceID"))); + + compoundMarkMap.put(Integer.parseInt(getNodeNamedAttribute(mark, "SeqID")), markMap); + + } + } + + } + courseMap.put(compoundMarkID, compoundMarkMap); + } + //outputMap.put("Course", courseMap); System.out.println(outputMap); } From fe480d5cb6c41ff03f50931646806dde29f08e08 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Fri, 28 Apr 2017 15:41:12 +1200 Subject: [PATCH 03/25] Finished parsing the Race XML data. Began making some optimizations to hopefully make parsing the Boat Data a quicker and simpler task. #story[820] --- .../seng302/models/parsers/StreamParser.java | 84 +++++++++++++++---- .../models/parsers/StreamReceiver.java | 4 +- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index 0c982ec6..dff14a7f 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -159,7 +159,7 @@ public class StreamParser { "CentralAltitude", "UtcOffset", "MagneticVariation", "ShorelineName"}; Map outputMap = parseAtomicElements(docEle, regattaElements); // Regatta contains only atomic elements - System.out.println(outputMap); + //System.out.println(outputMap); //return outputMap; } @@ -176,30 +176,56 @@ public class StreamParser { raceStartMap.put("Postpone", getNodeNamedAttribute(raceStartTime, "Postpone")); outputMap.put("RaceStartTime", raceStartMap); - //participants - Map participantMap = new HashMap<>(); + //Race Participants NodeList participants = docEle.getElementsByTagName("Participants").item(0).getChildNodes(); + outputMap.put("Participants", parseRaceParticipants(participants)); + + //Course (CompoundMarks) + NodeList course = docEle.getElementsByTagName("Course").item(0).getChildNodes(); + outputMap.put("Course", parseCourse(course)); + + //CompoundMark Sequence + NodeList markSequence = docEle.getElementsByTagName("CompoundMarkSequence").item(0).getChildNodes(); + outputMap.put("CourseMarkSequence", parseMarkSequence(markSequence)); + + //Course Limits + NodeList courseLimits = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes(); + outputMap.put("CourseLimit", parseCourseLimits(courseLimits)); + + System.out.println(outputMap.get("Course")); +// for (Map.Entry entry : outputMap.entrySet()) { +// System.out.println(entry); +// } + } + + private static ArrayList parseRaceParticipants(NodeList participants) { + ArrayList participantList = new ArrayList<>(); for (int i = 0; i < participants.getLength(); i++) { + Map participantMap = new HashMap<>(); Integer sourceID = null; String entry = null; Node participant = participants.item(i); if (participant.getNodeName().equals("Yacht")) { - //sourceID = Integer.parseInt(participant.getAttributes().getNamedItem("SourceID").getTextContent()); sourceID = Integer.parseInt(getNodeNamedAttribute(participant, "SourceID")); if (participant.getAttributes().getLength() == 2) { entry = getNodeNamedAttribute(participant, "Entry"); } - participantMap.put(sourceID, entry); + participantMap.put("sourceID", sourceID); + participantMap.put("Entry", entry); + participantList.add(participantMap); } } - outputMap.put("Participants", participantMap); + return participantList; + } - //Course - Order matters. + private static Map parseCourse(NodeList course) { Map courseMap = new TreeMap<>(); - NodeList course = docEle.getElementsByTagName("Course").item(0).getChildNodes(); + ArrayList courseList = new ArrayList<>(); for (int i = 0; i < course.getLength(); i++) { + Integer compoundMarkID = null; String name = null; + //map for an individual CompoundMark Map compoundMarkMap = new TreeMap<>(); Node compoundMark = course.item(i); if (compoundMark.getNodeName().equals("CompoundMark")) { @@ -208,6 +234,7 @@ public class StreamParser { //get marks for compound mark NodeList marks = compoundMark.getChildNodes(); for (int j = 0; j < marks.getLength(); j++) { + //map for individual mark details within a compound mark Map markMap = new TreeMap<>(); Node mark = marks.item(j); if (mark.getNodeName().equals("Mark")) { @@ -215,18 +242,47 @@ public class StreamParser { markMap.put("TargetLat", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLat"))); markMap.put("TargetLng", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLng"))); markMap.put("SourceID", Integer.parseInt(getNodeNamedAttribute(mark, "SourceID"))); - compoundMarkMap.put(Integer.parseInt(getNodeNamedAttribute(mark, "SeqID")), markMap); - } } - + courseMap.put(compoundMarkID, compoundMarkMap); } - courseMap.put(compoundMarkID, compoundMarkMap); } - //outputMap.put("Course", courseMap); - System.out.println(outputMap); + return courseMap; + } + + private static Map parseMarkSequence(NodeList markSequence) { + Map markSequenceMap = new TreeMap<>(); + + for (int i = 0; i < markSequence.getLength(); i++) { + Map cornerMap = new TreeMap<>(); + Node corner = markSequence.item(i); + if (corner.getNodeName().equals("Corner")) { + cornerMap.put("CompoundMarkID", Integer.parseInt(getNodeNamedAttribute(corner, "CompoundMarkID"))); + cornerMap.put("Rounding", getNodeNamedAttribute(corner, "Rounding")); + cornerMap.put("ZoneSize", getNodeNamedAttribute(corner, "ZoneSize")); + markSequenceMap.put(Integer.parseInt(getNodeNamedAttribute(corner, "SeqID")), cornerMap); + } + } + + return markSequenceMap; + } + + private static Map parseCourseLimits(NodeList courseLimits) { + Map courseLimitMap = new TreeMap<>(); + + for (int i = 0; i < courseLimits.getLength(); i++) { + Map limitMap = new HashMap<>(); + Node limit = courseLimits.item(i); + if (limit.getNodeName().equals("Limit")) { + limitMap.put("Lat", Double.parseDouble(getNodeNamedAttribute(limit,"Lat"))); + limitMap.put("Lon", Double.parseDouble(getNodeNamedAttribute(limit,"Lon"))); + courseLimitMap.put(Integer.parseInt(getNodeNamedAttribute(limit, "SeqID")), limitMap); + } + } + + return courseLimitMap; } private static void parseBoatXML(Document doc) { diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/parsers/StreamReceiver.java index 128683a3..31912815 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/parsers/StreamReceiver.java @@ -126,8 +126,8 @@ public class StreamReceiver { return (int) (s1.getTimeStamp() - s2.getTimeStamp()); } }); - StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, pq); - //StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, pq); + //StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, pq); + StreamReceiver sr = new StreamReceiver("livedata.americascup.com", 4941, pq); sr.connect(); } } From 0f4ad48de07daa911cc382dcaabdfd6fdd570a9c Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Fri, 28 Apr 2017 16:41:35 +1200 Subject: [PATCH 04/25] Fixed and enables the old wakes. Enabled the fps counter by implementing the team-27s fps counter from their code, fixed trails from starting at the start of the startline no matter at what point in the race the stream is connected to (this is means the map starts a lot cleaner). Added live tracked speeds which are taken from the boat location packet. Linked the speeds coming in to their specified boats and allowed the onscreen speed tracker to keep up with the speeds. Linked the current speeds to the wakes so the wakes are redrawn for each change in speed and size to match the speed. Also added the toggle functionality back to the fps counter so they can be toggled on an off. #story[818] --- .../seng302/controllers/CanvasController.java | 33 +++++++++- .../controllers/RaceViewController.java | 1 + src/main/java/seng302/models/Boat.java | 1 + src/main/java/seng302/models/BoatGroup.java | 65 +++++++++++++------ src/main/java/seng302/models/Wake.java | 2 +- .../seng302/models/parsers/StreamParser.java | 16 ++++- src/main/resources/config/teams.xml | 12 ++-- src/main/resources/views/RaceView.fxml | 19 +++--- 8 files changed, 110 insertions(+), 39 deletions(-) diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 37ef3193..dbce6849 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -22,6 +22,7 @@ import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamReceiver; import java.sql.Time; +import java.text.DecimalFormat; import java.util.*; /** @@ -59,6 +60,13 @@ public class CanvasController { private double metersToPixels; private List raceObjects = new ArrayList<>(); + //FRAME RATE + private static final double UPDATE_TIME = 0.016666; // 1 / 60 ie 60fps + 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; private enum ScaleDirection { @@ -138,6 +146,8 @@ public class CanvasController { timer = new AnimationTimer() { + + private int countdown = 60; private int[] currentRaceMarker = {1, 1, 1, 1, 1, 1}; List marks = raceViewController.getRace().getCourse(); @@ -150,6 +160,20 @@ public class CanvasController { int boatIndex = 0; Mark nextMark; + + long oldFrameTime = frameTimes[frameTimeIndex] ; + frameTimes[frameTimeIndex] = now ; + frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length ; + if (frameTimeIndex == 0) { + arrayFilled = true ; + } + if (arrayFilled) { + long elapsedNanos = now - oldFrameTime ; + long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ; + Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; + drawFps(frameRate.intValue()); + } + //if (countdown == 0) { //System.out.println("called the at"); for (RaceObject raceObject : raceObjects) { @@ -163,8 +187,6 @@ public class CanvasController { //descending = nextMark.getY() > boatGroup.getLayoutY(); //leftToRight = nextMark.getX() < boatGroup.getLayoutX(); - - raceObject.updatePosition(1000 / 60); for (int id : raceObject.getRaceIds()) { //System.out.println("id = " + id); @@ -285,10 +307,15 @@ public class CanvasController { private void drawFps(int fps){ if (raceViewController.isDisplayFps()){ + gc.clearRect(5,5,50,20); gc.setFill(Color.BLACK); gc.setFont(new Font(14)); gc.setLineWidth(3); gc.fillText(fps + " FPS", 5, 20); + } else { + gc.clearRect(5,5,50,20); + gc.setFill(Color.SKYBLUE); + gc.fillRect(4,4,51,21); } } @@ -307,7 +334,7 @@ public class CanvasController { for (Boat boat : boats) { BoatGroup boatGroup = new BoatGroup(boat, Colors.getColor()); boatGroup.moveTo(startingX, startingY, 0d); - boatGroup.setDestination(firstMarkX, firstMarkY); +// boatGroup.setDestination(firstMarkX, firstMarkY); boatGroup.forceRotation(); group.getChildren().add(boatGroup); raceObjects.add(boatGroup); diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index e781c8d3..214cf02f 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -5,6 +5,7 @@ import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.CheckBox; diff --git a/src/main/java/seng302/models/Boat.java b/src/main/java/seng302/models/Boat.java index d275091f..04ef14ab 100644 --- a/src/main/java/seng302/models/Boat.java +++ b/src/main/java/seng302/models/Boat.java @@ -140,4 +140,5 @@ public class Boat { public int getId() { return id; } + } \ No newline at end of file diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 44f5aa1f..46887e5a 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -8,6 +8,7 @@ import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; +import seng302.models.parsers.StreamParser; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public class BoatGroup extends RaceObject{ private static double expectedUpdateInterval = 200; private static int WAKE_FRAME_INTERVAL = 30; private double framesForNewLine = 0; + private boolean destinationSet; private Point2D lastPoint; private Boat boat; @@ -35,7 +37,7 @@ public class BoatGroup extends RaceObject{ private List wakes = new ArrayList<>(); private List lines = new ArrayList<>(); private Polygon boatPoly; -// private Polygon wakePoly; + private Polygon wakePoly; private Text teamNameObject; private Text velocityObject; @@ -56,12 +58,12 @@ public class BoatGroup extends RaceObject{ // boatPoly.setLayoutY(0); // boatPoly.relocate(boatPoly.getLayoutX(), boatPoly.getLayoutY()); // -// wakePoly = new Polygon( -// 5.0,0.0, -// 10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO, -// 0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO -// ); -// wakePoly.setFill(Color.DARKBLUE); + wakePoly = new Polygon( + 5.0,0.0, + 10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO, + 0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO + ); + wakePoly.setFill(Color.DARKBLUE); teamNameObject = new Text(boat.getShortName()); velocityObject = new Text(String.valueOf(boat.getVelocity())); @@ -73,9 +75,8 @@ public class BoatGroup extends RaceObject{ velocityObject.setX(VELOCITY_X_OFFSET); velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); - -// super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject); - super.getChildren().addAll(teamNameObject, velocityObject, boatPoly); + destinationSet = false; + super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject); } private void initChildren (Color color) { @@ -97,8 +98,8 @@ public class BoatGroup extends RaceObject{ teamNameObject.setLayoutY(teamNameObject.getLayoutY() + dy); velocityObject.setLayoutX(velocityObject.getLayoutX() + dx); velocityObject.setLayoutY(velocityObject.getLayoutY() + dy); -// wakePoly.setLayoutX(wakePoly.getLayoutX() + dx); -// wakePoly.setLayoutY(wakePoly.getLayoutY() + dy); + wakePoly.setLayoutX(wakePoly.getLayoutX() + dx); + wakePoly.setLayoutY(wakePoly.getLayoutY() + dy); rotateTo(currentRotation); } @@ -119,8 +120,8 @@ public class BoatGroup extends RaceObject{ teamNameObject.setLayoutY(y); velocityObject.setLayoutX(x); velocityObject.setLayoutY(y); -// wakePoly.setLayoutX(x); -// wakePoly.setLayoutY(y); + wakePoly.setLayoutX(x); + wakePoly.setLayoutY(y); } public void updatePosition (double timeInterval) { @@ -158,9 +159,8 @@ public class BoatGroup extends RaceObject{ boatPoly.getLayoutX(), boatPoly.getLayoutY(), pixelVelocityX, - pixelVelocityY, rotation); -// wake.getTransforms().clear(); -// wake.getTransforms().add(new Rotate(rotation, 0, 0)); + pixelVelocityY, + rotation); super.getChildren().add(wake); wakes.add(wake); } @@ -174,13 +174,18 @@ public class BoatGroup extends RaceObject{ lines.add(l); super.getChildren().add(l); } - lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + if (destinationSet){ + lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + } } framesForNewLine -= 1; } public void setDestination (double newXValue, double newYValue, double rotation, int... raceIds) { //System.out.println("MADE IT"); + destinationSet = true; + boat.setVelocity(StreamParser.boatSpeeds.get((long)boat.getId())); + resizeWake(); if (hasRaceId(raceIds)) { this.pixelVelocityX = (newXValue - boatPoly.getLayoutX()) / expectedUpdateInterval; this.pixelVelocityY = (newYValue - boatPoly.getLayoutY()) / expectedUpdateInterval; @@ -191,6 +196,8 @@ public class BoatGroup extends RaceObject{ } public void setDestination (double newXValue, double newYValue, int... raceIDs) { + destinationSet = true; + if (hasRaceId(raceIDs)) { double rotation = Math.abs( Math.toDegrees( @@ -212,16 +219,34 @@ public class BoatGroup extends RaceObject{ } } + void resizeWake(){ + velocityObject.setText(String.valueOf(boat.getVelocity())); + super.getChildren().remove(wakePoly); + wakePoly = new Polygon( + 5.0,0.0, + 10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO, + 0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO + ); + wakePoly.setLayoutX(boatPoly.getLayoutX()); + wakePoly.setLayoutY(boatPoly.getLayoutY()); + wakePoly.setFill(Color.DARKBLUE); + super.getChildren().add(wakePoly); + + } + public void rotateTo (double rotation) { if(rotation != 0) { rotationalGoal = rotation; boatPoly.getTransforms().clear(); boatPoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH / 2, 0)); + wakePoly.getTransforms().clear(); + wakePoly.getTransforms().add(new Translate(0, BOAT_HEIGHT)); + wakePoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH/2, -BOAT_HEIGHT)); } -// wakePoly.getTransforms().clear(); -// wakePoly.getTransforms().add(new Rotate(rotation, 0, 0)); } + + public void forceRotation () { rotateTo (rotationalGoal); } diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 92b2d30d..09b037ba 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -29,7 +29,7 @@ class Wake extends Arc { super.setType(ArcType.OPEN); super.setFill(new Color(0, 0, 0 ,0)); super.setStrokeWidth(2.0); - super.getTransforms().add(new Rotate(rotation, 5, -15)); + super.getTransforms().add(new Rotate(rotation - 270, startingX + 20, startingY + 20)); // this.velocityX = -velocityX; // this.velocityY = -velocityY; this.velocityX = 0; diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index f49e4d31..c917cd3c 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -14,6 +14,8 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; @@ -25,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; public class StreamParser extends Thread{ public static ConcurrentHashMap boatPositions = new ConcurrentHashMap<>(); + public static ConcurrentHashMap boatSpeeds = new ConcurrentHashMap<>(); private static ArrayList boat_IDS = new ArrayList<>(); private String threadName; private Thread t; @@ -253,6 +256,8 @@ public class StreamParser extends Thread{ byte[] lonBytes = Arrays.copyOfRange(payload,20,24); byte[] boatIdBytes = Arrays.copyOfRange(payload,7,11); byte[] headingBytes = Arrays.copyOfRange(payload,28,30); + byte[] speedBytes = Arrays.copyOfRange(payload,38,40); + long timeStamp = extractTimeStamp(Arrays.copyOfRange(payload,1,7), 6); // int boatSeq = ByteBuffer.wrap(seqBytes).getInt(); long seq = bytesToLong(seqBytes); @@ -260,7 +265,13 @@ public class StreamParser extends Thread{ long lat = bytesToLong(latBytes); long lon = bytesToLong(lonBytes); long heading = bytesToLong(headingBytes); - +// long speed = extractTimeStamp(speedBytes, 2); + ByteBuffer bb = ByteBuffer.allocate(2); + bb.order(ByteOrder.LITTLE_ENDIAN); + bb.put(speedBytes[0]); + bb.put(speedBytes[1]); + double speed = bb.getShort(0)/1000.0; + short s = (short) ((speedBytes[1] & 0xFF) << 8 | (speedBytes[0] & 0xFF)); if ((int)deviceType == 1 || (int)deviceType == 4){ // System.out.println("boatId = " + boatId); // System.out.println("deviceType = " + (long)deviceType); @@ -268,13 +279,16 @@ public class StreamParser extends Thread{ //needs to be validated Point3D point = new Point3D(((180d * (double)lat)/Math.pow(2,31)),((180d *(double)lon)/Math.pow(2,31)),(double)heading); boatPositions.putIfAbsent(boatId, point); + boatSpeeds.putIfAbsent(boatId, speed); boatPositions.replace(boatId, point); + boatSpeeds.replace(boatId, speed); // System.out.println("lon = " + ((180d * (double)lon)/Math.pow(2,31))); // System.out.println("lat = " + ((180d *(double)lat)/Math.pow(2,31))); } } + private static void extractMarkRounding(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; diff --git a/src/main/resources/config/teams.xml b/src/main/resources/config/teams.xml index f8d1e58d..0ac01cac 100644 --- a/src/main/resources/config/teams.xml +++ b/src/main/resources/config/teams.xml @@ -4,37 +4,37 @@ Oracle Team USA USA - 12.9 + 0.0 102 Artemis Racing ART - 13.1 + 0.0 101 Emirates Team New Zealand NZL - 15.6 + 0.0 103 Land Rover BAR BAR - 13.3 + 0.0 104 SoftBank Team Japan JAP - 14.7 + 0.0 105 Groupama Team France FRC - 11.4 + 0.0 106 \ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index f7fcbfeb..2cae1326 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -1,15 +1,18 @@ - - - - - - - + + + + + + + + + + - + From d204bee55df67a8b9e6f77a795ea101f64c3c304 Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Fri, 28 Apr 2017 17:01:28 +1200 Subject: [PATCH 05/25] Started documentation on the stream parser. #story[820] --- .../seng302/models/parsers/StreamParser.java | 88 +++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index c917cd3c..766296a7 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -1,12 +1,10 @@ package seng302.models.parsers; -import javafx.geometry.Point2D; import javafx.geometry.Point3D; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import sun.awt.UNIXToolkit; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -22,14 +20,16 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** + * The purpose of this class is to take in the stream of divided packets so they can be read + * and parsed in by turning the byte arrays into useful data. There are two public static hashmaps + * that are threadsafe so the visualiser can always access the latest speed and position avaible * Created by kre39 on 23/04/17. */ public class StreamParser extends Thread{ public static ConcurrentHashMap boatPositions = new ConcurrentHashMap<>(); public static ConcurrentHashMap boatSpeeds = new ConcurrentHashMap<>(); - private static ArrayList boat_IDS = new ArrayList<>(); - private String threadName; + private String threadName; private Thread t; private static boolean raceStarted = false; @@ -37,7 +37,11 @@ public class StreamParser extends Thread{ this.threadName = threadName; } - public void run(){ + /** + * Used to within threading so when the stream parser thread runs, it will keep looking for a packet to + * process until it is unable to find anymore packets + */ + public void run(){ try { System.out.println("START OF STREAM"); while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) { @@ -57,6 +61,9 @@ public class StreamParser extends Thread{ } } + /** + * Used to start the stream parser thread when multithreading + */ public void start () { System.out.println("Starting " + threadName ); if (t == null) { @@ -65,7 +72,7 @@ public class StreamParser extends Thread{ } } - static void parsePacket(StreamPacket packet) { + private static void parsePacket(StreamPacket packet) { switch (packet.getType()){ case HEARTBEAT: extractHeartBeat(packet); @@ -109,12 +116,20 @@ public class StreamParser extends Thread{ } } + /** + * Extracts the seq num used in the heartbeat packet + * @param packet Packet parsed in to use the payload + */ private static void extractHeartBeat(StreamPacket packet) { long heartbeat = bytesToLong(packet.getPayload()); -// System.out.println("Heartbeat: " + heartbeat); - } + /** + * Extracts the useful race status data from race status type packets. This method will also print to the + * console the current state of the race (if it has started/finished or is about to start), along side + * this it'll also display the amount of time since the race has started or time till it starts + * @param packet Packet parsed in to use the payload + */ private static void extractRaceStatus(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -158,6 +173,10 @@ public class StreamParser extends Thread{ } } + /** + * Used to extract the messages passed through with the display message packet + * @param packet Packet parsed in to use the payload + */ private static void extractDisplayMessage(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -172,7 +191,11 @@ public class StreamParser extends Thread{ } } - static void extractXmlMessage(StreamPacket packet){ + /** + * Used to read in the xml data. Will call the specific methods to create the course and boats + * @param packet Packet parsed in to use the payload + */ + private static void extractXmlMessage(StreamPacket packet){ byte[] payload = packet.getPayload(); String xmlMessage = ""; @@ -200,16 +223,17 @@ public class StreamParser extends Thread{ db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader(xmlMessage))); // TODO: 25/04/17 ajm412: Check that the object matches expected structure and return Document object. - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.printStackTrace(); - } catch (IOException e) { + } catch (ParserConfigurationException | IOException | SAXException e) { e.printStackTrace(); } } + /** + * Extracts the race start status from the packet, currently is unused within the app but + * is here for potential future use + * @param packet Packet parsed in to use the payload + */ private static void extractRaceStartStatus(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -219,6 +243,11 @@ public class StreamParser extends Thread{ int notificationType = payload[19]; } + /** + * When a yacht event occurs this will parse the byte array to retrieve the necessary info, + * currently unused + * @param packet Packet parsed in to use the payload + */ private static void extractYachtEventCode(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -229,6 +258,11 @@ public class StreamParser extends Thread{ int eventId = payload[21]; } + /** + * When a yacht action occurs this will parse the parse the byte array to retrieve the necessary info, + * currently unused + * @param packet Packet parsed in to use the payload + */ private static void extractYachtActionCode(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -239,6 +273,10 @@ public class StreamParser extends Thread{ // System.out.println("eventId = " + eventId); } + /** + * Strips the message from the chatter text type packets, currently the message is unused + * @param packet Packet parsed in to use the payload + */ private static void extractChatterText(StreamPacket packet){ byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -247,8 +285,12 @@ public class StreamParser extends Thread{ String message = new String(Arrays.copyOfRange(payload,3,3 + length)); } - - static void extractBoatLocation(StreamPacket packet){ + /** + * Used to breakdown the boatlocation packets so the boat coordinates, id and groundspeed are all used + * All the other extra data is still being read and translated however is unused. + * @param packet Packet parsed in to use the payload + */ + private static void extractBoatLocation(StreamPacket packet){ byte[] payload = packet.getPayload(); byte deviceType = payload[15]; byte[] seqBytes = Arrays.copyOfRange(payload,11,15); @@ -256,7 +298,7 @@ public class StreamParser extends Thread{ byte[] lonBytes = Arrays.copyOfRange(payload,20,24); byte[] boatIdBytes = Arrays.copyOfRange(payload,7,11); byte[] headingBytes = Arrays.copyOfRange(payload,28,30); - byte[] speedBytes = Arrays.copyOfRange(payload,38,40); + byte[] groundSpeedBytes = Arrays.copyOfRange(payload,38,40); long timeStamp = extractTimeStamp(Arrays.copyOfRange(payload,1,7), 6); // int boatSeq = ByteBuffer.wrap(seqBytes).getInt(); @@ -266,12 +308,8 @@ public class StreamParser extends Thread{ long lon = bytesToLong(lonBytes); long heading = bytesToLong(headingBytes); // long speed = extractTimeStamp(speedBytes, 2); - ByteBuffer bb = ByteBuffer.allocate(2); - bb.order(ByteOrder.LITTLE_ENDIAN); - bb.put(speedBytes[0]); - bb.put(speedBytes[1]); - double speed = bb.getShort(0)/1000.0; - short s = (short) ((speedBytes[1] & 0xFF) << 8 | (speedBytes[0] & 0xFF)); + double groundSpeed = bytesToLong(groundSpeedBytes)/1000.0; + short s = (short) ((groundSpeedBytes[1] & 0xFF) << 8 | (groundSpeedBytes[0] & 0xFF)); if ((int)deviceType == 1 || (int)deviceType == 4){ // System.out.println("boatId = " + boatId); // System.out.println("deviceType = " + (long)deviceType); @@ -279,9 +317,9 @@ public class StreamParser extends Thread{ //needs to be validated Point3D point = new Point3D(((180d * (double)lat)/Math.pow(2,31)),((180d *(double)lon)/Math.pow(2,31)),(double)heading); boatPositions.putIfAbsent(boatId, point); - boatSpeeds.putIfAbsent(boatId, speed); + boatSpeeds.putIfAbsent(boatId, groundSpeed); boatPositions.replace(boatId, point); - boatSpeeds.replace(boatId, speed); + boatSpeeds.replace(boatId, groundSpeed); // System.out.println("lon = " + ((180d * (double)lon)/Math.pow(2,31))); // System.out.println("lat = " + ((180d *(double)lat)/Math.pow(2,31))); } From 07bbd7e06d29948fd92f345438edba21d0efe6dd Mon Sep 17 00:00:00 2001 From: Peter Galloway Date: Fri, 28 Apr 2017 18:29:35 +1200 Subject: [PATCH 06/25] Added reasonable testing for StreamReciever, further testing would probably need StreamReciever to be rewritten #story[817] --- .../models/parsers/StreamReceiver.java | 12 +- .../models/parsers/StreamReceiverTest.java | 105 ++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/test/java/seng302/models/parsers/StreamReceiverTest.java diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/parsers/StreamReceiver.java index 31912815..663f3a8c 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/parsers/StreamReceiver.java @@ -19,6 +19,7 @@ public class StreamReceiver { public StreamReceiver(String hostAddress, int hostPort, PriorityBlockingQueue packetBuffer) { try { host = new Socket(hostAddress, hostPort); + } catch (IOException e) { e.printStackTrace(); System.exit(1); @@ -26,6 +27,12 @@ public class StreamReceiver { this.packetBuffer = packetBuffer; } + public StreamReceiver(Socket host, PriorityBlockingQueue packetBuffer){ + this.host=host; + this.packetBuffer = packetBuffer; + } + + public void connect(){ try { stream = host.getInputStream(); @@ -66,7 +73,6 @@ public class StreamReceiver { } catch (Exception e) { moreBytes = false; } - } } @@ -100,8 +106,8 @@ public class StreamReceiver { } /** - * takes an array of up to 7 bytes and returns a positive - * long constructed from the input bytes + * takes an array of up to 7 bytes in little endian format and + * returns a positive long constructed from the input bytes * * @return a positive long if there is less than 7 bytes -1 otherwise */ diff --git a/src/test/java/seng302/models/parsers/StreamReceiverTest.java b/src/test/java/seng302/models/parsers/StreamReceiverTest.java new file mode 100644 index 00000000..c7951e3b --- /dev/null +++ b/src/test/java/seng302/models/parsers/StreamReceiverTest.java @@ -0,0 +1,105 @@ +package seng302.models.parsers; + +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.lang.reflect.Method; +import java.net.Socket; +import java.util.Comparator; +import java.util.concurrent.PriorityBlockingQueue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Created by ptg19 on 26/04/17. + */ +public class StreamReceiverTest { + + private PriorityBlockingQueue pq; + private byte[] brokenPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type + 0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp + 0b00000000, 0b00010000, 0b01000000, 0b00000000, //source id + 0b00100010, 0b00101000, // message length + 0b00010010, 0b00010010, 0b00010010}; //random start of payload + + private byte[] workingPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type + 0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp + 0b00000000, 0b00010000, 0b01000000, 0b00000000, //source id + 0b00000010, 0b00000000, // message length + 0b00010010, 0b00010010, // payload + 0b00100110, (byte)0b10000111, 0b00110101, 0b01111000}; //crc + + private byte[] crcMismatchPacket = {0x47, (byte) 0x83, 37, // sync1 sync2 and message type + 0b00000000, 0b01000000, 0b00100010, 0b00100100, 0b00011000, 0b00000000, //timestamp + 0b00000000, 0b00000000, 0b01000000, 0b00000000, //source id + 0b00000010, 0b00000000, // message length + 0b00010010, 0b00010010, // payload + 0b00100110, (byte)0b10000111, 0b00110101, 0b01111000}; //crc + + + @Before + public void setup(){ + pq = new PriorityBlockingQueue<>(256, new Comparator() { + @Override + public int compare(StreamPacket s1, StreamPacket s2) { + return (int) (s1.getTimeStamp() - s2.getTimeStamp()); + } + }); + } + + @Test + public void connectExitsOnUnexpectedStreamEnd() throws Exception { + Socket host=mock(Socket.class); + InputStream stream = new ByteArrayInputStream(brokenPacket); + when(host.getInputStream()).thenReturn(stream); + StreamReceiver streamReceiver = new StreamReceiver(host, pq); + + streamReceiver.connect(); + assert pq.size() == 0; + } + + @Test + public void connectReadsAPacket() throws Exception { + Socket host=mock(Socket.class); + InputStream stream = new ByteArrayInputStream(workingPacket); + when(host.getInputStream()).thenReturn(stream); + StreamReceiver streamReceiver = new StreamReceiver(host, pq); + + streamReceiver.connect(); + assert pq.size() == 1; + } + + @Test + public void connectDropsAMismatchedCrc() throws Exception { + Socket host=mock(Socket.class); + InputStream stream = new ByteArrayInputStream(crcMismatchPacket); + when(host.getInputStream()).thenReturn(stream); + StreamReceiver streamReceiver = new StreamReceiver(host, pq); + + streamReceiver.connect(); + assert pq.size() == 0; + } + + @Test + public void bytestoLongTest() { + Socket host=mock(Socket.class); + StreamReceiver streamReceiver = new StreamReceiver(host, pq); + try { + Class[] args = new Class[1]; + args[0] = byte[].class; + Method bytesToLong = streamReceiver.getClass().getDeclaredMethod("bytesToLong", args); + bytesToLong.setAccessible(true); + byte[] sevenBtyeNumber = {0b01100100, 0b00110100, 0b00010100, 0b00000000, 0b00000000, 0b00000000, (byte)0b10000000}; + assert bytesToLong.invoke(streamReceiver, sevenBtyeNumber).equals(36028797020288100L); + byte[] eightByteNumber = {0b01100100, 0b00110100, 0b00010100, 0b00000000, 0b00000000, 0b00000000, (byte)0b10000000, 0b00100101}; + assert bytesToLong.invoke(streamReceiver, eightByteNumber).equals(-1L); + byte[] emptyArray = {}; + assert bytesToLong.invoke(streamReceiver, emptyArray).equals(0L); + } catch (Exception e){ + System.out.println(""); + } + } + +} \ No newline at end of file From ffdfc24e65bc91e6ad58e25f472de4a604df4ed2 Mon Sep 17 00:00:00 2001 From: Zhi You Tan Date: Fri, 28 Apr 2017 20:09:17 +1200 Subject: [PATCH 07/25] Created a start screen with a timer which shows the race progress #story[572] --- .../java/seng302/controllers/Controller.java | 49 +++++++++++++++++++ .../seng302/models/parsers/StreamParser.java | 34 ++++++++++++- src/main/resources/views/MainView.fxml | 39 ++++++++++++++- 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index 1892d472..94847735 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -1,14 +1,25 @@ package seng302.controllers; +import javafx.application.Platform; +import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; +import javafx.scene.paint.*; +import javafx.scene.paint.Color; +import seng302.models.parsers.StreamPacket; +import seng302.models.parsers.StreamParser; +import java.awt.*; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import java.util.Timer; +import java.util.TimerTask; /** * Created by michaelrausch on 21/03/17. @@ -16,6 +27,12 @@ import java.util.ResourceBundle; public class Controller implements Initializable { @FXML private AnchorPane contentPane; + @FXML + private Label timeTillLive; + @FXML + private Button streamButton; + @FXML + private Button switchToRaceViewButton; private void setContentPane(String jfxUrl){ try{ @@ -33,6 +50,38 @@ public class Controller implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { + + } + + public void startStream() { + if (StreamParser.isStreamStatus()) { + streamButton.setVisible(false); + timeTillLive.setVisible(true); + timeTillLive.setTextFill(Color.GREEN); + timeTillLive.setText("Connecting..."); + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + Platform.runLater(() -> { + if (StreamParser.getTimeSinceStart() != 0) { + timeTillLive.setTextFill(Color.BLACK); + switchToRaceViewButton.setDisable(false); + Long timerMinute = -1 * StreamParser.getTimeSinceStart() / 60; + Long timerSecond = -1 * StreamParser.getTimeSinceStart() % 60; + String timerString = timerMinute + "." + timerSecond + " minutes"; + timeTillLive.setText(timerString); + } + }); + } + }, 0, 500); + } else { + timeTillLive.setText("Stream not available."); + timeTillLive.setTextFill(Color.RED); + } + } + + public void switchToRaceView() { setContentPane("/views/RaceView.fxml"); } } diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index 766296a7..32d2eb4e 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -29,9 +29,11 @@ public class StreamParser extends Thread{ public static ConcurrentHashMap boatPositions = new ConcurrentHashMap<>(); public static ConcurrentHashMap boatSpeeds = new ConcurrentHashMap<>(); - private String threadName; + private String threadName; private Thread t; private static boolean raceStarted = false; + private static boolean streamStatus = false; + private static long timeSinceStart = 0; public StreamParser(String threadName){ this.threadName = threadName; @@ -44,6 +46,7 @@ public class StreamParser extends Thread{ public void run(){ try { System.out.println("START OF STREAM"); + streamStatus = true; while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) { Thread.sleep(1); } @@ -152,7 +155,7 @@ public class StreamParser extends Thread{ } if (timeTillStart % 10 == 0){ System.out.println("Time since start: " + -1 * timeTillStart + " Seconds"); - + timeSinceStart = timeTillStart; } } long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20)); @@ -400,5 +403,32 @@ public class StreamParser extends Thread{ } return partialLong; } + + /** + * returns false if race not started, true otherwise + * + * @return race started status + */ + public static boolean isRaceStarted() { + return raceStarted; + } + + /** + * returns false if stream not connected, true otherwise + * + * @return stream started status + */ + public static boolean isStreamStatus() { + return streamStatus; + } + + /** + * returns race timer + * + * @return race timer in long + */ + public static long getTimeSinceStart() { + return timeSinceStart; + } } diff --git a/src/main/resources/views/MainView.fxml b/src/main/resources/views/MainView.fxml index ac0b944e..15559900 100644 --- a/src/main/resources/views/MainView.fxml +++ b/src/main/resources/views/MainView.fxml @@ -1,11 +1,48 @@ + + - + + + + + + + + + + + + + + + + +