Merge branch 'Story47CourseLimits' into 'develop'

Story47 course limits

The idea was to have the course canvas update whenever we receive a new xml packet for the race data. Specifically for a few seconds after the race where the course boundaries change, going from a boundary which contains the start line to cutting off the start line as soon as the boats start.

See merge request !30
This commit is contained in:
Michael Rausch
2017-05-15 18:43:49 +12:00
9 changed files with 175 additions and 113 deletions
+5 -9
View File
@@ -9,8 +9,8 @@ import seng302.models.parsers.StreamParser;
import seng302.models.parsers.StreamReceiver; import seng302.models.parsers.StreamReceiver;
import seng302.server.ServerThread; import seng302.server.ServerThread;
public class App extends Application public class App extends Application {
{
@Override @Override
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
@@ -39,15 +39,15 @@ public class App extends Application
e.printStackTrace(); e.printStackTrace();
} }
if (args.length == 1 && args[0].equals("-standalone")){ if (args.length == 1 && args[0].equals("-standalone")) {
return; return;
} }
if (args.length == 3 && args[0].equals("-server")){ if (args.length == 3 && args[0].equals("-server")) {
sr = new StreamReceiver(args[1], Integer.valueOf(args[2]), "RaceStream"); sr = new StreamReceiver(args[1], Integer.valueOf(args[2]), "RaceStream");
} else if(args.length == 2 && args[0].equals("-server")){ } else if (args.length == 2 && args[0].equals("-server")) {
switch (args[1]) { switch (args[1]) {
case "internal": case "internal":
sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("localhost", 4949, "RaceStream");
@@ -62,8 +62,6 @@ public class App extends Application
} }
//Change the StreamReceiver in this else block to change the default data source. //Change the StreamReceiver in this else block to change the default data source.
else{ else{
// sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("localhost", 4949, "RaceStream");
} }
@@ -73,8 +71,6 @@ public class App extends Application
launch(args); launch(args);
} }
} }
@@ -240,6 +240,17 @@ public class CanvasController {
} }
} }
} }
checkForCourseChanges();
}
private void checkForCourseChanges() {
if (StreamParser.isNewRaceXmlReceived()){
gc.setFill(Color.SKYBLUE);
gc.fillRect(0,0, CANVAS_WIDTH, CANVAS_HEIGHT);
gc.restore();
addRaceBorder();
canvas.toBack();
}
} }
private void move(long id, RaceObject raceObject){ private void move(long id, RaceObject raceObject){
@@ -345,6 +356,8 @@ public class CanvasController {
* Calculates x and y location for every marker that fits it to the canvas the race will be drawn on. * Calculates x and y location for every marker that fits it to the canvas the race will be drawn on.
*/ */
private void fitMarksToCanvas() { private void fitMarksToCanvas() {
//Check is called once to avoid unnecessarily change the course limits once the race is running
StreamParser.isNewRaceXmlReceived();
findMinMaxPoint(); findMinMaxPoint();
double minLonToMaxLon = scaleRaceExtremities(); double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon); calculateReferencePointLocation(minLonToMaxLon);
@@ -1,53 +0,0 @@
package seng302.models.parsers;
/**
* Created by Kusal on 4/24/2017.
*/
public enum PacketType {
HEARTBEAT,
RACE_STATUS,
DISPLAY_TEXT_MESSAGE,
XML_MESSAGE,
RACE_START_STATUS,
YACHT_EVENT_CODE,
YACHT_ACTION_CODE,
CHATTER_TEXT,
BOAT_LOCATION,
MARK_ROUNDING,
COURSE_WIND,
AVG_WIND,
OTHER;
static PacketType assignPacketType(int packetType){
switch(packetType){
case 1:
return HEARTBEAT;
case 12:
return RACE_STATUS;
case 20:
return DISPLAY_TEXT_MESSAGE;
case 26:
return XML_MESSAGE;
case 27:
return RACE_START_STATUS;
case 29:
return YACHT_EVENT_CODE;
case 31:
return YACHT_ACTION_CODE;
case 36:
return CHATTER_TEXT;
case 37:
return BOAT_LOCATION;
case 38:
return MARK_ROUNDING;
case 44:
return COURSE_WIND;
case 47:
return AVG_WIND;
default:
}
return OTHER;
}
}
@@ -1,44 +0,0 @@
package seng302.models.parsers;
/**
* Created by kre39 on 23/04/17.
*/
public class StreamPacket {
//Change int to an ENUM for the type
private PacketType type;
private long messageLength;
private long timeStamp;
private byte[] payload;
StreamPacket(int type, long messageLength, long timeStamp, byte[] payload) {
this.type = PacketType.assignPacketType(type);
this.messageLength = messageLength;
this.timeStamp = timeStamp;
this.payload = payload;
// System.out.println("type = " + this.type.toString());
//switch the packet type to deal with what ever specific packet you want to deal with
// if (this.type == PacketType.XML_MESSAGE){
// //System.out.println("--------");
// System.out.println(new String(payload));
// //StreamParser.parsePacket(this);
// }
}
PacketType getType() {
return type;
}
public long getMessageLength() {
return messageLength;
}
byte[] getPayload() {
return payload;
}
long getTimeStamp() {
return timeStamp;
}
}
@@ -31,6 +31,7 @@ public class StreamParser extends Thread{
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatPositions = new ConcurrentHashMap<>(); public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatPositions = new ConcurrentHashMap<>();
private String threadName; private String threadName;
private Thread t; private Thread t;
private static boolean newRaceXmlReceived = false;
private static boolean raceStarted = false; private static boolean raceStarted = false;
private static XMLParser xmlObject; private static XMLParser xmlObject;
private static boolean raceFinished = false; private static boolean raceFinished = false;
@@ -123,6 +124,7 @@ public class StreamParser extends Thread{
extractDisplayMessage(packet); extractDisplayMessage(packet);
break; break;
case XML_MESSAGE: case XML_MESSAGE:
newRaceXmlReceived = true;
extractXmlMessage(packet); extractXmlMessage(packet);
break; break;
case RACE_START_STATUS: case RACE_START_STATUS:
@@ -296,9 +298,8 @@ public class StreamParser extends Thread{
byte[] payload = packet.getPayload(); byte[] payload = packet.getPayload();
int messageType = payload[9]; int messageType = payload[9];
long messagelength = bytesToLong(Arrays.copyOfRange(payload,12,14)); long messageLength = bytesToLong(Arrays.copyOfRange(payload,12,14));
String xmlMessage = new String((Arrays.copyOfRange(payload,14,(int) (14 + messagelength)))).trim(); String xmlMessage = new String((Arrays.copyOfRange(payload,14,(int) (14 + messageLength)))).trim();
//System.out.println("xmlMessage2 = " + xmlMessage);
//Create XML document Object //Create XML document Object
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
@@ -315,6 +316,9 @@ public class StreamParser extends Thread{
if (messageType == 7) { //7 is the boat XML if (messageType == 7) { //7 is the boat XML
boats = xmlObject.getBoatXML().getCompetingBoats(); boats = xmlObject.getBoatXML().getCompetingBoats();
} }
if (messageType == 6) { //6 is race info xml
newRaceXmlReceived = true;
}
} }
/** /**
@@ -593,5 +597,20 @@ public class StreamParser extends Thread{
appRunning = false; appRunning = false;
System.out.println("[CLIENT] Shutting down stream parser"); System.out.println("[CLIENT] Shutting down stream parser");
} }
/**
* 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;
}
}
} }
+25 -2
View File
@@ -235,6 +235,28 @@ public class ServerThread implements Runnable, Observer {
} }
} }
/**
* Send the post-start race course information
*/
private void sendPostStartCourseXml(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
try {
Message raceData = getXmlMessage("/server_config/courseLimits.xml", XMLMessageSubType.RACE);
if (raceData != null) {
server.send(raceData);
serverLog("Sending race data", 0);
}
}catch (IOException e) {
serverLog("Couldn't send an XML Message: " + e.getMessage(), 0);
}
}
},25000);
//Delays the new course xml data for 25 seconds so the boats are able to pass the starting line
}
public void run() { public void run() {
try{ try{
server = new StreamingServerSocket(PORT_NUMBER); server = new StreamingServerSocket(PORT_NUMBER);
@@ -252,12 +274,13 @@ public class ServerThread implements Runnable, Observer {
sendXml(); sendXml();
startSendingRaceStartStatusMessages(); startSendingRaceStartStatusMessages();
startSendingRaceStatusMessages(); startSendingRaceStatusMessages();
sendPostStartCourseXml();
} }
/** /**
* Start sending static boat position updates when race has finished * Start sending static boat position updates when race has finished
*/ */
private void startSendingRaceFinishedBoatPostions(){ private void startSendingRaceFinishedBoatPositions(){
Timer t = new Timer(); Timer t = new Timer();
t.schedule(new TimerTask() { t.schedule(new TimerTask() {
@Override @Override
@@ -316,7 +339,7 @@ public class ServerThread implements Runnable, Observer {
} }
if (numOfBoatsFinished == ((List<Boat>) arg).size()) { if (numOfBoatsFinished == ((List<Boat>) arg).size()) {
startSendingRaceFinishedBoatPostions(); startSendingRaceFinishedBoatPositions();
} }
} }
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Race>
<CreationTimeDate>2015-08-29T13:12:40+02:00</CreationTimeDate>
<RaceStartTime Start="2015-08-29T13:10:00+02:00" Postpone="False"/>
<RaceID>15082901</RaceID>
<RaceType>Fleet</RaceType>
<Participants>
<Yacht SourceID="101"/>
<Yacht SourceID="102"/>
<Yacht SourceID="103"/>
<Yacht SourceID="104"/>
<Yacht SourceID="105"/>
<Yacht SourceID="106"/>
</Participants>
<Course>
<CompoundMark CompoundMarkID="1" Name="Mark0">
<Mark SeqID="1" Name="Start Line 1" TargetLat="57.6703330" TargetLng="11.8278330"
SourceID="122"/>
<Mark SeqID="2" Name="Start Line 2" TargetLat="57.6703330" TargetLng="11.8278330"
SourceID="123"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Mark1">
<Mark SeqID="1" Name="Mark1" TargetLat="57.6675700" TargetLng="11.8359880" SourceID="131"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Mark2">
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="124"/>
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="125"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Mark3">
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="126"/>
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="127"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Mark2">
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="124"/>
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="125"/>
</CompoundMark>
<CompoundMark CompoundMarkID="6" Name="Mark3">
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="126"/>
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="127"/>
</CompoundMark>
<CompoundMark CompoundMarkID="7" Name="Mark2">
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="124"/>
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="125"/>
</CompoundMark>
<CompoundMark CompoundMarkID="8" Name="Mark3">
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="126"/>
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="127"/>
</CompoundMark>
<CompoundMark CompoundMarkID="9" Name="Mark2">
<Mark SeqID="1" Name="Lee Gate 1" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="124"/>
<Mark SeqID="2" Name="Lee Gate 2" TargetLat="57.6708220" TargetLng="11.8433900"
SourceID="125"/>
</CompoundMark>
<CompoundMark CompoundMarkID="10" Name="Mark3">
<Mark SeqID="1" Name="Wind Gate 1" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="126"/>
<Mark SeqID="2" Name="Wind Gate 2" TargetLat="57.6650170" TargetLng="11.8279170"
SourceID="127"/>
</CompoundMark>
<CompoundMark CompoundMarkID="11" Name="Mark4">
<Mark SeqID="1" Name="Finish Line 1" TargetLat="57.6715240" TargetLng="11.8444950"
SourceID="128"/>
<Mark SeqID="2" Name="Finish Line 2" TargetLat="57.6715240" TargetLng="11.8444950"
SourceID="129"/>
</CompoundMark>
</Course>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="PS" ZoneSize="3"/>
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3"/>
<Corner SeqID="3" CompoundMarkID="3" Rounding="SP" ZoneSize="3"/>
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="3"/>
<Corner SeqID="5" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
<Corner SeqID="6" CompoundMarkID="6" Rounding="PS" ZoneSize="3"/>
<Corner SeqID="7" CompoundMarkID="7" Rounding="SP" ZoneSize="3"/>
<Corner SeqID="8" CompoundMarkID="8" Rounding="PS" ZoneSize="3"/>
<Corner SeqID="9" CompoundMarkID="9" Rounding="SP" ZoneSize="3"/>
<Corner SeqID="10" CompoundMarkID="10" Rounding="PS" ZoneSize="3"/>
<Corner SeqID="11" CompoundMarkID="11" Rounding="PS" ZoneSize="3"/>
</CompoundMarkSequence>
<CourseLimit>
<Limit SeqID="1" Lat="57.6739450" Lon="11.8417100"/>
<Limit SeqID="2" Lat="57.6709520" Lon="11.8485010"/>
<Limit SeqID="3" Lat="57.6690260" Lon="11.8472790"/>
<Limit SeqID="4" Lat="57.6693140" Lon="11.8457610"/>
<Limit SeqID="5" Lat="57.6665370" Lon="11.8432910"/>
<Limit SeqID="6" Lat="57.6641400" Lon="11.8385840"/>
<Limit SeqID="7" Lat="57.6629430" Lon="11.8332030"/>
<Limit SeqID="8" Lat="57.6629480" Lon="11.8249660"/>
<Limit SeqID="9" Lat="57.6686890" Lon="11.8250920"/>
<Limit SeqID="10" Lat="57.6708220" Lon="11.8321340"/>
</CourseLimit>
</Race>
+4 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Race> <Race>
<CreationTimeDate>2015-08-29T13:12:40+02:00</CreationTimeDate> <CreationTimeDate>2015-08-29T11:27:15+02:00</CreationTimeDate>
<RaceStartTime Start="2015-08-29T13:10:00+02:00" Postpone="False" /> <RaceStartTime Start="2015-08-29T13:10:00+02:00" Postpone="False" />
<RaceID>15082901</RaceID> <RaceID>15082901</RaceID>
<RaceType>Fleet</RaceType> <RaceType>Fleet</RaceType>
@@ -80,6 +80,8 @@
<Limit SeqID="7" Lat="57.6629430" Lon="11.8332030" /> <Limit SeqID="7" Lat="57.6629430" Lon="11.8332030" />
<Limit SeqID="8" Lat="57.6629480" Lon="11.8249660" /> <Limit SeqID="8" Lat="57.6629480" Lon="11.8249660" />
<Limit SeqID="9" Lat="57.6686890" Lon="11.8250920" /> <Limit SeqID="9" Lat="57.6686890" Lon="11.8250920" />
<Limit SeqID="10" Lat="57.6708220" Lon="11.8321340" /> <Limit SeqID="10" Lat="57.6692230" Lon="11.8231430" />
<Limit SeqID="11" Lat="57.6725370" Lon="11.8272480" />
<Limit SeqID="12" Lat="57.6708220" Lon="11.8321340" />
</CourseLimit> </CourseLimit>
</Race> </Race>
@@ -8,6 +8,7 @@ import java.lang.reflect.Method;
import java.net.Socket; import java.net.Socket;
import java.util.Comparator; import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.PriorityBlockingQueue;
import seng302.models.parsers.packets.StreamPacket;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;