mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Parsing classes now static utilities. Data now moved to model via controller class. Race logic shifted out of grpahics classes. Several improvements to code readability.
#story[986] #refactor
This commit is contained in:
@@ -7,7 +7,7 @@ import javafx.scene.Scene;
|
|||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import seng302.model.PolarTable;
|
import seng302.model.PolarTable;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.StreamReceiver;
|
import seng302.model.stream.StreamReceiver;
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package seng302.gameServer;
|
|||||||
|
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.stream.PacketBufferDelegate;
|
import seng302.model.stream.PacketBufferDelegate;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
package seng302.gameServer;
|
package seng302.gameServer;
|
||||||
|
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.gameServer.GameState;
|
|
||||||
import seng302.models.Player;
|
|
||||||
import seng302.models.stream.PacketBufferDelegate;
|
|
||||||
import seng302.models.stream.StreamParser;
|
|
||||||
import seng302.models.stream.packets.StreamPacket;
|
|
||||||
import seng302.server.messages.ChatterMessage;
|
import seng302.server.messages.ChatterMessage;
|
||||||
import seng302.server.messages.Heartbeat;
|
|
||||||
import seng302.server.messages.Message;
|
import seng302.server.messages.Message;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package seng302.model;
|
|||||||
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.visualiser.controllers.RaceViewController;
|
import seng302.visualiser.controllers.RaceViewController;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -13,10 +14,10 @@ import java.text.SimpleDateFormat;
|
|||||||
* Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class,
|
* Class created to store more variables (eg. boat statuses) compared to the XMLParser boat class,
|
||||||
* also done outside Boat class because some old variables are not used anymore.
|
* also done outside Boat class because some old variables are not used anymore.
|
||||||
*/
|
*/
|
||||||
public class Yacht {
|
public class Boat {
|
||||||
|
|
||||||
// Used in boat group
|
// Used in boat group
|
||||||
private Color colour;
|
private Color colour = Color.BLACK;
|
||||||
|
|
||||||
private String boatType;
|
private String boatType;
|
||||||
private Integer sourceID;
|
private Integer sourceID;
|
||||||
@@ -25,19 +26,16 @@ public class Yacht {
|
|||||||
private String boatName;
|
private String boatName;
|
||||||
private String country;
|
private String country;
|
||||||
|
|
||||||
// Situational data
|
|
||||||
|
|
||||||
|
|
||||||
// Boat status
|
// Boat status
|
||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Integer legNumber;
|
private Integer legNumber = 0;
|
||||||
|
private Integer position = 0;
|
||||||
private Integer penaltiesAwarded;
|
private Integer penaltiesAwarded;
|
||||||
private Integer penaltiesServed;
|
private Integer penaltiesServed;
|
||||||
private Long estimateTimeAtFinish;
|
private Long estimateTimeAtFinish;
|
||||||
private String position;
|
|
||||||
private Double lat;
|
private Double lat;
|
||||||
private Double lon;
|
private Double lon;
|
||||||
private Float heading;
|
private Double heading;
|
||||||
private double velocity;
|
private double velocity;
|
||||||
private Long timeTillNext;
|
private Long timeTillNext;
|
||||||
private Long markRoundTime;
|
private Long markRoundTime;
|
||||||
@@ -46,31 +44,7 @@ public class Yacht {
|
|||||||
private Mark lastMarkRounded;
|
private Mark lastMarkRounded;
|
||||||
private Mark nextMark;
|
private Mark nextMark;
|
||||||
|
|
||||||
|
public Boat(String boatType, Integer sourceID, String hullID, String shortName,
|
||||||
/**
|
|
||||||
* Used in EventTest and RaceTest.
|
|
||||||
*
|
|
||||||
* @param boatName Create a yacht object with name.
|
|
||||||
*/
|
|
||||||
public Yacht(String boatName) {
|
|
||||||
this.boatName = boatName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used in BoatGroupTest.
|
|
||||||
*
|
|
||||||
* @param boatName The name of the team sailing the boat
|
|
||||||
* @param boatVelocity The speed of the boat in meters/second
|
|
||||||
* @param shortName A shorter version of the teams name
|
|
||||||
*/
|
|
||||||
public Yacht(String boatName, double boatVelocity, String shortName, int id) {
|
|
||||||
this.boatName = boatName;
|
|
||||||
this.velocity = boatVelocity;
|
|
||||||
this.shortName = shortName;
|
|
||||||
this.sourceID = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Yacht(String boatType, Integer sourceID, String hullID, String shortName,
|
|
||||||
String boatName, String country) {
|
String boatName, String country) {
|
||||||
this.boatType = boatType;
|
this.boatType = boatType;
|
||||||
this.sourceID = sourceID;
|
this.sourceID = sourceID;
|
||||||
@@ -78,7 +52,6 @@ public class Yacht {
|
|||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
this.boatName = boatName;
|
this.boatName = boatName;
|
||||||
this.country = country;
|
this.country = country;
|
||||||
this.position = "-";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBoatType() {
|
public String getBoatType() {
|
||||||
@@ -118,28 +91,9 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLegNumber(Integer legNumber) {
|
public void setLegNumber(Integer legNumber) {
|
||||||
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus(sourceID)) {
|
|
||||||
RaceViewController.updateYachtPositionSparkline(this, legNumber);
|
|
||||||
}
|
|
||||||
this.legNumber = legNumber;
|
this.legNumber = legNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getPenaltiesAwarded() {
|
|
||||||
return penaltiesAwarded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPenaltiesAwarded(Integer penaltiesAwarded) {
|
|
||||||
this.penaltiesAwarded = penaltiesAwarded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPenaltiesServed() {
|
|
||||||
return penaltiesServed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPenaltiesServed(Integer penaltiesServed) {
|
|
||||||
this.penaltiesServed = penaltiesServed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
|
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
|
||||||
timeTillNext = estimateTimeAtNextMark;
|
timeTillNext = estimateTimeAtNextMark;
|
||||||
}
|
}
|
||||||
@@ -153,11 +107,11 @@ public class Yacht {
|
|||||||
this.estimateTimeAtFinish = estimateTimeAtFinish;
|
this.estimateTimeAtFinish = estimateTimeAtFinish;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPosition() {
|
public Integer getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(String position) {
|
public void setPosition(Integer position) {
|
||||||
this.position = position;
|
this.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,11 +176,11 @@ public class Yacht {
|
|||||||
this.lon = lon;
|
this.lon = lon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float getHeading() {
|
public Double getHeading() {
|
||||||
return heading;
|
return heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHeading(Float heading) {
|
public void setHeading(Double heading) {
|
||||||
this.heading = heading;
|
this.heading = heading;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ package seng302.model;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for randomly generating colours.
|
* Enum for generating colours.
|
||||||
*/
|
*/
|
||||||
public enum Colors {
|
public enum Colors {
|
||||||
RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE;
|
RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE;
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package seng302.model;
|
||||||
|
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the data for the cornering of a mark.
|
||||||
|
*/
|
||||||
|
public class Corner {
|
||||||
|
|
||||||
|
private Integer seqID;
|
||||||
|
private Integer compoundMarkID;
|
||||||
|
private String rounding;
|
||||||
|
private Integer zoneSize;
|
||||||
|
|
||||||
|
public Corner(Integer seqID, Integer compoundMarkID, String rounding, Integer zoneSize) {
|
||||||
|
this.seqID = seqID;
|
||||||
|
this.compoundMarkID = compoundMarkID;
|
||||||
|
this.rounding = rounding;
|
||||||
|
this.zoneSize = zoneSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSeqID() {
|
||||||
|
return seqID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCompoundMarkID() {
|
||||||
|
return compoundMarkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRounding() {
|
||||||
|
return rounding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getZoneSize() {
|
||||||
|
return zoneSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package seng302.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores data on the border of a race
|
||||||
|
*/
|
||||||
|
public class Limit {
|
||||||
|
|
||||||
|
private Integer seqID;
|
||||||
|
private Double lat;
|
||||||
|
private Double lng;
|
||||||
|
|
||||||
|
public Limit(Integer seqID, Double lat, Double lng) {
|
||||||
|
this.seqID = seqID;
|
||||||
|
this.lat = lat;
|
||||||
|
this.lng = lng;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSeqID() {
|
||||||
|
return seqID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getLng() {
|
||||||
|
return lng;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import java.net.Socket;
|
|||||||
public class Player {
|
public class Player {
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private Yacht yacht;
|
private Boat boat;
|
||||||
private Integer lastMarkPassed;
|
private Integer lastMarkPassed;
|
||||||
|
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ public class Player {
|
|||||||
this.lastMarkPassed = lastMarkPassed;
|
this.lastMarkPassed = lastMarkPassed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Yacht getYacht() {
|
public Boat getYacht() {
|
||||||
return yacht;
|
return boat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package seng302.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.LongProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for storing race data that does not relate to specific vessels or marks such as time or wind
|
||||||
|
*/
|
||||||
|
public class RaceStatus {
|
||||||
|
double windSpeed;
|
||||||
|
double windDirection;
|
||||||
|
long raceTime;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ public abstract class Mark {
|
|||||||
private MarkType markType;
|
private MarkType markType;
|
||||||
private double latitude;
|
private double latitude;
|
||||||
private double longitude;
|
private double longitude;
|
||||||
private long id;
|
private int id;
|
||||||
private int compoundMarkID;
|
private int compoundMarkID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +134,7 @@ public abstract class Mark {
|
|||||||
return longitude;
|
return longitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,649 +0,0 @@
|
|||||||
package seng302.model.stream;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
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.model.Yacht;
|
|
||||||
import seng302.model.mark.Mark;
|
|
||||||
import seng302.model.stream.packets.BoatPositionPacket;
|
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 available
|
|
||||||
* Created by kre39 on 23/04/17.
|
|
||||||
*/
|
|
||||||
public class StreamParser{
|
|
||||||
|
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>();
|
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>();
|
|
||||||
private static boolean newRaceXmlReceived = false;
|
|
||||||
private static boolean raceStarted = false;
|
|
||||||
private static XMLParser xmlObject;
|
|
||||||
private static boolean raceFinished = false;
|
|
||||||
private static boolean streamStatus = false;
|
|
||||||
private static long timeSinceStart = -1;
|
|
||||||
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
|
|
||||||
private static Map<Integer, Yacht> boatsPos = new ConcurrentSkipListMap<>();
|
|
||||||
private static double windDirection = 0;
|
|
||||||
private static Double windSpeed = 0d;
|
|
||||||
private static Long currentTimeLong;
|
|
||||||
private static String currentTimeString;
|
|
||||||
|
|
||||||
|
|
||||||
//CONVERSION CONSTANTS
|
|
||||||
private static final Double MS_TO_KNOTS = 1.94384;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to initialise the thread name and stream parser object so a thread can be executed
|
|
||||||
*/
|
|
||||||
public StreamParser() {
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Looks at the type of the packet then sends it to the appropriate parser to extract the
|
|
||||||
* specific data associated with that packet type
|
|
||||||
*
|
|
||||||
* @param packet the packet to be looked at and processed
|
|
||||||
*/
|
|
||||||
public static void parsePacket(StreamPacket packet) {
|
|
||||||
try {
|
|
||||||
switch (packet.getType()) {
|
|
||||||
case HEARTBEAT:
|
|
||||||
extractHeartBeat(packet);
|
|
||||||
break;
|
|
||||||
case RACE_STATUS:
|
|
||||||
extractRaceStatus(packet);
|
|
||||||
break;
|
|
||||||
case DISPLAY_TEXT_MESSAGE:
|
|
||||||
extractDisplayMessage(packet);
|
|
||||||
break;
|
|
||||||
case XML_MESSAGE:
|
|
||||||
newRaceXmlReceived = true;
|
|
||||||
extractXmlMessage(packet);
|
|
||||||
break;
|
|
||||||
case RACE_START_STATUS:
|
|
||||||
extractRaceStartStatus(packet);
|
|
||||||
break;
|
|
||||||
case YACHT_EVENT_CODE:
|
|
||||||
extractYachtEventCode(packet);
|
|
||||||
break;
|
|
||||||
case YACHT_ACTION_CODE:
|
|
||||||
extractYachtActionCode(packet);
|
|
||||||
break;
|
|
||||||
case CHATTER_TEXT:
|
|
||||||
extractChatterText(packet);
|
|
||||||
break;
|
|
||||||
case BOAT_LOCATION:
|
|
||||||
extractBoatLocation(packet);
|
|
||||||
break;
|
|
||||||
case MARK_ROUNDING:
|
|
||||||
extractMarkRounding(packet);
|
|
||||||
break;
|
|
||||||
case COURSE_WIND:
|
|
||||||
extractCourseWind(packet);
|
|
||||||
break;
|
|
||||||
case AVG_WIND:
|
|
||||||
extractAvgWind(packet);
|
|
||||||
break;
|
|
||||||
case BOAT_ACTION:
|
|
||||||
extractBoatAction(packet);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
System.out.println("Error parsing packet");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getTimeZoneString() {
|
|
||||||
|
|
||||||
Integer offset = xmlObject.getRegattaXML().getUtcOffset();
|
|
||||||
StringBuilder utcOffset = new StringBuilder();
|
|
||||||
utcOffset.append("GMT");
|
|
||||||
if (offset > 0) {
|
|
||||||
utcOffset.append("+");
|
|
||||||
utcOffset.append(offset);
|
|
||||||
} else if (offset < 0) {
|
|
||||||
utcOffset.append("-");
|
|
||||||
utcOffset.append(offset);
|
|
||||||
}
|
|
||||||
return utcOffset.toString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
|
|
||||||
int raceStatus = payload[11];
|
|
||||||
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
|
|
||||||
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
|
|
||||||
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
|
|
||||||
|
|
||||||
currentTimeLong = currentTime;
|
|
||||||
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
|
||||||
if (xmlObject.getRegattaXML() != null) {
|
|
||||||
format.setTimeZone(TimeZone.getTimeZone(getTimeZoneString()));
|
|
||||||
currentTimeString = format.format((new Date(currentTime)).getTime());
|
|
||||||
}
|
|
||||||
long timeTillStart =
|
|
||||||
((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
|
|
||||||
|
|
||||||
if (timeTillStart > 0) {
|
|
||||||
timeSinceStart = timeTillStart;
|
|
||||||
} else {
|
|
||||||
if (raceStatus == 4 || raceStatus == 8) {
|
|
||||||
raceFinished = true;
|
|
||||||
raceStarted = false;
|
|
||||||
} else if (!raceStarted) {
|
|
||||||
raceStarted = true;
|
|
||||||
raceFinished = false;
|
|
||||||
}
|
|
||||||
timeSinceStart = timeTillStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
double windDirFactor = 0x4000 / 90; //0x4000 is 90 degrees, 0x8000 is 180 degrees, etc...
|
|
||||||
windDirection = windDir / windDirFactor;
|
|
||||||
windSpeed = rawWindSpeed / 1000 * MS_TO_KNOTS;
|
|
||||||
|
|
||||||
int noBoats = payload[22];
|
|
||||||
int raceType = payload[23];
|
|
||||||
for (int i = 0; i < noBoats; i++) {
|
|
||||||
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)]);
|
|
||||||
setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
|
|
||||||
boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
|
||||||
boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
|
||||||
Long estTimeAtNextMark = bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
|
||||||
boat.setEstimateTimeAtNextMark(estTimeAtNextMark);
|
|
||||||
Long estTimeAtFinish = bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
|
||||||
boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
|
||||||
// boatsPos.put(estTimeAtFinish, boat);
|
|
||||||
// String boatStatus = "SourceID: " + boatStatusSourceID;
|
|
||||||
// boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)];
|
|
||||||
// boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)];
|
|
||||||
// boatStatus += "\nPenaltiesAwarded: " + (int)payload[29 + (i * 20)];
|
|
||||||
// boatStatus += "\nPenaltiesServed: " + (int)payload[30 + (i * 20)];
|
|
||||||
// boatStatus += "\nEstTimeAtNextMark: " + bytesToLong(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20)));
|
|
||||||
// boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20)));
|
|
||||||
// boatStatuses.add(boatStatus);
|
|
||||||
}
|
|
||||||
// if (isRaceStarted()) {
|
|
||||||
// int pos = 1;
|
|
||||||
// for (Yacht yacht : boatsPos.values()) {
|
|
||||||
// yacht.setPosition(String.valueOf(pos));
|
|
||||||
// pos++;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// for (Yacht yacht : boatsPos.values()) {
|
|
||||||
// yacht.setPosition("-");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
|
||||||
Integer placing = 1;
|
|
||||||
if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
|
|
||||||
for (Yacht boat : boats.values()) {
|
|
||||||
if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
|
||||||
placing += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updatingBoat.setPosition(placing.toString());
|
|
||||||
updatingBoat.setLegNumber(leg);
|
|
||||||
boatsPos.putIfAbsent(placing, updatingBoat);
|
|
||||||
boatsPos.replace(placing, updatingBoat);
|
|
||||||
} else if(updatingBoat.getLegNumber() == null){
|
|
||||||
updatingBoat.setPosition("1");
|
|
||||||
updatingBoat.setLegNumber(leg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
int numOfLines = payload[3];
|
|
||||||
int totalLen = 0;
|
|
||||||
for (int i = 0; i < numOfLines; i++) {
|
|
||||||
int lineNum = payload[4 + totalLen];
|
|
||||||
int textLength = payload[5 + totalLen];
|
|
||||||
byte[] messageTextBytes = Arrays
|
|
||||||
.copyOfRange(payload, 6 + totalLen, 6 + textLength + totalLen);
|
|
||||||
String messageText = new String(messageTextBytes);
|
|
||||||
totalLen += 2 + textLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
int messageType = payload[9];
|
|
||||||
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
|
|
||||||
String xmlMessage = new String(
|
|
||||||
(Arrays.copyOfRange(payload, 14, (int) (14 + messageLength)))).trim();
|
|
||||||
|
|
||||||
//Create XML document Object
|
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
||||||
DocumentBuilder db = null;
|
|
||||||
Document doc = null;
|
|
||||||
try {
|
|
||||||
db = dbf.newDocumentBuilder();
|
|
||||||
doc = db.parse(new InputSource(new StringReader(xmlMessage)));
|
|
||||||
} catch (ParserConfigurationException | IOException | SAXException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlObject.constructXML(doc, messageType);
|
|
||||||
if (messageType == 7) { //7 is the boat XML
|
|
||||||
boats = xmlObject.getBoatXML().getCompetingBoats();
|
|
||||||
}
|
|
||||||
if (messageType == 6) { //6 is race info xml
|
|
||||||
|
|
||||||
newRaceXmlReceived = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long raceStartTime = bytesToLong(Arrays.copyOfRange(payload, 9, 15));
|
|
||||||
long raceId = bytesToLong(Arrays.copyOfRange(payload, 15, 19));
|
|
||||||
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];
|
|
||||||
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
|
||||||
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
|
||||||
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21));
|
|
||||||
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];
|
|
||||||
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
|
||||||
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
|
||||||
int eventId = payload[17];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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];
|
|
||||||
int messageType = payload[1];
|
|
||||||
int length = payload[2];
|
|
||||||
String message = new String(Arrays.copyOfRange(payload, 3, 3 + length));
|
|
||||||
System.out.println(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
int deviceType = (int) payload[15];
|
|
||||||
long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long seq = bytesToLong(Arrays.copyOfRange(payload, 11, 15));
|
|
||||||
long boatId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
|
|
||||||
long rawLat = bytesToLong(Arrays.copyOfRange(payload, 16, 20));
|
|
||||||
long rawLon = bytesToLong(Arrays.copyOfRange(payload, 20, 24));
|
|
||||||
//Converts the double to a usable lat/lon
|
|
||||||
double lat = ((180d * (double) rawLat) / Math.pow(2, 31));
|
|
||||||
double lon = ((180d * (double) rawLon) / Math.pow(2, 31));
|
|
||||||
long heading = bytesToLong(Arrays.copyOfRange(payload, 28, 30));
|
|
||||||
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){
|
|
||||||
Yacht boat = boats.get((int) boatId);
|
|
||||||
boat.setVelocity(groundSpeed);
|
|
||||||
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed);
|
|
||||||
|
|
||||||
//add a new priority que to the boatLocations HashMap
|
|
||||||
if (!boatLocations.containsKey(boatId)) {
|
|
||||||
boatLocations.put(boatId,
|
|
||||||
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
|
||||||
@Override
|
|
||||||
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
|
||||||
return (int) (p1.getTimeValid() - p2.getTimeValid());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
boatLocations.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 boatLocations HashMap
|
|
||||||
if (!markLocations.containsKey(boatId)) {
|
|
||||||
markLocations.put(boatId,
|
|
||||||
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
|
||||||
@Override
|
|
||||||
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
|
||||||
return (int) (p1.getTimeValid() - p2.getTimeValid());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
markLocations.get(boatId).put(markPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This packet type is received when a mark or gate is rounded by a boat
|
|
||||||
*
|
|
||||||
* @param packet The packet containing the payload
|
|
||||||
*/
|
|
||||||
private static void extractMarkRounding(StreamPacket packet) {
|
|
||||||
byte[] payload = packet.getPayload();
|
|
||||||
int messageVersionNo = payload[0];
|
|
||||||
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
|
||||||
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
|
||||||
int boatStatus = payload[17];
|
|
||||||
int roundingSide = payload[18];
|
|
||||||
int markType = payload[19];
|
|
||||||
int markId = payload[20];
|
|
||||||
|
|
||||||
// assign mark rounding time to boat
|
|
||||||
boats.get((int)subjectId).setMarkRoundingTime(timeStamp);
|
|
||||||
|
|
||||||
for (Mark mark : xmlObject.getRaceXML().getAllCompoundMarks()) {
|
|
||||||
if (mark.getCompoundMarkID() == markId) {
|
|
||||||
boats.get((int)subjectId).setLastMarkRounded(mark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Long[] array = new Long[]{subjectId, timeStamp, (long) markId};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This packet type contains periodic data on the state of the wind
|
|
||||||
*
|
|
||||||
* @param packet The packet containing the payload
|
|
||||||
*/
|
|
||||||
private static void extractCourseWind(StreamPacket packet) {
|
|
||||||
byte[] payload = packet.getPayload();
|
|
||||||
int messageVersionNo = payload[0];
|
|
||||||
int selectedWindId = payload[1];
|
|
||||||
int loopCount = payload[2];
|
|
||||||
List<String> windInfo = new ArrayList<>();
|
|
||||||
for (int i = 0; i < loopCount; i++) {
|
|
||||||
String wind = "WindId: " + payload[3 + (20 * i)];
|
|
||||||
wind +=
|
|
||||||
"\nTime: " + bytesToLong(Arrays.copyOfRange(payload, 4 + (20 * i), 10 + (20 * i)));
|
|
||||||
wind += "\nRaceId: " + bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 10 + (20 * i), 14 + (20 * i)));
|
|
||||||
wind += "\nWindDirection: " + bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 14 + (20 * i), 16 + (20 * i)));
|
|
||||||
wind += "\nWindSpeed: " + bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 16 + (20 * i), 18 + (20 * i)));
|
|
||||||
wind += "\nBestUpWindAngle: " + bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 18 + (20 * i), 20 + (20 * i)));
|
|
||||||
wind += "\nBestDownWindAngle: " + bytesToLong(
|
|
||||||
Arrays.copyOfRange(payload, 20 + (20 * i), 22 + (20 * i)));
|
|
||||||
wind += "\nFlags: " + String
|
|
||||||
.format("%8s", Integer.toBinaryString(payload[22 + (20 * i)] & 0xFF))
|
|
||||||
.replace(' ', '0');
|
|
||||||
windInfo.add(wind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This packet conatins the average wind to ground speed
|
|
||||||
*
|
|
||||||
* @param packet The packet containing the paylaod
|
|
||||||
*/
|
|
||||||
private static void extractAvgWind(StreamPacket packet) {
|
|
||||||
byte[] payload = packet.getPayload();
|
|
||||||
int messageVersionNo = payload[0];
|
|
||||||
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
|
||||||
long rawPeriod = bytesToLong(Arrays.copyOfRange(payload, 7, 9));
|
|
||||||
long rawSamplePeriod = bytesToLong(Arrays.copyOfRange(payload, 9, 11));
|
|
||||||
long period2 = bytesToLong(Arrays.copyOfRange(payload, 11, 13));
|
|
||||||
long speed2 = bytesToLong(Arrays.copyOfRange(payload, 13, 15));
|
|
||||||
long period3 = bytesToLong(Arrays.copyOfRange(payload, 15, 17));
|
|
||||||
long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19));
|
|
||||||
long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21));
|
|
||||||
long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void extractBoatAction(StreamPacket packet) {
|
|
||||||
byte[] payload = packet.getPayload();
|
|
||||||
int messageVersionNo = payload[0];
|
|
||||||
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
|
|
||||||
if (actionType == 1) {
|
|
||||||
System.out.println("VMG");
|
|
||||||
} else if (actionType == 2) {
|
|
||||||
System.out.println("SAILS IN");
|
|
||||||
} else if (actionType == 3) {
|
|
||||||
System.out.println("SAILS OUT");
|
|
||||||
} else if (actionType == 4) {
|
|
||||||
System.out.println("TACK/GYBE");
|
|
||||||
} else if (actionType == 5) {
|
|
||||||
System.out.println("UPWIND");
|
|
||||||
} else if (actionType == 6) {
|
|
||||||
System.out.println("DOWNWIND");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* takes an array of up to 7 bytes and returns a positive
|
|
||||||
* long constructed from the input bytes
|
|
||||||
*
|
|
||||||
* @return a positive long if there is less than 7 bytes -1 otherwise
|
|
||||||
*/
|
|
||||||
private static long bytesToLong(byte[] bytes) {
|
|
||||||
long partialLong = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (byte b : bytes) {
|
|
||||||
if (index > 6) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return false if race not finished, true otherwise
|
|
||||||
*
|
|
||||||
* @return race finished status
|
|
||||||
*/
|
|
||||||
public static boolean isRaceFinished() {
|
|
||||||
return raceFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a map of boats with sourceID and the boat
|
|
||||||
*
|
|
||||||
* @return map of boats
|
|
||||||
*/
|
|
||||||
public static Map<Integer, Yacht> getBoats() {
|
|
||||||
return boats;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the latest updated object from xml parser
|
|
||||||
*
|
|
||||||
* @return the latest xml object
|
|
||||||
*/
|
|
||||||
public static XMLParser getXmlObject() {
|
|
||||||
return xmlObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the wind direction in degrees
|
|
||||||
*
|
|
||||||
* @return a double wind direction value
|
|
||||||
*/
|
|
||||||
public static double getWindDirection() {
|
|
||||||
return windDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the wind speed in knots
|
|
||||||
* @return A double indicating the wind speed in knots
|
|
||||||
*/
|
|
||||||
public static Double getWindSpeed() {
|
|
||||||
return windSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns stream time in formatted string format
|
|
||||||
*
|
|
||||||
* @return String of stream time
|
|
||||||
*/
|
|
||||||
public static String getCurrentTimeString() {
|
|
||||||
return currentTimeString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* used in boat position since tree map can sort position efficiently.
|
|
||||||
*
|
|
||||||
* @return a map of time to finish and boat.
|
|
||||||
*/
|
|
||||||
public static Map<Integer, Yacht> getBoatsPos() {
|
|
||||||
|
|
||||||
return boatsPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns current time in stream in long
|
|
||||||
*
|
|
||||||
* @return a long value of current time
|
|
||||||
*/
|
|
||||||
public static Long getCurrentTimeLong() {
|
|
||||||
return currentTimeLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to check if a new un-processed xml has been found, if so will return true before
|
|
||||||
* toggling off so that the next check will return false.
|
|
||||||
*
|
|
||||||
* @return the status of if new xml has been received
|
|
||||||
*/
|
|
||||||
public static boolean isNewRaceXmlReceived() {
|
|
||||||
if (newRaceXmlReceived) {
|
|
||||||
newRaceXmlReceived = false;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -116,25 +116,6 @@ public class StreamReceiver extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 8 bytes -1 otherwise
|
|
||||||
*/
|
|
||||||
private long bytesToLong(byte[] bytes){
|
|
||||||
long partialLong = 0;
|
|
||||||
int index = 0;
|
|
||||||
for (byte b: bytes){
|
|
||||||
if (index > 6){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return partialLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1");
|
StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1");
|
||||||
|
|||||||
@@ -1,610 +0,0 @@
|
|||||||
package seng302.model.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.model.Yacht;
|
|
||||||
import seng302.model.mark.GateMark;
|
|
||||||
import seng302.model.mark.Mark;
|
|
||||||
import seng302.model.mark.MarkType;
|
|
||||||
import seng302.model.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.
|
|
||||||
*/
|
|
||||||
public class XMLParser {
|
|
||||||
|
|
||||||
private Document xmlDoc;
|
|
||||||
|
|
||||||
private RaceXMLObject raceXML;
|
|
||||||
private RegattaXMLObject regattaXML;
|
|
||||||
private BoatXMLObject boatXML;
|
|
||||||
|
|
||||||
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).
|
|
||||||
*/
|
|
||||||
public void constructXML(Document doc, Integer messageType) {
|
|
||||||
this.xmlDoc = doc;
|
|
||||||
switch (messageType) {
|
|
||||||
case 5:
|
|
||||||
regattaXML = new RegattaXMLObject(this.xmlDoc);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
raceXML = new RaceXMLObject(this.xmlDoc);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
boatXML = new BoatXMLObject(this.xmlDoc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
private static Integer getElementInt(Element ele, String tag) {
|
|
||||||
NodeList tagList = ele.getElementsByTagName(tag);
|
|
||||||
if (tagList.getLength() > 0) {
|
|
||||||
return Integer.parseInt(tagList.item(0).getTextContent());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private static String getElementString(Element ele, String tag) {
|
|
||||||
NodeList tagList = ele.getElementsByTagName(tag);
|
|
||||||
if (tagList.getLength() > 0) {
|
|
||||||
return tagList.item(0).getTextContent();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private static Double getElementDouble(Element ele, String tag) {
|
|
||||||
NodeList tagList = ele.getElementsByTagName(tag);
|
|
||||||
if (tagList.getLength() > 0) {
|
|
||||||
return Double.parseDouble(tagList.item(0).getTextContent());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private static String getNodeAttributeString(Node n, String attr) {
|
|
||||||
Node attrItem = n.getAttributes().getNamedItem(attr);
|
|
||||||
if (attrItem != null) {
|
|
||||||
return attrItem.getTextContent();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private static Integer getNodeAttributeInt(Node n, String attr) {
|
|
||||||
Node attrItem = n.getAttributes().getNamedItem(attr);
|
|
||||||
if (attrItem != null) {
|
|
||||||
return Integer.parseInt(attrItem.getTextContent());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
private static Double getNodeAttributeDouble(Node n, String attr) {
|
|
||||||
Node attrItem = n.getAttributes().getNamedItem(attr);
|
|
||||||
if (attrItem != null) {
|
|
||||||
return Double.parseDouble(attrItem.getTextContent());
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RegattaXMLObject {
|
|
||||||
|
|
||||||
//Regatta Info
|
|
||||||
private Integer regattaID;
|
|
||||||
private String regattaName;
|
|
||||||
private String courseName;
|
|
||||||
private Double centralLat;
|
|
||||||
private Double centralLng;
|
|
||||||
private Integer utcOffset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
Element docEle = doc.getDocumentElement();
|
|
||||||
|
|
||||||
this.regattaID = getElementInt(docEle, "RegattaID");
|
|
||||||
this.regattaName = getElementString(docEle, "RegattaName");
|
|
||||||
this.courseName = getElementString(docEle, "CourseName");
|
|
||||||
this.centralLat = getElementDouble(docEle, "CentralLatitude");
|
|
||||||
this.centralLng = getElementDouble(docEle, "CentralLongitude");
|
|
||||||
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 class RaceXMLObject {
|
|
||||||
|
|
||||||
// Race Info
|
|
||||||
private Integer raceID;
|
|
||||||
private String raceType;
|
|
||||||
private String creationTimeDate; // XML Creation Time
|
|
||||||
|
|
||||||
//Race Start Details
|
|
||||||
private String raceStartTime;
|
|
||||||
private Boolean postponeStatus;
|
|
||||||
|
|
||||||
//Non atomic race attributes
|
|
||||||
private ArrayList<Participant> participants;
|
|
||||||
private ArrayList<Mark> allMarks;
|
|
||||||
private ArrayList<Mark> nonDuplicateMarks;
|
|
||||||
private ArrayList<Corner> compoundMarkSequence;
|
|
||||||
private ArrayList<Limit> courseLimit;
|
|
||||||
|
|
||||||
// ensures there's no duplicate marks.
|
|
||||||
private List<Long> seenSourceIDs = new ArrayList<Long>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
Element docEle = doc.getDocumentElement();
|
|
||||||
|
|
||||||
//Atomic and Semi-Atomic Elements
|
|
||||||
this.raceID = getElementInt(docEle, "RaceID");
|
|
||||||
this.raceType = getElementString(docEle, "RaceType");
|
|
||||||
this.creationTimeDate = getElementString(docEle, "CreationTimeDate");
|
|
||||||
|
|
||||||
Node raceStart = docEle.getElementsByTagName("RaceStartTime").item(0);
|
|
||||||
this.raceStartTime = getNodeAttributeString(raceStart, "Start");
|
|
||||||
this.postponeStatus = Boolean
|
|
||||||
.parseBoolean(getNodeAttributeString(raceStart, "Postpone"));
|
|
||||||
|
|
||||||
//Participants
|
|
||||||
participants = new ArrayList<>();
|
|
||||||
|
|
||||||
NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes();
|
|
||||||
for (int i = 0; i < pList.getLength(); i++) {
|
|
||||||
Node pNode = pList.item(i);
|
|
||||||
String entry;
|
|
||||||
if (pNode.getNodeName().equals("Yacht")) {
|
|
||||||
Integer sourceID = getNodeAttributeInt(pNode, "SourceID");
|
|
||||||
|
|
||||||
if (pNode.getAttributes().getLength() == 2) {
|
|
||||||
entry = getNodeAttributeString(pNode, "Entry");
|
|
||||||
} else {
|
|
||||||
entry = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Participant pa = new Participant(sourceID, entry);
|
|
||||||
participants.add(pa);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Course
|
|
||||||
allMarks = new ArrayList<>();
|
|
||||||
nonDuplicateMarks = new ArrayList<>();
|
|
||||||
createCompoundMarks(docEle);
|
|
||||||
|
|
||||||
//Course Mark Sequence
|
|
||||||
compoundMarkSequence = new ArrayList<>();
|
|
||||||
|
|
||||||
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")) {
|
|
||||||
Corner corner = new Corner(cornerNode);
|
|
||||||
compoundMarkSequence.add(corner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Course Limits
|
|
||||||
courseLimit = new ArrayList<>();
|
|
||||||
|
|
||||||
NodeList limitList = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes();
|
|
||||||
for (int i = 0; i < limitList.getLength(); i++) {
|
|
||||||
Node limitNode = limitList.item(i);
|
|
||||||
if (limitNode.getNodeName().equals("Limit")) {
|
|
||||||
Limit limit = new Limit(limitNode);
|
|
||||||
courseLimit.add(limit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void createCompoundMarks(Element docEle) {
|
|
||||||
|
|
||||||
NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
|
|
||||||
for (int i = 0; i < cMarkList.getLength(); i++) {
|
|
||||||
Node cMarkNode = cMarkList.item(i);
|
|
||||||
if (cMarkNode.getNodeName().equals("CompoundMark")) {
|
|
||||||
createAndAddMark(cMarkNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void createAndAddMark(Node compoundMark) {
|
|
||||||
|
|
||||||
Boolean markSeen = false;
|
|
||||||
List<SingleMark> marksList = new ArrayList<>();
|
|
||||||
Integer compoundMarkID = getNodeAttributeInt(compoundMark, "CompoundMarkID");
|
|
||||||
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, compoundMarkID);
|
|
||||||
marksList.add(mark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SingleMark mark : marksList) {
|
|
||||||
if (seenSourceIDs.contains(mark.getId())) {
|
|
||||||
markSeen = true;
|
|
||||||
} else {
|
|
||||||
seenSourceIDs.add(mark.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (marksList.size() == 1) {
|
|
||||||
if (!markSeen) {
|
|
||||||
nonDuplicateMarks.add(marksList.get(0));
|
|
||||||
}
|
|
||||||
allMarks.add(marksList.get(0));
|
|
||||||
} else if (marksList.size() == 2) {
|
|
||||||
GateMark thisGateMark = new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0),
|
|
||||||
marksList.get(1), marksList.get(0).getLatitude(),
|
|
||||||
marksList.get(0).getLongitude(), compoundMarkID);
|
|
||||||
if(!markSeen) {
|
|
||||||
nonDuplicateMarks.add(thisGateMark);
|
|
||||||
}
|
|
||||||
allMarks.add(thisGateMark);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Participant> getParticipants() {
|
|
||||||
return participants;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns ALL compound marks as stated in the RaceXML (INCLUDING DUPLICATE MARKS)
|
|
||||||
*/
|
|
||||||
public List<Mark> getAllCompoundMarks() {
|
|
||||||
return allMarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns Marks from the race XML without any duplicates
|
|
||||||
*/
|
|
||||||
public List<Mark> getNonDupCompoundMarks() {
|
|
||||||
return nonDuplicateMarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<Corner> getCompoundMarkSequence() {
|
|
||||||
return compoundMarkSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<Limit> getCourseLimit() {
|
|
||||||
return courseLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Participant {
|
|
||||||
|
|
||||||
Integer sourceID;
|
|
||||||
String entry;
|
|
||||||
|
|
||||||
Participant(Integer sourceID, String entry) {
|
|
||||||
this.sourceID = sourceID;
|
|
||||||
this.entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getsourceID() {
|
|
||||||
return sourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEntry() {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Corner {
|
|
||||||
|
|
||||||
private Integer seqID;
|
|
||||||
private Integer compoundMarkID;
|
|
||||||
private String rounding;
|
|
||||||
private Integer zoneSize;
|
|
||||||
|
|
||||||
Corner(Node cornerNode) {
|
|
||||||
this.seqID = getNodeAttributeInt(cornerNode, "SeqID");
|
|
||||||
this.compoundMarkID = getNodeAttributeInt(cornerNode, "CompoundMarkID");
|
|
||||||
this.rounding = getNodeAttributeString(cornerNode, "Rounding");
|
|
||||||
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 class Limit {
|
|
||||||
|
|
||||||
private Integer seqID;
|
|
||||||
private Double lat;
|
|
||||||
private Double lng;
|
|
||||||
|
|
||||||
Limit(Node limitNode) {
|
|
||||||
this.seqID = getNodeAttributeInt(limitNode, "SeqID");
|
|
||||||
this.lat = getNodeAttributeDouble(limitNode, "Lat");
|
|
||||||
this.lng = getNodeAttributeDouble(limitNode, "Lon");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getSeqID() {
|
|
||||||
return seqID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getLat() {
|
|
||||||
return lat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getLng() {
|
|
||||||
return lng;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoatXMLObject {
|
|
||||||
|
|
||||||
private String lastModified;
|
|
||||||
private Integer version;
|
|
||||||
|
|
||||||
//Settings for the boat type in the race. This may end up having to be reworked if multiple boat types compete.
|
|
||||||
private String boatType;
|
|
||||||
private Double boatLength;
|
|
||||||
private Double hullLength;
|
|
||||||
private Double markZoneSize;
|
|
||||||
private Double courseZoneSize;
|
|
||||||
private ArrayList<Double> zoneLimits;// will only contain 5 elements. Limits 1-5
|
|
||||||
|
|
||||||
//Boats
|
|
||||||
ArrayList<Yacht> boats;
|
|
||||||
//Competing boats
|
|
||||||
Map<Integer, Yacht> competingBoats = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
|
|
||||||
Element docEle = doc.getDocumentElement();
|
|
||||||
|
|
||||||
this.lastModified = getElementString(docEle, "Modified");
|
|
||||||
this.version = getElementInt(docEle, "Version");
|
|
||||||
|
|
||||||
NodeList settingsList = docEle.getElementsByTagName("Settings").item(0).getChildNodes();
|
|
||||||
this.boatType = getNodeAttributeString(settingsList.item(1), "Type");
|
|
||||||
this.boatLength = getNodeAttributeDouble(settingsList.item(3), "BoatLength");
|
|
||||||
this.hullLength = getNodeAttributeDouble(settingsList.item(3), "HullLength");
|
|
||||||
this.markZoneSize = getNodeAttributeDouble(settingsList.item(5), "MarkZoneSize");
|
|
||||||
this.courseZoneSize = getNodeAttributeDouble(settingsList.item(5), "CourseZoneSize");
|
|
||||||
|
|
||||||
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);
|
|
||||||
this.zoneLimits.add(getNodeAttributeDouble(zoneLimitsList, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.boats = new ArrayList<>();
|
|
||||||
NodeList boatsList = docEle.getElementsByTagName("Boats").item(0).getChildNodes();
|
|
||||||
for (int i = 0; i < boatsList.getLength(); i++) {
|
|
||||||
Node currentBoat = boatsList.item(i);
|
|
||||||
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"));
|
|
||||||
this.boats.add(boat);
|
|
||||||
if (boat.getBoatType().equals("Yacht")) {
|
|
||||||
competingBoats.put(boat.getSourceID(), boat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Double> getZoneLimits() {
|
|
||||||
return zoneLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<Yacht> getBoats() {
|
|
||||||
return boats;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Integer, Yacht> getCompetingBoats() {
|
|
||||||
return competingBoats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package seng302.model.stream.packets;
|
|
||||||
|
|
||||||
public class BoatPositionPacket {
|
|
||||||
private long boatId;
|
|
||||||
private long timeValid;
|
|
||||||
private double lat;
|
|
||||||
private double lon;
|
|
||||||
private double heading;
|
|
||||||
private double groundSpeed;
|
|
||||||
|
|
||||||
public BoatPositionPacket(long boatId, long timeValid, double lat, double lon, double heading, double groundSpeed) {
|
|
||||||
this.boatId = boatId;
|
|
||||||
this.timeValid = timeValid;
|
|
||||||
this.lat = lat;
|
|
||||||
this.lon = lon;
|
|
||||||
this.heading = heading;
|
|
||||||
this.groundSpeed = groundSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimeValid() {
|
|
||||||
return timeValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLat() {
|
|
||||||
return lat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLon() {
|
|
||||||
return lon;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHeading() {
|
|
||||||
return heading;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getGroundSpeed() {
|
|
||||||
return groundSpeed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package seng302.model.stream.packets;
|
package seng302.model.stream.packets;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Kusal on 4/24/2017.
|
* Created by Kusal on 4/24/2017.
|
||||||
*/
|
*/
|
||||||
@@ -9,7 +7,9 @@ public enum PacketType {
|
|||||||
HEARTBEAT,
|
HEARTBEAT,
|
||||||
RACE_STATUS,
|
RACE_STATUS,
|
||||||
DISPLAY_TEXT_MESSAGE,
|
DISPLAY_TEXT_MESSAGE,
|
||||||
XML_MESSAGE,
|
RACE_XML,
|
||||||
|
REGATTA_XML,
|
||||||
|
BOAT_XML,
|
||||||
RACE_START_STATUS,
|
RACE_START_STATUS,
|
||||||
YACHT_EVENT_CODE,
|
YACHT_EVENT_CODE,
|
||||||
YACHT_ACTION_CODE,
|
YACHT_ACTION_CODE,
|
||||||
@@ -21,7 +21,7 @@ public enum PacketType {
|
|||||||
BOAT_ACTION,
|
BOAT_ACTION,
|
||||||
OTHER;
|
OTHER;
|
||||||
|
|
||||||
public static PacketType assignPacketType(int packetType){
|
public static PacketType assignPacketType(int packetType, byte[] payload){
|
||||||
switch(packetType){
|
switch(packetType){
|
||||||
case 1:
|
case 1:
|
||||||
return HEARTBEAT;
|
return HEARTBEAT;
|
||||||
@@ -30,7 +30,14 @@ public enum PacketType {
|
|||||||
case 20:
|
case 20:
|
||||||
return DISPLAY_TEXT_MESSAGE;
|
return DISPLAY_TEXT_MESSAGE;
|
||||||
case 26:
|
case 26:
|
||||||
return XML_MESSAGE;
|
switch (payload[9]) { //The type of XML message
|
||||||
|
case 5:
|
||||||
|
return REGATTA_XML;
|
||||||
|
case 6:
|
||||||
|
return RACE_XML;
|
||||||
|
case 7:
|
||||||
|
return BOAT_XML;
|
||||||
|
}
|
||||||
case 27:
|
case 27:
|
||||||
return RACE_START_STATUS;
|
return RACE_START_STATUS;
|
||||||
case 29:
|
case 29:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class StreamPacket {
|
|||||||
private byte[] payload;
|
private byte[] payload;
|
||||||
|
|
||||||
public StreamPacket(int type, long messageLength, long timeStamp, byte[] payload) {
|
public StreamPacket(int type, long messageLength, long timeStamp, byte[] payload) {
|
||||||
this.type = PacketType.assignPacketType(type);
|
this.type = PacketType.assignPacketType(type, payload);
|
||||||
this.messageLength = messageLength;
|
this.messageLength = messageLength;
|
||||||
this.timeStamp = timeStamp;
|
this.timeStamp = timeStamp;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package seng302.model.stream.parsers;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple data wrapper for mark rounding data packet.
|
||||||
|
*/
|
||||||
|
public class MarkRoundingData {
|
||||||
|
|
||||||
|
private int boatId;
|
||||||
|
private int markId;
|
||||||
|
private int roundingSide;
|
||||||
|
private long timeStamp;
|
||||||
|
|
||||||
|
public MarkRoundingData(int boatId, int markId, int roundingSide, long timeStamp) {
|
||||||
|
this.boatId = boatId;
|
||||||
|
this.markId = markId;
|
||||||
|
this.roundingSide = roundingSide;
|
||||||
|
this.timeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBoatId() {
|
||||||
|
return boatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMarkId() {
|
||||||
|
return markId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRoundingSide() {
|
||||||
|
return roundingSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp() {
|
||||||
|
return timeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package seng302.model.stream.parsers;
|
||||||
|
|
||||||
|
public class PositionUpdateData {
|
||||||
|
|
||||||
|
public enum DeviceType {
|
||||||
|
YACHT_TYPE,
|
||||||
|
MARK_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
private int deviceId;
|
||||||
|
private DeviceType type;
|
||||||
|
private double lat;
|
||||||
|
private double lon;
|
||||||
|
private double heading;
|
||||||
|
private double groundSpeed;
|
||||||
|
|
||||||
|
public PositionUpdateData(int deviceId, DeviceType type, double lat, double lon,
|
||||||
|
double heading, double groundSpeed) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.type = type;
|
||||||
|
this.lat = lat;
|
||||||
|
this.lon = lon;
|
||||||
|
this.heading = heading;
|
||||||
|
this.groundSpeed = groundSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLon() {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHeading() {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getGroundSpeed() {
|
||||||
|
return groundSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package seng302.model.stream.parsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for storing data parsed from race start status packet
|
||||||
|
*/
|
||||||
|
public class RaceStartData {
|
||||||
|
|
||||||
|
long raceId;
|
||||||
|
long raceStartTime;
|
||||||
|
int notificationType;
|
||||||
|
long timeStamp;
|
||||||
|
|
||||||
|
public RaceStartData (long raceId, long raceStartTime, int notificationType, long timeStamp) {
|
||||||
|
this.raceId = raceId;
|
||||||
|
this.raceStartTime = raceStartTime;
|
||||||
|
this.notificationType = notificationType;
|
||||||
|
this.timeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRaceId() {
|
||||||
|
return raceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRaceStartTime() {
|
||||||
|
return raceStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNotificationType() {
|
||||||
|
return notificationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp() {
|
||||||
|
return timeStamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package seng302.model.stream.parsers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores parsed data from race status packets
|
||||||
|
*/
|
||||||
|
public class RaceStatusData {
|
||||||
|
|
||||||
|
//CONVERSION CONSTANTS
|
||||||
|
private static final double WIND_DIR_FACTOR = 0x4000 / 90; //0x4000 is 90 degrees
|
||||||
|
private static final double MS_TO_KNOTS = 1.94384;
|
||||||
|
|
||||||
|
private double windDirection;
|
||||||
|
private double windSpeed;
|
||||||
|
private boolean raceStarted = false;
|
||||||
|
private long currentTime;
|
||||||
|
private long expectedStartTime;
|
||||||
|
List<long[]> boatData = new ArrayList<>();
|
||||||
|
|
||||||
|
public RaceStatusData(
|
||||||
|
long windDir, long rawWindSpeed, int raceStatus, long currentTime, long expectedStartTime) {
|
||||||
|
|
||||||
|
windDirection = windDir / WIND_DIR_FACTOR;
|
||||||
|
windSpeed = rawWindSpeed / 1000 * MS_TO_KNOTS;
|
||||||
|
raceStarted = raceStatus == 3;
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
this.expectedStartTime = expectedStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBoatData (long boatID, long estTimeToNextMark, long estTimeToFinish, int leg) {
|
||||||
|
boatData.add(new long[] {boatID, estTimeToNextMark, estTimeToFinish, leg});
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWindDirection() {
|
||||||
|
return windDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWindSpeed() {
|
||||||
|
return windSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRaceStarted() {
|
||||||
|
return raceStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCurrentTime() {
|
||||||
|
return currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExpectedStartTime() {
|
||||||
|
return expectedStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data for boats collected form race status packets.
|
||||||
|
*
|
||||||
|
* @return A list of boat data. Boat data is in the form
|
||||||
|
* [boatID, estTimeToNextMark, estTimeToFinish, legNumber].
|
||||||
|
*/
|
||||||
|
public List<long[]> getBoatData () {
|
||||||
|
return boatData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,440 @@
|
|||||||
|
package seng302.model.stream.parsers;
|
||||||
|
|
||||||
|
import seng302.model.stream.parsers.PositionUpdateData.DeviceType;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
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.model.stream.packets.PacketType;
|
||||||
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
|
import seng302.model.stream.parsers.xml.RegattaXMLData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StreamParser is a utilities class for taking byte data, formatted according to the AC35
|
||||||
|
* streaming protocol, and parsing it into basic data types or collections.
|
||||||
|
*
|
||||||
|
* Created by kre39 on 23/04/17.
|
||||||
|
*/
|
||||||
|
public class StreamParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts and returns the seq num used in the heartbeat packet.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return the packet sequence number if the packet is of type HEARTBEAT, null otherwise.
|
||||||
|
*/
|
||||||
|
public static Long extractHeartBeat(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.HEARTBEAT)
|
||||||
|
return null;
|
||||||
|
long heartbeat = bytesToLong(packet.getPayload());
|
||||||
|
System.out.println("heartbeat = " + heartbeat);
|
||||||
|
return heartbeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getTimeZoneString(RegattaXMLData regattaXML) {
|
||||||
|
Integer offset = regattaXML.getUtcOffset();
|
||||||
|
StringBuilder utcOffset = new StringBuilder();
|
||||||
|
utcOffset.append("GMT");
|
||||||
|
if (offset > 0) {
|
||||||
|
utcOffset.append("+");
|
||||||
|
utcOffset.append(offset);
|
||||||
|
} else if (offset < 0) {
|
||||||
|
utcOffset.append("-");
|
||||||
|
utcOffset.append(offset);
|
||||||
|
}
|
||||||
|
return utcOffset.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @return null if the packet type is not RACE_STATUS, otherwise an instance of RaceStatusData
|
||||||
|
* containing the parsed packet data.
|
||||||
|
*/
|
||||||
|
public static RaceStatusData extractRaceStatus(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.RACE_STATUS)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
|
||||||
|
int raceStatus = payload[11];
|
||||||
|
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
|
||||||
|
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
|
||||||
|
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
|
||||||
|
|
||||||
|
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||||
|
// currentTime = format.format((new Date(currentTime)))
|
||||||
|
|
||||||
|
RaceStatusData data = new RaceStatusData(
|
||||||
|
windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// long timeTillStart =
|
||||||
|
// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
|
||||||
|
//
|
||||||
|
// if (timeTillStart > 0) {
|
||||||
|
// timeSinceStart = timeTillStart;
|
||||||
|
// } else {
|
||||||
|
// if (raceStatus == 4 || raceStatus == 8) {
|
||||||
|
// raceFinished = true;
|
||||||
|
// raceStarted = false;
|
||||||
|
// } else if (!raceStarted) {
|
||||||
|
// raceStarted = true;
|
||||||
|
// raceFinished = false;
|
||||||
|
// }
|
||||||
|
// timeSinceStart = timeTillStart;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
int noBoats = payload[22];
|
||||||
|
int raceType = payload[23];
|
||||||
|
for (int i = 0; i < noBoats; i++) {
|
||||||
|
long boatID = bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
||||||
|
// boat.setBoatStatus((int) payload[28 + (i * 20)]);
|
||||||
|
|
||||||
|
// setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
|
||||||
|
// boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
||||||
|
// boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
||||||
|
Long estTimeAtNextMark = bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
|
||||||
|
// boat.setEstimateTimeAtNextMark(estTimeAtNextMark);
|
||||||
|
Long estTimeAtFinish = bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
||||||
|
int leg = (int) payload[29 + (i * 20)];
|
||||||
|
// boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
||||||
|
data.addBoatData(boatID, estTimeAtNextMark, estTimeAtFinish, leg);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
||||||
|
// Integer placing = 1;
|
||||||
|
// if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
|
||||||
|
// for (Yacht boat : boats.values()) {
|
||||||
|
// if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
||||||
|
// placing += 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// updatingBoat.setPosition(placing.toString());
|
||||||
|
// updatingBoat.setLegNumber(leg);
|
||||||
|
// boatsPos.putIfAbsent(placing, updatingBoat);
|
||||||
|
// boatsPos.replace(placing, updatingBoat);
|
||||||
|
// } else if(updatingBoat.getLegNumber() == null){
|
||||||
|
// updatingBoat.setPosition("1");
|
||||||
|
// updatingBoat.setLegNumber(leg);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses and returns the text from a StreamPacket containing text data for display.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return A list containing all display message text. Is null if the packet is not of type
|
||||||
|
* DISPLAY_TEXT_MESSAGE.
|
||||||
|
*/
|
||||||
|
public static List<String> extractDisplayMessage(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE)
|
||||||
|
return null;
|
||||||
|
List<String> message = new ArrayList<>();
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
int numOfLines = payload[3];
|
||||||
|
int totalLen = 0;
|
||||||
|
for (int i = 0; i < numOfLines; i++) {
|
||||||
|
int lineNum = payload[4 + totalLen];
|
||||||
|
int textLength = payload[5 + totalLen];
|
||||||
|
byte[] messageTextBytes = Arrays
|
||||||
|
.copyOfRange(payload, 6 + totalLen, 6 + textLength + totalLen);
|
||||||
|
message.add(new String(messageTextBytes));
|
||||||
|
totalLen += 2 + textLength;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses and returns an XMLParser containing XML data sent in the given StreamPacket. XML data
|
||||||
|
* can be for races, boats or the regatta.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return XMLParse containing xmldata. Returns null if the StreamPacket is not of type
|
||||||
|
* XML_MESSAGE.
|
||||||
|
*/
|
||||||
|
public static Document extractXmlMessage(StreamPacket packet) {
|
||||||
|
if ( packet.getType() != PacketType.RACE_XML &&
|
||||||
|
packet.getType() != PacketType.REGATTA_XML &&
|
||||||
|
packet.getType() != PacketType.BOAT_XML )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageType = payload[9];
|
||||||
|
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
|
||||||
|
String xmlMessage = new String(
|
||||||
|
(Arrays.copyOfRange(payload, 14, (int) (14 + messageLength)))).trim();
|
||||||
|
|
||||||
|
//Create XML document Object
|
||||||
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder db;
|
||||||
|
Document doc = null;
|
||||||
|
try {
|
||||||
|
db = dbf.newDocumentBuilder();
|
||||||
|
doc = db.parse(new InputSource(new StringReader(xmlMessage)));
|
||||||
|
} catch (ParserConfigurationException | IOException | SAXException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the race start status from the packet and returns it as a long array.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if
|
||||||
|
* the packet type is not of RACE_START_STATUS.
|
||||||
|
*/
|
||||||
|
public static RaceStartData extractRaceStartStatus(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.RACE_START_STATUS) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long raceStartTime = bytesToLong(Arrays.copyOfRange(payload, 9, 15));
|
||||||
|
long raceId = bytesToLong(Arrays.copyOfRange(payload, 15, 19));
|
||||||
|
int notificationType = payload[19];
|
||||||
|
return new RaceStartData(raceId, raceStartTime, notificationType, timeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the the byte array in a StreamPacket for yacht events to retrieve the necessary info
|
||||||
|
* and returns it a an array of longs.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return the event data in the form [boatID, incidentID, eventID, timeStamp]. Returns null if
|
||||||
|
* the packet is not of type YACHT_EVENT_CODE.
|
||||||
|
*/
|
||||||
|
public static long[] extractYachtEventCode(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.YACHT_EVENT_CODE)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
||||||
|
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
||||||
|
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21));
|
||||||
|
int eventId = payload[21];
|
||||||
|
return new long[] {subjectId, incidentId, eventId, timeStamp};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses data from a StreamPacket for yacht actions and returns it in a long array.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return long array of packet data in the form [subjectID, incidentID, eventID, timeStamp].
|
||||||
|
* Returns null if the packet is not of type YACHT_ACTION_CODE.
|
||||||
|
*/
|
||||||
|
public static long[] extractYachtActionCode(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.YACHT_ACTION_CODE)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
||||||
|
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
||||||
|
int eventId = payload[17];
|
||||||
|
return new long[] {subjectId, incidentId, eventId, timeStamp};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips the message from the chatter text type packets.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return Chatter text message as a string. Returns null if the packet is not of type
|
||||||
|
* CHATTER_TEXT.
|
||||||
|
*/
|
||||||
|
public static String extractChatterText(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.CHATTER_TEXT)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
int messageType = payload[1];
|
||||||
|
int length = payload[2];
|
||||||
|
return new String(Arrays.copyOfRange(payload, 3, 3 + length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes the data from a bot location stream packet and parses the id, timeValid, lat, lon,
|
||||||
|
* heading and groundspeed into a BoatPositionPacket which is returned.
|
||||||
|
*
|
||||||
|
* @param packet Packet parsed in to use the payload
|
||||||
|
* @return BoatPositionPacket containing important boat information. Returns null if the packet
|
||||||
|
* is not of type BOAT_LOCATION.
|
||||||
|
*/
|
||||||
|
public static PositionUpdateData extractBoatLocation(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.BOAT_LOCATION)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int deviceType = (int) payload[15];
|
||||||
|
long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long seq = bytesToLong(Arrays.copyOfRange(payload, 11, 15));
|
||||||
|
long boatId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
|
||||||
|
long rawLat = bytesToLong(Arrays.copyOfRange(payload, 16, 20));
|
||||||
|
long rawLon = bytesToLong(Arrays.copyOfRange(payload, 20, 24));
|
||||||
|
//Converts the double to a usable lat/lon
|
||||||
|
double lat = ((180d * (double) rawLat) / Math.pow(2, 31));
|
||||||
|
double lon = ((180d * (double) rawLon) / Math.pow(2, 31));
|
||||||
|
double heading = bytesToLong(Arrays.copyOfRange(payload, 28, 30));
|
||||||
|
heading = 360.0 / 0xffff * heading; //Convert to degrees.
|
||||||
|
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
|
||||||
|
|
||||||
|
DeviceType type;
|
||||||
|
if (deviceType == 1)
|
||||||
|
type = DeviceType.YACHT_TYPE;
|
||||||
|
else
|
||||||
|
type = DeviceType.MARK_TYPE;
|
||||||
|
|
||||||
|
return new PositionUpdateData((int) boatId, type, lat, lon, heading, groundSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a stream packet for a mark rounding and returns the boatID, markID and timestamp.
|
||||||
|
*
|
||||||
|
* @param packet The packet containing the payload
|
||||||
|
* @return an array containing longs. The values are [boatID, markID, timeStamp]. Returns null
|
||||||
|
* if packet is not of type MARK_ROUNDING.
|
||||||
|
*/
|
||||||
|
public static MarkRoundingData extractMarkRounding(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.MARK_ROUNDING)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
|
||||||
|
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
|
||||||
|
int boatStatus = payload[17];
|
||||||
|
int roundingSide = payload[18];
|
||||||
|
int markType = payload[19];
|
||||||
|
int markId = payload[20];
|
||||||
|
|
||||||
|
return new MarkRoundingData((int) subjectId, markId, roundingSide, timeStamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list containing the string value of data within the given stream packet for
|
||||||
|
* course wind.
|
||||||
|
*
|
||||||
|
* @param packet The packet containing the payload
|
||||||
|
* @return the string values of the wind packet. Returns null if the packet is not of type
|
||||||
|
* COURSE_WIND.
|
||||||
|
*/
|
||||||
|
public static List<String> extractCourseWind(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.COURSE_WIND)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
int selectedWindId = payload[1];
|
||||||
|
int loopCount = payload[2];
|
||||||
|
List<String> windInfo = new ArrayList<>();
|
||||||
|
for (int i = 0; i < loopCount; i++) {
|
||||||
|
String wind = "WindId: " + payload[3 + (20 * i)];
|
||||||
|
wind +=
|
||||||
|
"\nTime: " + bytesToLong(Arrays.copyOfRange(payload, 4 + (20 * i), 10 + (20 * i)));
|
||||||
|
wind += "\nRaceId: " + bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 10 + (20 * i), 14 + (20 * i)));
|
||||||
|
wind += "\nWindDirection: " + bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 14 + (20 * i), 16 + (20 * i)));
|
||||||
|
wind += "\nWindSpeed: " + bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 16 + (20 * i), 18 + (20 * i)));
|
||||||
|
wind += "\nBestUpWindAngle: " + bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 18 + (20 * i), 20 + (20 * i)));
|
||||||
|
wind += "\nBestDownWindAngle: " + bytesToLong(
|
||||||
|
Arrays.copyOfRange(payload, 20 + (20 * i), 22 + (20 * i)));
|
||||||
|
wind += "\nFlags: " + String
|
||||||
|
.format("%8s", Integer.toBinaryString(payload[22 + (20 * i)] & 0xFF))
|
||||||
|
.replace(' ', '0');
|
||||||
|
windInfo.add(wind);
|
||||||
|
}
|
||||||
|
return windInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parsed data from a StreamPacket for average wind data.
|
||||||
|
*
|
||||||
|
* @param packet The packet containing the payload
|
||||||
|
* @return The wind data in the form
|
||||||
|
* [rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timestamp]
|
||||||
|
* or null if the packet is not of type AVG_WIND.
|
||||||
|
*/
|
||||||
|
public static long[] extractAvgWind(StreamPacket packet) {
|
||||||
|
if (packet.getType() != PacketType.AVG_WIND)
|
||||||
|
return null;
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
|
||||||
|
long rawPeriod = bytesToLong(Arrays.copyOfRange(payload, 7, 9));
|
||||||
|
long rawSamplePeriod = bytesToLong(Arrays.copyOfRange(payload, 9, 11));
|
||||||
|
long period2 = bytesToLong(Arrays.copyOfRange(payload, 11, 13));
|
||||||
|
long speed2 = bytesToLong(Arrays.copyOfRange(payload, 13, 15));
|
||||||
|
long period3 = bytesToLong(Arrays.copyOfRange(payload, 15, 17));
|
||||||
|
long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19));
|
||||||
|
long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21));
|
||||||
|
long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23));
|
||||||
|
return new long[] {
|
||||||
|
rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timeStamp
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void extractBoatAction(StreamPacket packet) {
|
||||||
|
byte[] payload = packet.getPayload();
|
||||||
|
int messageVersionNo = payload[0];
|
||||||
|
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
|
||||||
|
if (actionType == 1) {
|
||||||
|
System.out.println("VMG");
|
||||||
|
} else if (actionType == 2) {
|
||||||
|
System.out.println("SAILS IN");
|
||||||
|
} else if (actionType == 3) {
|
||||||
|
System.out.println("SAILS OUT");
|
||||||
|
} else if (actionType == 4) {
|
||||||
|
System.out.println("TACK/GYBE");
|
||||||
|
} else if (actionType == 5) {
|
||||||
|
System.out.println("UPWIND");
|
||||||
|
} else if (actionType == 6) {
|
||||||
|
System.out.println("DOWNWIND");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* takes an array of up to 7 bytes and returns a positive
|
||||||
|
* long constructed from the input bytes
|
||||||
|
*
|
||||||
|
* @return a positive long if there is less than 7 bytes -1 otherwise
|
||||||
|
*/
|
||||||
|
public static long bytesToLong(byte[] bytes) {
|
||||||
|
long partialLong = 0;
|
||||||
|
int index = 0;
|
||||||
|
for (byte b : bytes) {
|
||||||
|
if (index > 6) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
partialLong = partialLong | (b & 0xFFL) << (index * 8);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return partialLong;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package seng302.model.stream.parsers.xml;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import seng302.model.Corner;
|
||||||
|
import seng302.model.Limit;
|
||||||
|
import seng302.model.mark.Mark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a Document object containing race data in XML format and stores the data.
|
||||||
|
*/
|
||||||
|
public class RaceXMLData {
|
||||||
|
|
||||||
|
private List<Integer> participants;
|
||||||
|
private Map<Integer, Mark> compoundMarks;
|
||||||
|
private List<Corner> markSequence;
|
||||||
|
private List<Limit> courseLimit;
|
||||||
|
private Map<Integer, Mark> individualMarks;
|
||||||
|
|
||||||
|
RaceXMLData(List<Integer> participants, List<Mark> compoundMarks, List<Corner> markSequence,
|
||||||
|
List<Limit> courseLimit) {
|
||||||
|
this.participants = participants;
|
||||||
|
this.markSequence = markSequence;
|
||||||
|
this.courseLimit = courseLimit;
|
||||||
|
this.compoundMarks = new HashMap<>();
|
||||||
|
for (Mark mark : compoundMarks)
|
||||||
|
this.compoundMarks.put(mark.getId(), mark);
|
||||||
|
for (Mark mark : compoundMarks) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getParticipants() {
|
||||||
|
return participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Mark> getCompoundMarks() {
|
||||||
|
return compoundMarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Corner> getMarkSequence() {
|
||||||
|
return markSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Limit> getCourseLimit() {
|
||||||
|
return courseLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package seng302.model.stream.parsers.xml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores data from regatta xml packet.
|
||||||
|
*/
|
||||||
|
public class RegattaXMLData {
|
||||||
|
//Regatta Info
|
||||||
|
private Integer regattaID;
|
||||||
|
private String regattaName;
|
||||||
|
private String courseName;
|
||||||
|
private Double centralLat;
|
||||||
|
private Double centralLng;
|
||||||
|
private Integer utcOffset;
|
||||||
|
|
||||||
|
RegattaXMLData (Integer regattaID, String regattaName, String courseName,
|
||||||
|
Double centralLat, Double centralLng, Integer utcOffset) {
|
||||||
|
this.regattaID = regattaID;
|
||||||
|
this.regattaName = regattaName;
|
||||||
|
this.courseName = courseName;
|
||||||
|
this.centralLat = centralLat;
|
||||||
|
this.centralLng = centralLng;
|
||||||
|
this.utcOffset = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
package seng302.model.stream.parsers.xml;
|
||||||
|
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.model.Boat;
|
||||||
|
import seng302.model.Corner;
|
||||||
|
import seng302.model.Limit;
|
||||||
|
import seng302.model.mark.GateMark;
|
||||||
|
import seng302.model.mark.Mark;
|
||||||
|
import seng302.model.mark.MarkType;
|
||||||
|
import seng302.model.mark.SingleMark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for parsing XML documents
|
||||||
|
*/
|
||||||
|
public class XMLParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static Integer getElementInt(Element ele, String tag) {
|
||||||
|
NodeList tagList = ele.getElementsByTagName(tag);
|
||||||
|
if (tagList.getLength() > 0) {
|
||||||
|
return Integer.parseInt(tagList.item(0).getTextContent());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static String getElementString(Element ele, String tag) {
|
||||||
|
NodeList tagList = ele.getElementsByTagName(tag);
|
||||||
|
if (tagList.getLength() > 0) {
|
||||||
|
return tagList.item(0).getTextContent();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static Double getElementDouble(Element ele, String tag) {
|
||||||
|
NodeList tagList = ele.getElementsByTagName(tag);
|
||||||
|
if (tagList.getLength() > 0) {
|
||||||
|
return Double.parseDouble(tagList.item(0).getTextContent());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static String getNodeAttributeString(Node n, String attr) {
|
||||||
|
Node attrItem = n.getAttributes().getNamedItem(attr);
|
||||||
|
if (attrItem != null) {
|
||||||
|
return attrItem.getTextContent();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static Integer getNodeAttributeInt(Node n, String attr) {
|
||||||
|
Node attrItem = n.getAttributes().getNamedItem(attr);
|
||||||
|
if (attrItem != null) {
|
||||||
|
return Integer.parseInt(attrItem.getTextContent());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private static Double getNodeAttributeDouble(Node n, String attr) {
|
||||||
|
Node attrItem = n.getAttributes().getNamedItem(attr);
|
||||||
|
if (attrItem != null) {
|
||||||
|
return Double.parseDouble(attrItem.getTextContent());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a mapping of boat sourceIDS to boat objects created from the given xml document.
|
||||||
|
* @param doc XML Document Object
|
||||||
|
* @return Mapping of sourceIds to Boats.
|
||||||
|
*/
|
||||||
|
public static Map<Integer, Boat> parseBoats(Document doc){
|
||||||
|
Map<Integer, Boat> competingBoats = new HashMap<>();
|
||||||
|
|
||||||
|
Element docEle = doc.getDocumentElement();
|
||||||
|
|
||||||
|
NodeList boatsList = docEle.getElementsByTagName("Boats").item(0).getChildNodes();
|
||||||
|
for (int i = 0; i < boatsList.getLength(); i++) {
|
||||||
|
Node currentBoat = boatsList.item(i);
|
||||||
|
if (currentBoat.getNodeName().equals("Boat")) {
|
||||||
|
// Boat boat = new Boat(currentBoat);
|
||||||
|
Boat boat = new Boat(XMLParser.getNodeAttributeString(currentBoat, "Type"),
|
||||||
|
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
|
||||||
|
XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
|
||||||
|
XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
|
||||||
|
XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
|
||||||
|
XMLParser.getNodeAttributeString(currentBoat, "Country"));
|
||||||
|
if (boat.getBoatType().equals("Yacht")) {
|
||||||
|
competingBoats.put(boat.getSourceID(), boat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return competingBoats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object containing the data extracted from the given xml formatted document
|
||||||
|
*
|
||||||
|
* @param doc XML Document Object
|
||||||
|
* @return Object containing regatta data
|
||||||
|
*/
|
||||||
|
public static RegattaXMLData parseRegatta(Document doc) {
|
||||||
|
|
||||||
|
Element docEle = doc.getDocumentElement();
|
||||||
|
Integer regattaID = XMLParser.getElementInt(docEle, "RegattaID");
|
||||||
|
String regattaName = XMLParser.getElementString(docEle, "RegattaName");
|
||||||
|
String courseName = XMLParser.getElementString(docEle, "CourseName");
|
||||||
|
Double centralLat = XMLParser.getElementDouble(docEle, "CentralLatitude");
|
||||||
|
Double centralLng = XMLParser.getElementDouble(docEle, "CentralLongitude");
|
||||||
|
Integer utcOffset = XMLParser.getElementInt(docEle, "UtcOffset");
|
||||||
|
return new RegattaXMLData(
|
||||||
|
regattaID, regattaName, courseName, centralLat, centralLng, utcOffset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object containing the data extracted from the given xml formatted document
|
||||||
|
*
|
||||||
|
* @param doc XML document
|
||||||
|
* @return object containing race data
|
||||||
|
*/
|
||||||
|
public static RaceXMLData parseRace(Document doc) {
|
||||||
|
Element docEle = doc.getDocumentElement();
|
||||||
|
return new RaceXMLData(
|
||||||
|
extractParticpantIDs(docEle),
|
||||||
|
extractCompoundMarks(docEle),
|
||||||
|
extractMarkOrder(docEle),
|
||||||
|
extractCourseLimit(docEle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts course limit data
|
||||||
|
*/
|
||||||
|
private static List<Limit> extractCourseLimit(Element docEle) {
|
||||||
|
List<Limit> courseLimit = new ArrayList<>();
|
||||||
|
NodeList limitList = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes();
|
||||||
|
for (int i = 0; i < limitList.getLength(); i++) {
|
||||||
|
Node limitNode = limitList.item(i);
|
||||||
|
if (limitNode.getNodeName().equals("Limit")) {
|
||||||
|
courseLimit.add(
|
||||||
|
new Limit(
|
||||||
|
XMLParser.getNodeAttributeInt(limitNode, "SeqID"),
|
||||||
|
XMLParser.getNodeAttributeDouble(limitNode, "Lat"),
|
||||||
|
XMLParser.getNodeAttributeDouble(limitNode, "lon")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return courseLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts course order data
|
||||||
|
*/
|
||||||
|
private static List<Corner> extractMarkOrder (Element docEle) {
|
||||||
|
List<Corner> compoundMarkSequence = new ArrayList<>();
|
||||||
|
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")) {
|
||||||
|
compoundMarkSequence.add(
|
||||||
|
new Corner(
|
||||||
|
XMLParser.getNodeAttributeInt(cornerNode, "SeqID"),
|
||||||
|
XMLParser.getNodeAttributeInt(cornerNode, "CompoundMarkID"),
|
||||||
|
XMLParser.getNodeAttributeString(cornerNode, "Rounding"),
|
||||||
|
XMLParser.getNodeAttributeInt(cornerNode, "ZoneSize")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compoundMarkSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts course participants data
|
||||||
|
*/
|
||||||
|
private static List<Integer> extractParticpantIDs (Element docEle) {
|
||||||
|
List<Integer> boatIDs = new ArrayList<>();
|
||||||
|
NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes();
|
||||||
|
for (int i = 0; i < pList.getLength(); i++) {
|
||||||
|
Node pNode = pList.item(i);
|
||||||
|
if (pNode.getNodeName().equals("Yacht")) {
|
||||||
|
boatIDs.add(XMLParser.getNodeAttributeInt(pNode, "SourceID"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boatIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts course mark data
|
||||||
|
*/
|
||||||
|
private static List<Mark> extractCompoundMarks(Element docEle) {
|
||||||
|
List<Mark> allMarks = 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")) {
|
||||||
|
allMarks.add(createMark(cMarkNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allMarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates marks objects from the given node
|
||||||
|
*/
|
||||||
|
private static Mark createMark(Node compoundMark) {
|
||||||
|
List<SingleMark> subMarks = new ArrayList<>();
|
||||||
|
Integer compoundMarkID = XMLParser.getNodeAttributeInt(compoundMark, "CompoundMarkID");
|
||||||
|
String cMarkName = XMLParser.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 = XMLParser.getNodeAttributeInt(markNode, "SourceID");
|
||||||
|
String markName = XMLParser.getNodeAttributeString(markNode, "Name");
|
||||||
|
Double targetLat = XMLParser.getNodeAttributeDouble(markNode, "TargetLat");
|
||||||
|
Double targetLng = XMLParser.getNodeAttributeDouble(markNode, "TargetLng");
|
||||||
|
SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID,
|
||||||
|
compoundMarkID);
|
||||||
|
subMarks.add(mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subMarks.size() == 1) {
|
||||||
|
return subMarks.get(0);
|
||||||
|
} else {
|
||||||
|
return new GateMark( cMarkName, MarkType.OPEN_GATE, subMarks.get(0), subMarks.get(1),
|
||||||
|
subMarks.get(0).getLatitude(), subMarks.get(0).getLongitude(), compoundMarkID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package seng302.visualiser;
|
||||||
|
|
||||||
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by cir27 on 21/07/17.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ClientSocketListener {
|
||||||
|
void newPacket(StreamPacket packet);
|
||||||
|
}
|
||||||
@@ -8,18 +8,10 @@ import java.net.Socket;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.Checksum;
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ListChangeListener;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import seng302.model.stream.StreamParser;
|
|
||||||
import seng302.model.stream.XMLParser;
|
|
||||||
import seng302.model.stream.packets.BoatPositionPacket;
|
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.server.messages.BoatActionMessage;
|
import seng302.server.messages.BoatActionMessage;
|
||||||
import seng302.server.messages.Message;
|
import seng302.server.messages.Message;
|
||||||
@@ -29,7 +21,7 @@ import seng302.server.messages.Message;
|
|||||||
*/
|
*/
|
||||||
public class ClientToServerThread extends Thread {
|
public class ClientToServerThread extends Thread {
|
||||||
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
|
private Queue<StreamPacket> streamPackets = new ConcurrentLinkedQueue<>();
|
||||||
private List<ListChangeListener<Queue<StreamPacket>>> boatPacketListeners = new ArrayList<>();
|
private List<ClientSocketListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private InputStream is;
|
private InputStream is;
|
||||||
@@ -78,10 +70,9 @@ public class ClientToServerThread extends Thread {
|
|||||||
long computedCrc = checksum.getValue();
|
long computedCrc = checksum.getValue();
|
||||||
long packetCrc = Message.bytesToLong(getBytes(4));
|
long packetCrc = Message.bytesToLong(getBytes(4));
|
||||||
if (computedCrc == packetCrc) {
|
if (computedCrc == packetCrc) {
|
||||||
streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
// streamPackets.add(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||||
for (ListChangeListener cl : boatPacketListeners) {
|
for (ClientSocketListener csl : listeners)
|
||||||
cl.onChanged();
|
csl.newPacket(new StreamPacket(type, payloadLength, timeStamp, payload));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Packet has been dropped");
|
System.err.println("Packet has been dropped");
|
||||||
}
|
}
|
||||||
@@ -114,12 +105,12 @@ public class ClientToServerThread extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStreamObserver (ListChangeListener<Queue<StreamPacket>> listChangeListener) {
|
public void addStreamObserver (ClientSocketListener streamListener) {
|
||||||
boatPacketListeners.add(listChangeListener);
|
listeners.add(streamListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeStreamObserver (ListChangeListener<Queue<StreamPacket>> listChangeListener) {
|
public void removeStreamObserver (ClientSocketListener streamListener) {
|
||||||
boatPacketListeners.remove(listChangeListener);
|
listeners.remove(streamListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readByte() throws Exception {
|
private int readByte() throws Exception {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
@@ -19,22 +20,22 @@ import javafx.scene.layout.Pane;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import seng302.visualiser.controllers.RaceViewController;
|
import seng302.model.Limit;
|
||||||
import seng302.visualiser.fxObjects.BoatGroup;
|
import seng302.visualiser.fxObjects.BoatGroup;
|
||||||
import seng302.visualiser.fxObjects.MarkGroup;
|
import seng302.visualiser.fxObjects.MarkGroup;
|
||||||
import seng302.model.Colors;
|
import seng302.model.Colors;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Boat;
|
||||||
import seng302.model.map.Boundary;
|
import seng302.model.map.Boundary;
|
||||||
import seng302.model.map.CanvasMap;
|
import seng302.model.map.CanvasMap;
|
||||||
import seng302.model.mark.GateMark;
|
import seng302.model.mark.GateMark;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.MarkType;
|
import seng302.model.mark.MarkType;
|
||||||
import seng302.model.mark.SingleMark;
|
import seng302.model.mark.SingleMark;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.XMLParser;
|
import seng302.model.stream.parsers.xml.XMLParser;
|
||||||
import seng302.model.stream.XMLParser.RaceXMLObject.Limit;
|
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Limit;
|
||||||
import seng302.model.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Participant;
|
||||||
import seng302.model.stream.packets.BoatPositionPacket;
|
import seng302.model.stream.parsers.PositionUpdateData;
|
||||||
import seng302.utilities.GeoPoint;
|
import seng302.utilities.GeoPoint;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ import seng302.utilities.GeoUtility;
|
|||||||
* Created by cir27 on 20/07/17.
|
* Created by cir27 on 20/07/17.
|
||||||
*/
|
*/
|
||||||
public class GameView extends Pane {
|
public class GameView extends Pane {
|
||||||
private RaceViewController raceViewController;
|
|
||||||
private ObservableList<Node> gameObjects;
|
private ObservableList<Node> gameObjects;
|
||||||
private ImageView mapImage;
|
private ImageView mapImage;
|
||||||
|
|
||||||
@@ -66,7 +67,9 @@ public class GameView extends Pane {
|
|||||||
|
|
||||||
private List<MarkGroup> markGroups = new ArrayList<>();
|
private List<MarkGroup> markGroups = new ArrayList<>();
|
||||||
private List<BoatGroup> boatGroups = new ArrayList<>();
|
private List<BoatGroup> boatGroups = new ArrayList<>();
|
||||||
private Text FPSdisplay = new Text();
|
|
||||||
|
private Text fpsDisplay = new Text();
|
||||||
|
|
||||||
private Polygon raceBorder = new Polygon();
|
private Polygon raceBorder = new Polygon();
|
||||||
|
|
||||||
//FRAME RATE
|
//FRAME RATE
|
||||||
@@ -75,44 +78,24 @@ public class GameView extends Pane {
|
|||||||
private int frameTimeIndex = 0;
|
private int frameTimeIndex = 0;
|
||||||
private boolean arrayFilled = false;
|
private boolean arrayFilled = false;
|
||||||
|
|
||||||
AnimationTimer timer;
|
private AnimationTimer timer;
|
||||||
|
|
||||||
private enum ScaleDirection {
|
private enum ScaleDirection {
|
||||||
HORIZONTAL,
|
HORIZONTAL,
|
||||||
VERTICAL
|
VERTICAL
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup(RaceViewController raceViewController) {
|
public GameView () {
|
||||||
this.raceViewController = raceViewController;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize() {
|
|
||||||
raceViewController = new RaceViewController();
|
|
||||||
gameObjects = this.getChildren();
|
gameObjects = this.getChildren();
|
||||||
// create image view for map, bind panel size to image
|
// create image view for map, bind panel size to image
|
||||||
mapImage = new ImageView();
|
mapImage = new ImageView();
|
||||||
gameObjects.add(mapImage);
|
gameObjects.add(mapImage);
|
||||||
mapImage.fitWidthProperty().bind(this.widthProperty());
|
mapImage.fitWidthProperty().bind(this.widthProperty());
|
||||||
mapImage.fitHeightProperty().bind(this.heightProperty());
|
mapImage.fitHeightProperty().bind(this.heightProperty());
|
||||||
|
initializeTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeCanvas() {
|
private void initializeTimer () {
|
||||||
|
|
||||||
fitMarksToCanvas();
|
|
||||||
drawGoogleMap();
|
|
||||||
FPSdisplay.setLayoutX(5);
|
|
||||||
FPSdisplay.setLayoutY(20);
|
|
||||||
FPSdisplay.setStrokeWidth(2);
|
|
||||||
gameObjects.add(FPSdisplay);
|
|
||||||
gameObjects.add(raceBorder);
|
|
||||||
initializeMarks();
|
|
||||||
initializeBoats();
|
|
||||||
this.widthProperty().addListener(resize -> {
|
|
||||||
canvasWidth = this.getWidth();
|
|
||||||
canvasHeight = this.getHeight();
|
|
||||||
fitMarksToCanvas();
|
|
||||||
});
|
|
||||||
|
|
||||||
timer = new AnimationTimer() {
|
timer = new AnimationTimer() {
|
||||||
private long lastTime = 0;
|
private long lastTime = 0;
|
||||||
private int FPSCount = 30;
|
private int FPSCount = 30;
|
||||||
@@ -136,24 +119,35 @@ public class GameView extends Pane {
|
|||||||
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
|
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
|
||||||
if (FPSCount-- == 0) {
|
if (FPSCount-- == 0) {
|
||||||
FPSCount = 30;
|
FPSCount = 30;
|
||||||
// drawFps(frameRate.intValue());
|
drawFps(frameRate.intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateGroups();
|
updateGroups();
|
||||||
if (StreamParser.isRaceFinished()) {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
lastTime = now;
|
lastTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (StreamParser.isRaceFinished()) {
|
|
||||||
this.stop();
|
|
||||||
switchToFinishScreen();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initializeCanvas() {
|
||||||
|
|
||||||
|
fitMarksToCanvas();
|
||||||
|
drawGoogleMap();
|
||||||
|
fpsDisplay.setLayoutX(5);
|
||||||
|
fpsDisplay.setLayoutY(20);
|
||||||
|
fpsDisplay.setStrokeWidth(2);
|
||||||
|
gameObjects.add(fpsDisplay);
|
||||||
|
gameObjects.add(raceBorder);
|
||||||
|
initializeMarks();
|
||||||
|
initializeBoats();
|
||||||
|
this.widthProperty().addListener(resize -> {
|
||||||
|
canvasWidth = this.getWidth();
|
||||||
|
canvasHeight = this.getHeight();
|
||||||
|
fitMarksToCanvas();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void switchToFinishScreen() {
|
private void switchToFinishScreen() {
|
||||||
try {
|
try {
|
||||||
// canvas view -> anchor pane -> grid pane -> main view
|
// canvas view -> anchor pane -> grid pane -> main view
|
||||||
@@ -255,28 +249,28 @@ public class GameView extends Pane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateBoatGroup(BoatGroup boatGroup) {
|
private void updateBoatGroup(BoatGroup boatGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
// PriorityBlockingQueue<PositionUpdateData> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
||||||
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
// // giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
||||||
if (movementQueue.size() > 0) {
|
// if (movementQueue.size() > 0) {
|
||||||
try {
|
// try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
// PositionUpdateData positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
double heading = 360.0 / 0xffff * positionPacket.getHeading();
|
// double heading = 360.0 / 0xffff * positionPacket.getHeading();
|
||||||
boatGroup.setDestination(
|
boatGroup.setDestination(
|
||||||
p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(),
|
p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(),
|
||||||
positionPacket.getTimeValid(), frameRate);
|
positionPacket.getTimeValid(), frameRate);
|
||||||
} catch (InterruptedException e){
|
// } catch (InterruptedException e){
|
||||||
e.printStackTrace();
|
// e.printStackTrace();
|
||||||
}
|
// }
|
||||||
|
//// }
|
||||||
// }
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
private void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.markLocations.get(raceId);
|
PriorityBlockingQueue<PositionUpdateData> movementQueue = StreamParser.markLocations.get(raceId);
|
||||||
if (movementQueue.size() > 0){
|
if (movementQueue.size() > 0){
|
||||||
try {
|
try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
PositionUpdateData positionPacket = movementQueue.take();
|
||||||
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
|
||||||
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -289,7 +283,7 @@ public class GameView extends Pane {
|
|||||||
* Draws all the boats.
|
* Draws all the boats.
|
||||||
*/
|
*/
|
||||||
private void initializeBoats() {
|
private void initializeBoats() {
|
||||||
Map<Integer, Yacht> boats = StreamParser.getBoats();
|
Map<Integer, Boat> boats = StreamParser.getBoats();
|
||||||
Group wakes = new Group();
|
Group wakes = new Group();
|
||||||
Group trails = new Group();
|
Group trails = new Group();
|
||||||
Group annotations = new Group();
|
Group annotations = new Group();
|
||||||
@@ -301,7 +295,7 @@ public class GameView extends Pane {
|
|||||||
participantIDs.add(p.getsourceID());
|
participantIDs.add(p.getsourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Yacht boat : boats.values()) {
|
for (Boat boat : boats.values()) {
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
if (participantIDs.contains(boat.getSourceID())) {
|
||||||
boat.setColour(Colors.getColor());
|
boat.setColour(Colors.getColor());
|
||||||
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
|
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
|
||||||
@@ -336,14 +330,9 @@ public class GameView extends Pane {
|
|||||||
gameObjects.addAll(markGroups);
|
gameObjects.addAll(markGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void drawFps(int fps){
|
private void drawFps(int fps){
|
||||||
// if (raceViewController.isDisplayFps()){
|
fpsDisplay.setText(String.format("%d FPS", fps));
|
||||||
// FPSdisplay.setVisible(true);
|
}
|
||||||
// FPSdisplay.setText(String.format("%d FPS", fps));
|
|
||||||
// } else {
|
|
||||||
// FPSdisplay.setVisible(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates x and y location for every marker that fits it to the canvas the race will be
|
* Calculates x and y location for every marker that fits it to the canvas the race will be
|
||||||
@@ -367,7 +356,7 @@ public class GameView extends Pane {
|
|||||||
*/
|
*/
|
||||||
private void findMinMaxPoint() {
|
private void findMinMaxPoint() {
|
||||||
List<Limit> sortedPoints = new ArrayList<>();
|
List<Limit> sortedPoints = new ArrayList<>();
|
||||||
for (Limit limit : StreamParser.getXmlObject().getRaceXML().getCourseLimit()) {
|
for (Limit limit : raceData) {
|
||||||
sortedPoints.add(limit);
|
sortedPoints.add(limit);
|
||||||
}
|
}
|
||||||
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
||||||
@@ -512,4 +501,34 @@ public class GameView extends Pane {
|
|||||||
metersPerPixelY = dVertical / dy;
|
metersPerPixelY = dVertical / dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAnnotationVisibilities (boolean teamName, boolean velocity, boolean estTime,
|
||||||
|
boolean legTime, boolean trail, boolean wake) {
|
||||||
|
for (BoatGroup boatGroup : boatGroups) {
|
||||||
|
boatGroup.setVisibility(teamName, velocity, estTime, legTime, trail, wake);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFPSVisibility (boolean visibility) {
|
||||||
|
fpsDisplay.setVisible(visibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyBooleanProperty getFPSVisibilityProperty () {
|
||||||
|
return fpsDisplay.visibleProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectBoat (int boatId) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pauseRace () {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startRace () {
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBorder (List<Limit> courseLimit) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,24 +15,24 @@ import javafx.scene.control.cell.PropertyValueFactory;
|
|||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Boat;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Participant;
|
||||||
|
|
||||||
public class FinishScreenViewController implements Initializable {
|
public class FinishScreenViewController implements Initializable {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private GridPane finishScreenGridPane;
|
private GridPane finishScreenGridPane;
|
||||||
@FXML
|
@FXML
|
||||||
private TableView<Yacht> finishOrderTable;
|
private TableView<Boat> finishOrderTable;
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Yacht, String> posCol;
|
private TableColumn<Boat, String> posCol;
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Yacht, String> boatNameCol;
|
private TableColumn<Boat, String> boatNameCol;
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Yacht, String> shortNameCol;
|
private TableColumn<Boat, String> shortNameCol;
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Yacht, String> countryCol;
|
private TableColumn<Boat, String> countryCol;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
@@ -41,7 +41,7 @@ public class FinishScreenViewController implements Initializable {
|
|||||||
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
|
||||||
// set up data for table
|
// set up data for table
|
||||||
ObservableList<Yacht> data = FXCollections.observableArrayList();
|
ObservableList<Boat> data = FXCollections.observableArrayList();
|
||||||
finishOrderTable.setItems(data);
|
finishOrderTable.setItems(data);
|
||||||
|
|
||||||
// setting table col data
|
// setting table col data
|
||||||
@@ -67,7 +67,7 @@ public class FinishScreenViewController implements Initializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add data to table
|
// add data to table
|
||||||
for (Yacht boat : StreamParser.getBoatsPos().values()) {
|
for (Boat boat : StreamParser.getBoatsPos().values()) {
|
||||||
if (participantIDs.contains(boat.getSourceID())) {
|
if (participantIDs.contains(boat.getSourceID())) {
|
||||||
data.add(boat);
|
data.add(boat);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ import javafx.stage.Stage;
|
|||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
import seng302.model.Corner;
|
||||||
|
import seng302.model.stream.parsers.xml.RaceXMLData;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
|
import seng302.visualiser.GameView;
|
||||||
import seng302.visualiser.controllers.annotations.Annotation;
|
import seng302.visualiser.controllers.annotations.Annotation;
|
||||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
|
import seng302.visualiser.controllers.annotations.ImportantAnnotationController;
|
||||||
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
|
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
|
||||||
@@ -38,13 +41,11 @@ import seng302.model.*;
|
|||||||
import seng302.model.mark.GateMark;
|
import seng302.model.mark.GateMark;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.SingleMark;
|
import seng302.model.mark.SingleMark;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
import seng302.model.stream.XMLParser;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import seng302.model.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.model.stream.parsers.xml.XMLParser.RaceXMLObject.Participant;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by ptg19 on 29/03/17.
|
* Created by ptg19 on 29/03/17.
|
||||||
@@ -70,21 +71,21 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
@FXML
|
@FXML
|
||||||
private Button selectAnnotationBtn;
|
private Button selectAnnotationBtn;
|
||||||
@FXML
|
@FXML
|
||||||
private ComboBox boatSelectionComboBox;
|
private ComboBox<Boat> boatSelectionComboBox;
|
||||||
@FXML
|
|
||||||
private GameViewController gameViewController;
|
//Race Data
|
||||||
|
private Map<Integer, Boat> participants;
|
||||||
|
private Map<Integer, Mark> course;
|
||||||
|
private RaceXMLData raceData;
|
||||||
|
private GameView gameView;
|
||||||
|
|
||||||
private static ArrayList<Yacht> startingBoats = new ArrayList<>();
|
|
||||||
private boolean displayFps;
|
|
||||||
private Timeline timerTimeline;
|
private Timeline timerTimeline;
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
private static HashMap<Integer, Series<String, Double>> sparkLineData = new HashMap<>();
|
private HashMap<Integer, Series<String, Double>> sparkLineData = new HashMap<>();
|
||||||
private static ArrayList<Yacht> racingBoats = new ArrayList<>();
|
|
||||||
private ImportantAnnotationsState importantAnnotations;
|
private ImportantAnnotationsState importantAnnotations;
|
||||||
private Yacht selectedBoat;
|
private Boat selectedBoat;
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
|
||||||
// Load a default important annotation state
|
// Load a default important annotation state
|
||||||
importantAnnotations = new ImportantAnnotationsState();
|
importantAnnotations = new ImportantAnnotationsState();
|
||||||
|
|
||||||
@@ -94,19 +95,22 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
raceSparkLine.getYAxis().setTranslateX(-5);
|
raceSparkLine.getYAxis().setTranslateX(-5);
|
||||||
raceSparkLine.getYAxis().setAutoRanging(false);
|
raceSparkLine.getYAxis().setAutoRanging(false);
|
||||||
sparklineYAxis.setTickMarkVisible(false);
|
sparklineYAxis.setTickMarkVisible(false);
|
||||||
startingBoats = new ArrayList<>(StreamParser.getBoats().values());
|
|
||||||
|
|
||||||
gameViewController.setup(this);
|
|
||||||
gameViewController.initializeCanvas();
|
|
||||||
initializeUpdateTimer();
|
initializeUpdateTimer();
|
||||||
initialiseFPSCheckBox();
|
initialiseFPSCheckBox();
|
||||||
initialiseAnnotationSlider();
|
initialiseAnnotationSlider();
|
||||||
initialiseBoatSelectionComboBox();
|
initialiseBoatSelectionComboBox();
|
||||||
gameViewController.timer.start();
|
|
||||||
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
|
||||||
|
|
||||||
|
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadRace (Map<Integer, Boat> participants, RaceXMLData raceData) {
|
||||||
|
this.participants = participants;
|
||||||
|
this.raceData = raceData;
|
||||||
|
this.course = raceData.getCompoundMarks();
|
||||||
|
gameView = new GameView();
|
||||||
|
gameView.startRace();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The important annotations have been changed, update this view
|
* The important annotations have been changed, update this view
|
||||||
@@ -126,33 +130,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
try {
|
try {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader();
|
FXMLLoader fxmlLoader = new FXMLLoader();
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
|
|
||||||
// Set controller
|
// Set controller
|
||||||
ImportantAnnotationController controller = new ImportantAnnotationController(this,
|
ImportantAnnotationController controller = new ImportantAnnotationController(
|
||||||
stage);
|
this, stage
|
||||||
|
);
|
||||||
fxmlLoader.setController(controller);
|
fxmlLoader.setController(controller);
|
||||||
|
|
||||||
// Load FXML and set CSS
|
// Load FXML and set CSS
|
||||||
fxmlLoader
|
fxmlLoader.setLocation(
|
||||||
.setLocation(getClass().getResource("/views/importantAnnotationSelectView.fxml"));
|
getClass().getResource("/views/importantAnnotationSelectView.fxml")
|
||||||
|
);
|
||||||
Scene scene = new Scene(fxmlLoader.load(), 469, 298);
|
Scene scene = new Scene(fxmlLoader.load(), 469, 298);
|
||||||
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
stage.initStyle(StageStyle.UNDECORATED);
|
stage.initStyle(StageStyle.UNDECORATED);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
|
|
||||||
controller.loadState(importantAnnotations);
|
controller.loadState(importantAnnotations);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void initialiseFPSCheckBox() {
|
private void initialiseFPSCheckBox() {
|
||||||
displayFps = true;
|
toggleFps.selectedProperty().addListener((obs, oldVal, newVal) ->
|
||||||
toggleFps.selectedProperty().addListener(
|
gameView.setFPSVisibility(toggleFps.isSelected())
|
||||||
(observable, oldValue, newValue) -> displayFps = !displayFps);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseAnnotationSlider() {
|
private void initialiseAnnotationSlider() {
|
||||||
@@ -189,8 +190,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
});
|
});
|
||||||
|
|
||||||
annotationSlider.valueProperty().addListener((obs, oldval, newVal) ->
|
annotationSlider.valueProperty().addListener((obs, oldval, newVal) ->
|
||||||
setAnnotations((int) annotationSlider.getValue()));
|
setAnnotations((int) annotationSlider.getValue())
|
||||||
|
);
|
||||||
annotationSlider.setValue(2);
|
annotationSlider.setValue(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,21 +199,21 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
/**
|
/**
|
||||||
* Used to add any new boats into the race that may have started late or not have had data received yet
|
* Used to add any new boats into the race that may have started late or not have had data received yet
|
||||||
*/
|
*/
|
||||||
void updateSparkLine(){
|
private void updateSparkLine(){
|
||||||
// Collect the racing boats that aren't already in the chart
|
// Collect the racing boats that aren't already in the chart
|
||||||
ArrayList<Yacht> sparkLineCandidates = startingBoats.stream().filter(yacht -> !sparkLineData.containsKey(yacht.getSourceID())
|
List<Boat> sparkLineCandidates = new ArrayList<>();
|
||||||
&& yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
participants.forEach((id, boat) ->{
|
||||||
|
if (!sparkLineData.containsKey(id) && boat.getPosition() != null && !boat.getPosition().equals("-"))
|
||||||
|
sparkLineCandidates.add(boat);
|
||||||
|
});
|
||||||
|
|
||||||
// Obtain the qualifying boats to set the max on the Y axis
|
sparklineYAxis.setUpperBound(participants.size() + 1);
|
||||||
racingBoats = startingBoats.stream().filter(yacht ->
|
|
||||||
yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
|
||||||
sparklineYAxis.setUpperBound(racingBoats.size() + 1);
|
|
||||||
|
|
||||||
// Create a new data series for new boats
|
// Create a new data series for new boats
|
||||||
sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> {
|
sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> {
|
||||||
Series<String, Double> yachtData = new Series<>();
|
Series<String, Double> yachtData = new Series<>();
|
||||||
yachtData.setName(yacht.getBoatName());
|
yachtData.setName(yacht.getSourceID().toString());
|
||||||
yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + participants.size() - Double.parseDouble(yacht.getPosition())));
|
||||||
sparkLineData.put(yacht.getSourceID(), yachtData);
|
sparkLineData.put(yacht.getSourceID(), yachtData);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -230,7 +231,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
// Adds the new data series to the sparkline (and set the colour of the series)
|
// Adds the new data series to the sparkline (and set the colour of the series)
|
||||||
raceSparkLine.setCreateSymbols(false);
|
raceSparkLine.setCreateSymbols(false);
|
||||||
positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> {
|
positions
|
||||||
|
.stream()
|
||||||
|
.filter(spark -> !raceSparkLine.getData().contains(spark))
|
||||||
|
.forEach(spark -> {
|
||||||
raceSparkLine.getData().add(spark);
|
raceSparkLine.getData().add(spark);
|
||||||
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||||
});
|
});
|
||||||
@@ -239,39 +243,40 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the yachts sparkline of the desired boat and using the new leg number
|
* Updates the yachts sparkline of the desired boat and using the new leg number
|
||||||
* @param yacht The yacht to be updated on the sparkline
|
* @param boat The yacht to be updated on the sparkline
|
||||||
* @param legNumber the leg number that the position will be assigned to
|
* @param legNumber the leg number that the position will be assigned to
|
||||||
*/
|
*/
|
||||||
public static void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
|
public void updateYachtPositionSparkline(Boat boat, Integer legNumber){
|
||||||
XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
|
XYChart.Series<String, Double> positionData = sparkLineData.get(boat.getSourceID());
|
||||||
positionData.getData().add(new XYChart.Data<>(Integer.toString(legNumber), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
positionData.getData().add(
|
||||||
|
new XYChart.Data<>(
|
||||||
|
Integer.toString(legNumber),
|
||||||
|
1 + participants.size() - Double.parseDouble(boat.getPosition())
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the rgb string of the boats colour to use for the chart via css
|
* gets the rgb string of the boats colour to use for the chart via css
|
||||||
* @param boatName boat passed in to get the boats colour
|
* @param boatId id of boat passed in to get the boats colour
|
||||||
* @return the colour as an rgb string
|
* @return the colour as an rgb string
|
||||||
*/
|
*/
|
||||||
private String getBoatColorAsRGB(String boatName){
|
private String getBoatColorAsRGB(String boatId){
|
||||||
Color color = Color.WHITE;
|
Color color = participants.get(Integer.valueOf(boatId)).getColour();
|
||||||
for (Yacht yacht: startingBoats){
|
|
||||||
if (Objects.equals(yacht.getBoatName(), boatName)){
|
|
||||||
color = yacht.getColour();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (color == null){
|
if (color == null){
|
||||||
return String.format("#%02X%02X%02X",255,255,255);
|
return String.format("#%02X%02X%02X",255,255,255);
|
||||||
}
|
}
|
||||||
return String.format( "#%02X%02X%02X",
|
return String.format( "#%02X%02X%02X",
|
||||||
(int)( color.getRed() * 255 ),
|
(int)( color.getRed() * 255 ),
|
||||||
(int)( color.getGreen() * 255 ),
|
(int)( color.getGreen() * 255 ),
|
||||||
(int)( color.getBlue() * 255 ) );
|
(int)( color.getBlue() * 255 )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initalises a timer which updates elements of the RaceView such as wind direction, boat
|
* Initialises a timer which updates elements of the RaceView such as wind direction, boat
|
||||||
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
||||||
* Updates of each of these attributes are called ONCE EACH SECOND
|
* Updates of each of these attributes are called ONCE EACH SECOND
|
||||||
*/
|
*/
|
||||||
@@ -286,9 +291,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
updateWindDirection();
|
updateWindDirection();
|
||||||
updateOrder();
|
updateOrder();
|
||||||
updateBoatSelectionComboBox();
|
updateBoatSelectionComboBox();
|
||||||
|
updateSparkLine();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start the timer
|
// Start the timer
|
||||||
timerTimeline.playFromStart();
|
timerTimeline.playFromStart();
|
||||||
}
|
}
|
||||||
@@ -302,9 +307,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
* @return The next Mark or null if none found
|
* @return The next Mark or null if none found
|
||||||
*/
|
*/
|
||||||
private Mark getNextMark(BoatGroup bg) {
|
private Mark getNextMark(BoatGroup bg) {
|
||||||
Integer legNumber = bg.getBoat().getLegNumber();
|
|
||||||
|
|
||||||
List<XMLParser.RaceXMLObject.Corner> markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence();
|
Integer legNumber = bg.getBoat().getLegNumber();
|
||||||
|
List<Corner> markSequence = raceData.getMarkSequence();
|
||||||
|
|
||||||
if (legNumber == 0) {
|
if (legNumber == 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -312,18 +317,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (XMLParser.RaceXMLObject.Corner corner : markSequence) {
|
for (Corner corner : markSequence) {
|
||||||
if (legNumber + 2 == corner.getSeqID()) {
|
if (legNumber + 2 == corner.getSeqID()) {
|
||||||
Integer thisCompoundMarkID = corner.getCompoundMarkID();
|
return raceData.getCompoundMarks().get(corner.getCompoundMarkID());
|
||||||
|
|
||||||
for (Mark mark : StreamParser.getXmlObject().getRaceXML().getAllCompoundMarks()) {
|
|
||||||
if (mark.getCompoundMarkID() == thisCompoundMarkID) {
|
|
||||||
return mark;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,8 +353,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
* in the boat selection combo box
|
* in the boat selection combo box
|
||||||
*/
|
*/
|
||||||
private void updateBoatSelectionComboBox() {
|
private void updateBoatSelectionComboBox() {
|
||||||
ObservableList<Yacht> observableBoats = FXCollections
|
ObservableList<Boat> observableBoats = FXCollections.observableArrayList();
|
||||||
.observableArrayList(StreamParser.getBoatsPos().values());
|
observableBoats.addAll(participants.values());
|
||||||
boatSelectionComboBox.setItems(observableBoats);
|
boatSelectionComboBox.setItems(observableBoats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,15 +369,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
positionVbox.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
|
|
||||||
// list of racing boat id
|
// list of racing boat id
|
||||||
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
|
|
||||||
.getParticipants();
|
|
||||||
ArrayList<Integer> participantIDs = new ArrayList<>();
|
|
||||||
for (Participant p : participants) {
|
|
||||||
participantIDs.add(p.getsourceID());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StreamParser.isRaceStarted()) {
|
if (StreamParser.isRaceStarted()) {
|
||||||
for (Yacht boat : StreamParser.getBoatsPos().values()) {
|
for (Boat boat : StreamParser.getBoatsPos().values()) {
|
||||||
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
|
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
|
||||||
if (boat.getBoatStatus() == 3) { // 3 is finish status
|
if (boat.getBoatStatus() == 3) { // 3 is finish status
|
||||||
Text textToAdd = new Text(boat.getPosition() + ". " +
|
Text textToAdd = new Text(boat.getPosition() + ". " +
|
||||||
@@ -397,15 +389,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (Yacht boat : StreamParser.getBoats().values()) {
|
participants.forEach((id, boat) ->{
|
||||||
if (participantIDs.contains(boat.getSourceID())) { // check if the boat is racing
|
|
||||||
Text textToAdd = new Text(boat.getPosition() + ". " +
|
Text textToAdd = new Text(boat.getPosition() + ". " +
|
||||||
boat.getShortName() + " ");
|
boat.getShortName() + " ");
|
||||||
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
textToAdd.setFill(Paint.valueOf("#d3d3d3"));
|
||||||
textToAdd.setStyle("");
|
textToAdd.setStyle("");
|
||||||
positionVbox.getChildren().add(textToAdd);
|
positionVbox.getChildren().add(textToAdd);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,8 +500,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
//This listener is fired whenever the combo box changes. This means when the values are updated
|
//This listener is fired whenever the combo box changes. This means when the values are updated
|
||||||
//We dont want to set the selected value if the values are updated but nothing clicked (null)
|
//We dont want to set the selected value if the values are updated but nothing clicked (null)
|
||||||
if (newValue != null && newValue != selectedBoat) {
|
if (newValue != null && newValue != selectedBoat) {
|
||||||
Yacht thisYacht = (Yacht) newValue;
|
Boat thisBoat = (Boat) newValue;
|
||||||
setSelectedBoat(thisYacht);
|
setSelectedBoat(thisBoat);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -529,9 +519,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
contentAnchorPane.getChildren().addAll((Pane) loader.load());
|
contentAnchorPane.getChildren().addAll((Pane) loader.load());
|
||||||
|
|
||||||
} catch (javafx.fxml.LoadException e) {
|
} catch (javafx.fxml.LoadException e) {
|
||||||
System.err.println(e.getCause());
|
System.err.println(e.getCause().toString());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println(e);
|
System.err.println(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,23 +559,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
return timerString;
|
return timerString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boolean isDisplayFps() {
|
|
||||||
return displayFps;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAnnotations(Integer annotationLevel) {
|
private void setAnnotations(Integer annotationLevel) {
|
||||||
switch (annotationLevel) {
|
switch (annotationLevel) {
|
||||||
// No Annotations
|
// No Annotations
|
||||||
case 0:
|
case 0:
|
||||||
for (BoatGroup bg : gameViewController.getBoatGroups()) {
|
gameView.setAnnotationVisibilities(
|
||||||
bg.setVisibility(false, false, false, false, false, false);
|
false, false, false, false, false, false
|
||||||
}
|
);
|
||||||
break;
|
break;
|
||||||
// Important Annotations
|
// Important Annotations
|
||||||
case 1:
|
case 1:
|
||||||
for (BoatGroup bg : gameViewController.getBoatGroups()) {
|
gameView.setAnnotationVisibilities(
|
||||||
bg.setVisibility(
|
|
||||||
importantAnnotations.getAnnotationState(Annotation.NAME),
|
importantAnnotations.getAnnotationState(Annotation.NAME),
|
||||||
importantAnnotations.getAnnotationState(Annotation.SPEED),
|
importantAnnotations.getAnnotationState(Annotation.SPEED),
|
||||||
importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
|
importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
|
||||||
@@ -593,13 +577,12 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
importantAnnotations.getAnnotationState(Annotation.TRACK),
|
importantAnnotations.getAnnotationState(Annotation.TRACK),
|
||||||
importantAnnotations.getAnnotationState(Annotation.WAKE)
|
importantAnnotations.getAnnotationState(Annotation.WAKE)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
// All Annotations
|
// All Annotations
|
||||||
case 2:
|
case 2:
|
||||||
for (BoatGroup bg : gameViewController.getBoatGroups()) {
|
gameView.setAnnotationVisibilities(
|
||||||
bg.setVisibility(true, true, true, true, true, true);
|
true, true, true, true, true, true
|
||||||
}
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,16 +591,16 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
/**
|
/**
|
||||||
* Sets all the annotations of the selected boat to be visible and all others to be hidden
|
* Sets all the annotations of the selected boat to be visible and all others to be hidden
|
||||||
*
|
*
|
||||||
* @param yacht The yacht for which we want to view all annotations
|
* @param boat The yacht for which we want to view all annotations
|
||||||
*/
|
*/
|
||||||
private void setSelectedBoat(Yacht yacht) {
|
private void setSelectedBoat(Boat boat) {
|
||||||
for (BoatGroup bg : gameViewController.getBoatGroups()) {
|
for (BoatGroup bg : gameViewController.getBoatGroups()) {
|
||||||
//We need to iterate over all race groups to get the matching boat group belonging to this boat if we
|
//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.
|
//are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup.
|
||||||
if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
|
if (bg.getBoat().getHullID().equals(boat.getHullID())) {
|
||||||
updateLaylines(bg);
|
updateLaylines(bg);
|
||||||
bg.setIsSelected(true);
|
bg.setIsSelected(true);
|
||||||
selectedBoat = yacht;
|
selectedBoat = boat;
|
||||||
} else {
|
} else {
|
||||||
bg.setIsSelected(false);
|
bg.setIsSelected(false);
|
||||||
}
|
}
|
||||||
@@ -641,4 +624,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
return sparkLineData.containsKey(yachtId);
|
return sparkLineData.containsKey(yachtId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateRaceData (RaceXMLData raceData) {
|
||||||
|
this.raceData = raceData;
|
||||||
|
gameView.updateBorder(raceData.getCourseLimit());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ public class StartScreenController {
|
|||||||
new GameState(ipAddress);
|
new GameState(ipAddress);
|
||||||
new MainServerThread().start();
|
new MainServerThread().start();
|
||||||
ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
|
ClientToServerThread clientToServerThread = new ClientToServerThread("localhost", 4950);
|
||||||
controller.setClientToServerThread(clientToServerThread);
|
// controller.setClientToServerThread(clientToServerThread);
|
||||||
clientToServerThread.start();
|
clientToServerThread.start();
|
||||||
// new GameServerThread("Fuck you");
|
// new GameServerThread("Fuck you");
|
||||||
// get the lobby controller so that we can pass the game server thread to it
|
// get the lobby controller so that we can pass the game server thread to it
|
||||||
|
|||||||
@@ -1,78 +1,174 @@
|
|||||||
package seng302.visualiser.controllers.client;
|
package seng302.visualiser.controllers.client;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import java.text.DateFormat;
|
||||||
import javafx.scene.Node;
|
import java.text.SimpleDateFormat;
|
||||||
import javafx.scene.input.KeyEvent;
|
import java.time.ZoneId;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import seng302.model.stream.XMLParser;
|
import seng302.model.Boat;
|
||||||
|
import seng302.model.mark.Mark;
|
||||||
|
import seng302.model.stream.parsers.PositionUpdateData.DeviceType;
|
||||||
|
import seng302.model.stream.parsers.MarkRoundingData;
|
||||||
|
import seng302.model.stream.parsers.RaceStartData;
|
||||||
|
import seng302.model.stream.parsers.RaceStatusData;
|
||||||
|
import seng302.model.stream.parsers.xml.RaceXMLData;
|
||||||
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
|
import seng302.model.stream.parsers.xml.RegattaXMLData;
|
||||||
|
import seng302.model.stream.parsers.xml.XMLParser;
|
||||||
|
import seng302.model.stream.parsers.PositionUpdateData;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
import seng302.server.messages.BoatActionMessage;
|
import seng302.visualiser.ClientSocketListener;
|
||||||
import seng302.server.messages.BoatActionType;
|
|
||||||
import seng302.visualiser.ClientToServerThread;
|
import seng302.visualiser.ClientToServerThread;
|
||||||
|
import seng302.visualiser.controllers.RaceViewController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by cir27 on 20/07/17.
|
* Created by cir27 on 20/07/17.
|
||||||
*/
|
*/
|
||||||
public class ClientController {
|
public class ClientController {
|
||||||
|
|
||||||
Pane holderPane;
|
private final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||||
ClientToServerThread socketThread;
|
|
||||||
|
private Pane holderPane;
|
||||||
|
private ClientToServerThread socketThread;
|
||||||
|
private ClientSocketListener socketListener;
|
||||||
|
|
||||||
|
private RaceViewController raceView;
|
||||||
|
|
||||||
|
private Map<Integer, Boat> allBoatsMap;
|
||||||
|
private Map<Integer, Boat> racingBoats = new HashMap<>();
|
||||||
|
private RegattaXMLData regattaData;
|
||||||
|
private RaceXMLData raceData;
|
||||||
|
|
||||||
public ClientController (String ipAddress, Pane holder) {
|
public ClientController (String ipAddress, Pane holder) {
|
||||||
this.holderPane = holder;
|
this.holderPane = holder;
|
||||||
socketThread = new ClientToServerThread(ipAddress, 4950);
|
socketThread = new ClientToServerThread(ipAddress, 4950);
|
||||||
socketThread.start();
|
socketThread.start();
|
||||||
socketThread.waitForXML(event -> storeXMLData());
|
socketListener = this::parsePacket;
|
||||||
|
socketThread.addStreamObserver(socketListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRaceView () {
|
||||||
|
allBoatsMap.forEach((id, boat) -> {
|
||||||
|
if (raceData.getParticipants().contains(id))
|
||||||
|
racingBoats.put(id, boat);
|
||||||
|
});
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("RaceView.fxml"));
|
||||||
|
raceView = fxmlLoader.getController();
|
||||||
|
raceView.loadRace(racingBoats, raceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parsePacket(StreamPacket packet) {
|
private void parsePacket(StreamPacket packet) {
|
||||||
try {
|
|
||||||
switch (packet.getType()) {
|
switch (packet.getType()) {
|
||||||
case HEARTBEAT:
|
|
||||||
extractHeartBeat(packet);
|
|
||||||
break;
|
|
||||||
case RACE_STATUS:
|
case RACE_STATUS:
|
||||||
extractRaceStatus(packet);
|
processRaceStatusUpdate(StreamParser.extractRaceStatus(packet));
|
||||||
break;
|
break;
|
||||||
case DISPLAY_TEXT_MESSAGE:
|
|
||||||
extractDisplayMessage(packet);
|
case REGATTA_XML:
|
||||||
|
regattaData = XMLParser.parseRegatta(
|
||||||
|
StreamParser.extractXmlMessage(packet)
|
||||||
|
);
|
||||||
|
DATE_TIME_FORMAT.setTimeZone(
|
||||||
|
TimeZone.getTimeZone(
|
||||||
|
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(regattaData.getUtcOffset()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
startRaceIfAllDataRecieved();
|
||||||
break;
|
break;
|
||||||
case XML_MESSAGE:
|
|
||||||
newRaceXmlReceived = true;
|
case RACE_XML:
|
||||||
extractXmlMessage(packet);
|
raceData = XMLParser.parseRace(
|
||||||
|
StreamParser.extractXmlMessage(packet)
|
||||||
|
);
|
||||||
|
if (raceView != null) {
|
||||||
|
raceView.updateRaceData(raceData);
|
||||||
|
}
|
||||||
|
startRaceIfAllDataRecieved();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BOAT_XML:
|
||||||
|
allBoatsMap = XMLParser.parseBoats(
|
||||||
|
StreamParser.extractXmlMessage(packet)
|
||||||
|
);
|
||||||
|
startRaceIfAllDataRecieved();
|
||||||
|
break;
|
||||||
|
|
||||||
case RACE_START_STATUS:
|
case RACE_START_STATUS:
|
||||||
extractRaceStartStatus(packet);
|
RaceStartData raceStartData = StreamParser.extractRaceStartStatus(packet);
|
||||||
break;
|
|
||||||
case YACHT_EVENT_CODE:
|
|
||||||
extractYachtEventCode(packet);
|
|
||||||
break;
|
|
||||||
case YACHT_ACTION_CODE:
|
|
||||||
extractYachtActionCode(packet);
|
|
||||||
break;
|
|
||||||
case CHATTER_TEXT:
|
|
||||||
extractChatterText(packet);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOAT_LOCATION:
|
case BOAT_LOCATION:
|
||||||
extractBoatLocation(packet);
|
PositionUpdateData positionData = StreamParser.extractBoatLocation(packet);
|
||||||
|
updatePosition(positionData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MARK_ROUNDING:
|
case MARK_ROUNDING:
|
||||||
extractMarkRounding(packet);
|
MarkRoundingData roundingData = StreamParser.extractMarkRounding(packet);
|
||||||
break;
|
updateMarkRounding(roundingData);
|
||||||
case COURSE_WIND:
|
|
||||||
extractCourseWind(packet);
|
|
||||||
break;
|
|
||||||
case AVG_WIND:
|
|
||||||
extractAvgWind(packet);
|
|
||||||
break;
|
|
||||||
case BOAT_ACTION:
|
|
||||||
extractBoatAction(packet);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (NullPointerException e) {
|
}
|
||||||
System.out.println("Error parsing packet");
|
|
||||||
e.printStackTrace();
|
private void startRaceIfAllDataRecieved () {
|
||||||
|
if (raceData != null && allBoatsMap != null && regattaData != null)
|
||||||
|
loadRaceView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the position of a boat. Boat and position are given in the provided data.
|
||||||
|
* @param positionData
|
||||||
|
*/
|
||||||
|
private void updatePosition(PositionUpdateData positionData) {
|
||||||
|
if (positionData.getType() == DeviceType.YACHT_TYPE) {
|
||||||
|
Boat boat = racingBoats.get(positionData.getDeviceId());
|
||||||
|
boat.setVelocity(positionData.getGroundSpeed());
|
||||||
|
boat.setLat(positionData.getLat());
|
||||||
|
boat.setLon(positionData.getLon());
|
||||||
|
boat.setHeading(positionData.getHeading());
|
||||||
|
} else {
|
||||||
|
Mark mark = raceData.getCompoundMarks().get(positionData.getDeviceId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the boat as having passed the mark. Boat and mark are given by the ids in the
|
||||||
|
* provided data.
|
||||||
|
* @param roundingData Contains data for the rounding of a mark.
|
||||||
|
*/
|
||||||
|
private void updateMarkRounding(MarkRoundingData roundingData) {
|
||||||
|
Boat boat = racingBoats.get(roundingData.getBoatId());
|
||||||
|
boat.setMarkRoundingTime(roundingData.getTimeStamp());
|
||||||
|
boat.setLastMarkRounded(
|
||||||
|
raceData.getCompoundMarks().get(
|
||||||
|
roundingData.getMarkId()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRaceStatusUpdate (RaceStatusData data) {
|
||||||
|
String raceTimeStr = DATE_TIME_FORMAT.format(data.getCurrentTime());
|
||||||
|
Date date = new Date();
|
||||||
|
date.getTime();
|
||||||
|
long timeTillStart = (data.getExpectedStartTime() - data.getCurrentTime()) / 1000;
|
||||||
|
for (long[] boatData : data.getBoatData()) {
|
||||||
|
Boat boat = allBoatsMap.get((int) boatData[0]);
|
||||||
|
boat.setEstimateTimeAtNextMark(boatData[1]);
|
||||||
|
boat.setEstimateTimeAtFinish(boatData[2]);
|
||||||
|
int legNumber = (int) boatData[3];
|
||||||
|
boat.setLegNumber(legNumber);
|
||||||
|
if (legNumber != boat.getLegNumber()) {
|
||||||
|
int placing = 1;
|
||||||
|
for (Boat otherBoat : allBoatsMap.values()) {
|
||||||
|
if (otherBoat.getSourceID() != boatData[0] &&
|
||||||
|
boat.getLegNumber() <= otherBoat.getLegNumber())
|
||||||
|
placing++;
|
||||||
|
}
|
||||||
|
boat.setPosition(placing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import javafx.scene.Group;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Boat;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -34,9 +34,9 @@ public class BoatAnnotations extends Group{
|
|||||||
private Text estTimeToNextMarkObject;
|
private Text estTimeToNextMarkObject;
|
||||||
private Text legTimeObject;
|
private Text legTimeObject;
|
||||||
|
|
||||||
private Yacht boat;
|
private Boat boat;
|
||||||
|
|
||||||
BoatAnnotations (Yacht boat, Color theme) {
|
BoatAnnotations (Boat boat, Color theme) {
|
||||||
super.setCache(true);
|
super.setCache(true);
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
background.setX(BACKGROUND_X);
|
background.setX(BACKGROUND_X);
|
||||||
@@ -83,10 +83,10 @@ public class BoatAnnotations extends Group{
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update () {
|
void update () {
|
||||||
velocityObject.setText(String.format(String.format("%.2f m/s", boat.getVelocity())));
|
velocityObject.setText(String.format("%.2f m/s", boat.getVelocity()));
|
||||||
|
|
||||||
if (boat.getTimeTillNext() != null) {
|
|
||||||
DateFormat format = new SimpleDateFormat("mm:ss");
|
DateFormat format = new SimpleDateFormat("mm:ss");
|
||||||
|
if (boat.getTimeTillNext() != null) {
|
||||||
String timeToNextMark = format
|
String timeToNextMark = format
|
||||||
.format(boat.getTimeTillNext() - StreamParser.getCurrentTimeLong());
|
.format(boat.getTimeTillNext() - StreamParser.getCurrentTimeLong());
|
||||||
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
|
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
|
||||||
@@ -95,7 +95,6 @@ public class BoatAnnotations extends Group{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (boat.getMarkRoundTime() != null) {
|
if (boat.getMarkRoundTime() != null) {
|
||||||
DateFormat format = new SimpleDateFormat("mm:ss");
|
|
||||||
String elapsedTime = format
|
String elapsedTime = format
|
||||||
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundTime());
|
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundTime());
|
||||||
legTimeObject.setText("Last mark: " + elapsedTime);
|
legTimeObject.setText("Last mark: " + elapsedTime);
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import javafx.scene.shape.Line;
|
|||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
import seng302.visualiser.controllers.GameViewController;
|
import seng302.visualiser.controllers.GameViewController;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Boat;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.model.mark.GateMark;
|
import seng302.model.mark.GateMark;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.SingleMark;
|
import seng302.model.mark.SingleMark;
|
||||||
import seng302.model.stream.StreamParser;
|
import seng302.model.stream.parsers.StreamParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
|
* BoatGroup is a javafx group that by default contains a graphical objects for representing a 2
|
||||||
@@ -38,7 +38,7 @@ public class BoatGroup extends Group {
|
|||||||
private Double lastRotation = 0.0;
|
private Double lastRotation = 0.0;
|
||||||
private long framesToMove;
|
private long framesToMove;
|
||||||
//Graphical objects
|
//Graphical objects
|
||||||
private Yacht boat;
|
private Boat boat;
|
||||||
private Group lineGroup = new Group();
|
private Group lineGroup = new Group();
|
||||||
private Polygon boatPoly;
|
private Polygon boatPoly;
|
||||||
private Wake wake;
|
private Wake wake;
|
||||||
@@ -58,7 +58,7 @@ public class BoatGroup extends Group {
|
|||||||
* to tell which BoatGroup to update.
|
* to tell which BoatGroup to update.
|
||||||
* @param color The colour of the boat polygon and the trailing line.
|
* @param color The colour of the boat polygon and the trailing line.
|
||||||
*/
|
*/
|
||||||
public BoatGroup(Yacht boat, Color color) {
|
public BoatGroup(Boat boat, Color color) {
|
||||||
destinationSet = false;
|
destinationSet = false;
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
initChildren(color);
|
initChildren(color);
|
||||||
@@ -74,7 +74,7 @@ public class BoatGroup extends Group {
|
|||||||
* @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat
|
* @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) {
|
public BoatGroup(Boat boat, Color color, double... points) {
|
||||||
destinationSet = false;
|
destinationSet = false;
|
||||||
this.boat = boat;
|
this.boat = boat;
|
||||||
initChildren(color, points);
|
initChildren(color, points);
|
||||||
@@ -258,15 +258,9 @@ public class BoatGroup extends Group {
|
|||||||
boat is travelling into the wind. thus upwind. Otherwise if they are on different sides, then the boat is going
|
boat is travelling into the wind. thus upwind. Otherwise if they are on different sides, then the boat is going
|
||||||
with the wind.
|
with the wind.
|
||||||
*/
|
*/
|
||||||
if (boatLineFuncResult == windLineFuncResult) {
|
return boatLineFuncResult.equals(windLineFuncResult);
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setIsSelected(Boolean isSelected) {
|
public void setIsSelected(Boolean isSelected) {
|
||||||
this.isSelected = isSelected;
|
this.isSelected = isSelected;
|
||||||
setLineGroupVisible(isSelected);
|
setLineGroupVisible(isSelected);
|
||||||
@@ -275,7 +269,9 @@ public class BoatGroup extends Group {
|
|||||||
setLayLinesVisible(isSelected);
|
setLayLinesVisible(isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) {
|
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime,
|
||||||
|
boolean trail, boolean wake) {
|
||||||
|
|
||||||
boatAnnotations.setVisibile(teamName, velocity, estTime, legTime);
|
boatAnnotations.setVisibile(teamName, velocity, estTime, legTime);
|
||||||
this.wake.setVisible(wake);
|
this.wake.setVisible(wake);
|
||||||
this.lineGroup.setVisible(trail);
|
this.lineGroup.setVisible(trail);
|
||||||
@@ -306,7 +302,7 @@ public class BoatGroup extends Group {
|
|||||||
return laylines;
|
return laylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Yacht getBoat() {
|
public Boat getBoat() {
|
||||||
return boat;
|
return boat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user