# Conflicts:
#	src/main/java/seng302/App.java
#	src/main/java/seng302/controllers/Controller.java
This commit is contained in:
Calum
2017-05-04 10:48:06 +12:00
19 changed files with 359 additions and 257 deletions
+26 -10
View File
@@ -1,11 +1,11 @@
package seng302; package seng302;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import seng302.controllers.Controller;
import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamParser;
import seng302.models.parsers.StreamReceiver; import seng302.models.parsers.StreamReceiver;
import seng302.server.ServerThread; import seng302.server.ServerThread;
@@ -14,31 +14,47 @@ 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"));
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/MainView.fxml"));
primaryStage.setTitle("RaceVision"); primaryStage.setTitle("RaceVision");
primaryStage.setScene(new Scene(loader.load())); primaryStage.setScene(new Scene(root));
((Controller) loader.getController()).setStage(primaryStage); primaryStage.setMaximized(true);
primaryStage.show(); primaryStage.show();
primaryStage.setOnCloseRequest(e -> {
Platform.exit();
});
} }
public static void main(String[] args) { public static void main(String[] args) {
StreamReceiver sr; StreamReceiver sr = null;
new ServerThread("Racevision Test Server"); new ServerThread("Racevision Test Server");
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (args.length > 1){ if (args.length == 3 && args[0].equals("-server")){
sr = new StreamReceiver(args[1], Integer.valueOf(args[2]), "RaceStream");
}
else if(args.length == 2 && args[0].equals("-server")){
switch (args[1]) {
case "internal":
sr = new StreamReceiver("localhost", 8085, "RaceStream"); sr = new StreamReceiver("localhost", 8085, "RaceStream");
break;
case "staffserver":
sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
break;
case "official":
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
break;
}
} }
else{ else{
sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"RaceStream"); sr = new StreamReceiver("localhost", 8085, "RaceStream");
// sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
// sr = new StreamReceiver("localhost", 8085, "RaceStream");
} }
sr.start(); sr.start();
@@ -135,7 +135,7 @@ public class CanvasController {
} }
}; };
for (Mark m : raceViewController.getRace().getCourse()) { for (Mark m : raceViewController.getRace().getCourse()) {
System.out.println(m.getName()); //System.out.println(m.getName());
} }
//timer.start(); //timer.start();
} }
@@ -3,11 +3,9 @@ package seng302.controllers;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn;
@@ -16,21 +14,17 @@ import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.stage.Stage; import seng302.models.Yacht;
import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamParser;
import seng302.models.parsers.XMLParser; import seng302.models.parsers.XMLParser;
import javax.xml.crypto.dsig.XMLObject;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
/**
* Created by michaelrausch on 21/03/17.
*/
public class Controller implements Initializable { public class Controller implements Initializable {
@FXML @FXML
private AnchorPane contentPane; private AnchorPane contentPane;
@@ -41,26 +35,25 @@ public class Controller implements Initializable {
@FXML @FXML
private Button switchToRaceViewButton; private Button switchToRaceViewButton;
@FXML @FXML
private TableView teamList; private TableView<Yacht> teamList;
@FXML @FXML
private TableColumn boatNameCol; private TableColumn<Yacht, String> boatNameCol;
@FXML @FXML
private TableColumn shortNameCol; private TableColumn<Yacht, String> shortNameCol;
@FXML @FXML
private TableColumn countryCol; private TableColumn<Yacht, String> countryCol;
@FXML
private TableColumn<Yacht, String> posCol;
@FXML @FXML
private Label realTime; private Label realTime;
private Stage stage;
private XMLParser xmlParser;
private void setContentPane(String jfxUrl){ private void setContentPane(String jfxUrl){
try{ try{
contentPane.getChildren().removeAll(); contentPane.getChildren().removeAll();
contentPane.getChildren().clear(); contentPane.getChildren().clear();
// contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
FXMLLoader loader = new FXMLLoader(getClass().getResource(jfxUrl));
contentPane.getChildren().addAll((Node) loader.load());
RaceViewController r = (RaceViewController) loader.getController();
//((RaceViewController) loader.getController()).setStage(stage);
} }
catch(javafx.fxml.LoadException e){ catch(javafx.fxml.LoadException e){
System.err.println(e.getCause()); System.err.println(e.getCause());
@@ -82,7 +75,7 @@ public class Controller implements Initializable {
*/ */
public void startStream() { public void startStream() {
if (StreamParser.isStreamStatus()) { if (StreamParser.isStreamStatus()) {
XMLParser xmlParser = StreamParser.getXmlObject(); xmlParser = StreamParser.getXmlObject();
streamButton.setVisible(false); streamButton.setVisible(false);
realTime.setVisible(true); realTime.setVisible(true);
timeTillLive.setVisible(true); timeTillLive.setVisible(true);
@@ -108,7 +101,7 @@ public class Controller implements Initializable {
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
String timerString = "-" + timerMinute + ":" + timerSecond + " minutes"; String timerString = "-" + timerMinute + ":" + timerSecond;
timeTillLive.setText(timerString); timeTillLive.setText(timerString);
} else { } else {
realTime.setText(StreamParser.getCurrentTimeString()); realTime.setText(StreamParser.getCurrentTimeString());
@@ -120,7 +113,7 @@ public class Controller implements Initializable {
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
String timerString = timerMinute + ":" + timerSecond + " minutes"; String timerString = timerMinute + ":" + timerSecond;
timeTillLive.setText(timerString); timeTillLive.setText(timerString);
} }
}); });
@@ -137,22 +130,32 @@ public class Controller implements Initializable {
} }
private void updateTeamList() { private void updateTeamList() {
ObservableList<XMLParser.BoatXMLObject.Boat> data = FXCollections.observableArrayList(); ObservableList<Yacht> data = FXCollections.observableArrayList();
teamList.setItems(data); teamList.setItems(data);
boatNameCol.setCellValueFactory( boatNameCol.setCellValueFactory(
new PropertyValueFactory<XMLParser.BoatXMLObject.Boat,String>("BoatName") new PropertyValueFactory<>("boatName")
); );
shortNameCol.setCellValueFactory( shortNameCol.setCellValueFactory(
new PropertyValueFactory<XMLParser.BoatXMLObject.Boat,String>("ShortName") new PropertyValueFactory<>("shortName")
); );
countryCol.setCellValueFactory( countryCol.setCellValueFactory(
new PropertyValueFactory<XMLParser.BoatXMLObject.Boat,String>("Country") new PropertyValueFactory<>("country")
); );
for (XMLParser.BoatXMLObject.Boat boat : StreamParser.getBoats()) { posCol.setCellValueFactory(
new PropertyValueFactory<>("position")
);
if (StreamParser.isRaceStarted()) {
data.addAll(StreamParser.getBoatsPos().values());
} else {
for (Yacht boat : StreamParser.getBoats().values()) {
boat.setPosition("-");
data.add(boat); data.add(boat);
} }
} }
public void setStage (Stage stage) { teamList.refresh();
this.stage = stage;
// posCol.setSortType(TableColumn.SortType.ASCENDING);
// teamList.getSortOrder().add(posCol);
// posCol.setSortable(false);
} }
} }
@@ -307,21 +307,21 @@ public class RaceViewController extends Thread{
} }
private String currentTimer() { private String currentTimer() {
String timerString = "0:00 minutes"; String timerString = "0:00";
if (StreamParser.getTimeSinceStart() > 0) { if (StreamParser.getTimeSinceStart() > 0) {
String timerMinute = Long.toString(StreamParser.getTimeSinceStart() / 60); String timerMinute = Long.toString(StreamParser.getTimeSinceStart() / 60);
String timerSecond = Long.toString(StreamParser.getTimeSinceStart() % 60); String timerSecond = Long.toString(StreamParser.getTimeSinceStart() % 60);
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
timerString = "-" + timerMinute + ":" + timerSecond + " minutes"; timerString = "-" + timerMinute + ":" + timerSecond;
} else { } else {
String timerMinute = Long.toString(-1 * StreamParser.getTimeSinceStart() / 60); String timerMinute = Long.toString(-1 * StreamParser.getTimeSinceStart() / 60);
String timerSecond = Long.toString(-1 * StreamParser.getTimeSinceStart() % 60); String timerSecond = Long.toString(-1 * StreamParser.getTimeSinceStart() % 60);
if (timerSecond.length() == 1) { if (timerSecond.length() == 1) {
timerSecond = "0" + timerSecond; timerSecond = "0" + timerSecond;
} }
timerString = timerMinute + ":" + timerSecond + " minutes"; timerString = timerMinute + ":" + timerSecond;
} }
return timerString; return timerString;
} }
-31
View File
@@ -21,10 +21,6 @@ public class Boat {
private int markLastPast; private int markLastPast;
private String shortName; private String shortName;
private int id; private int id;
// new attributes to boat
private int sourceID;
private String boatName;
private String country;
public Boat(String teamName) { public Boat(String teamName) {
this.teamName = teamName; this.teamName = teamName;
@@ -49,21 +45,6 @@ public class Boat {
this.id = id; this.id = id;
} }
/**
* New instance created by BoatsParser.
*
* @param sourceID source ID of the boat
* @param boatName full name of the boat
* @param shortName short name of the boat
* @param country country of the boat
*/
public Boat(int sourceID, String boatName, String shortName, String country) {
this.sourceID = sourceID;
this.boatName = boatName;
this.shortName = shortName;
this.country = country;
}
/** /**
* Returns the name of the team sailing the boat * Returns the name of the team sailing the boat
* *
@@ -159,16 +140,4 @@ public class Boat {
public int getId() { public int getId() {
return id; return id;
} }
public int getSourceID() {
return sourceID;
}
public String getBoatName() {
return boatName;
}
public String getCountry() {
return country;
}
} }
-2
View File
@@ -21,8 +21,6 @@ public class Event {
private final double ORIGIN_LON = -64.857063; private final double ORIGIN_LON = -64.857063;
private final double SCALE = 16000; private final double SCALE = 16000;
/** /**
* Event class containing the time of specific event, related team/boat, and * Event class containing the time of specific event, related team/boat, and
* event location such as leg. * event location such as leg.
+2 -2
View File
@@ -102,8 +102,8 @@ public class Race {
events.put(boat, new ArrayList<>(Arrays.asList(event))); events.put(boat, new ArrayList<>(Arrays.asList(event)));
} }
totalDistance += event.getDistanceBetweenMarks(); totalDistance += event.getDistanceBetweenMarks();
System.out.println(totalDistance); //System.out.println(totalDistance);
System.out.println(boat.getVelocity()); //System.out.println(boat.getVelocity());
} }
// There are no more marks after this event // There are no more marks after this event
+114
View File
@@ -0,0 +1,114 @@
package seng302.models;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
/**
* Yacht class for the racing boat.
*
* 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.
*/
public class Yacht {
private String boatType;
private Integer sourceID;
private String hullID; //matches HullNum in the XML spec.
private String shortName;
private String boatName;
private String country;
// Boat status
private Integer boatStatus;
private Integer legNumber;
private Integer penaltiesAwarded;
private Integer penaltiesServed;
private Long estimateTimeAtNextMark;
private Long estimateTimeAtFinish;
private String position;
public Yacht(String boatType, Integer sourceID, String hullID, String shortName, String boatName, String country) {
this.boatType = boatType;
this.sourceID = sourceID;
this.hullID = hullID;
this.shortName = shortName;
this.boatName = boatName;
this.country = country;
}
public String getBoatType() {
return boatType;
}
public Integer getSourceID() {
return sourceID;
}
public String getHullID() {
return hullID;
}
public String getShortName() {
return shortName;
}
public String getBoatName() {
return boatName;
}
public String getCountry() {
return country;
}
public Integer getBoatStatus() {
return boatStatus;
}
public void setBoatStatus(Integer boatStatus) {
this.boatStatus = boatStatus;
}
public Integer getLegNumber() {
return legNumber;
}
public void setLegNumber(Integer 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 Long getEstimateTimeAtNextMark() {
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
// return format.format(estimateTimeAtNextMark);
return estimateTimeAtNextMark;
}
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
this.estimateTimeAtNextMark = estimateTimeAtNextMark;
}
public String getEstimateTimeAtFinish() {
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
return format.format(estimateTimeAtFinish);
}
public void setEstimateTimeAtFinish(Long estimateTimeAtFinish) {
this.estimateTimeAtFinish = estimateTimeAtFinish;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
@@ -1,77 +0,0 @@
package seng302.models.parsers;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import seng302.models.Boat;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Created by ryan_ on 30/04/2017.
*/
public class BoatsParser extends FileParser {
private Document doc;
public BoatsParser(String xmlString) {
this.doc = this.parseFile(xmlString);
}
/**
* Create a boat instance from a given node if 'Type' is 'Yacht'
*
* @param node a boat node
* @return an instance of Boat
*/
private Boat parseBoat(Node node) {
try {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
if (element.getAttribute("Type").equals("Yacht")) {
String sourceID = element.getAttribute("SourceID");
String boatName = element.getAttribute("BoatName");
String shortName = element.getAttribute("ShortName");
String stoweName = element.getAttribute("StoweName");
String country = element.getAttribute("Country");
Boat boat = new Boat(Integer.parseInt(sourceID), boatName, shortName, country);
return boat;
}
} else {
throw new NoSuchElementException("Cannot generate a boat by given node");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Returns a list of boats from the xml.
*
* @return a list of boats
*/
public List<Boat> getBoats() {
ArrayList<Boat> boats = new ArrayList<>();
try {
NodeList nodes = this.doc.getElementsByTagName("Boat");
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
Boat boat = parseBoat(node);
if (!(boat == null)) {
boats.add(boat);
}
}
return boats;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
@@ -5,6 +5,7 @@ import javafx.geometry.Point3D;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import seng302.models.Yacht;
import seng302.models.parsers.packets.BoatPositionPacket; import seng302.models.parsers.packets.BoatPositionPacket;
import seng302.models.parsers.packets.StreamPacket; import seng302.models.parsers.packets.StreamPacket;
@@ -37,7 +38,8 @@ public class StreamParser extends Thread{
private static boolean raceFinished = false; private static boolean raceFinished = false;
private static boolean streamStatus = false; private static boolean streamStatus = false;
private static long timeSinceStart = -1; private static long timeSinceStart = -1;
private static List<XMLParser.BoatXMLObject.Boat> boats = new ArrayList<>(); private static Map<Integer, Yacht> boats = new HashMap<>();
private static Map<Long, Yacht> boatsPos = new TreeMap<>();
private static double windDirection = 0; private static double windDirection = 0;
private static String currentTimeString; private static String currentTimeString;
@@ -57,7 +59,7 @@ public class StreamParser extends Thread{
*/ */
public void run(){ public void run(){
try { try {
System.out.println("START OF STREAM"); System.out.println("[CLIENT] Start of stream");
streamStatus = true; streamStatus = true;
xmlObject = new XMLParser(); xmlObject = new XMLParser();
while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) { while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) {
@@ -94,7 +96,7 @@ public class StreamParser extends Thread{
* *
*/ */
public void start () { public void start () {
System.out.println("Starting " + threadName ); System.out.println("[CLIENT] Starting " + threadName );
if (t == null) { if (t == null) {
t = new Thread (this, threadName); t = new Thread (this, threadName);
t.start (); t.start ();
@@ -181,18 +183,18 @@ public class StreamParser extends Thread{
currentTimeString = format.format((new Date (currentTime)).getTime()); currentTimeString = format.format((new Date (currentTime)).getTime());
if (timeTillStart > 0) { if (timeTillStart > 0) {
timeSinceStart = timeTillStart; timeSinceStart = timeTillStart;
System.out.println("Time till start: " + timeTillStart + " Seconds"); //System.out.println("Time till start: " + timeTillStart + " Seconds");
} else { } else {
if (raceStatus == 4 || raceStatus == 8){ if (raceStatus == 4 || raceStatus == 8){
raceFinished = true; raceFinished = true;
raceStarted = false; raceStarted = false;
System.out.println("RACE HAS FINISHED"); System.out.println("[CLIENT] Race has finished");
} else if (!raceStarted){ } else if (!raceStarted){
raceStarted = true; raceStarted = true;
raceFinished = false; raceFinished = false;
System.out.println("RACE HAS STARTED"); System.out.println("[CLIENT] Race has started");
} }
System.out.println("Time since start: " + -1 * timeTillStart + " Seconds"); //System.out.println("Time since start: " + -1 * timeTillStart + " Seconds");
timeSinceStart = timeTillStart; timeSinceStart = timeTillStart;
} }
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20)); long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
@@ -201,17 +203,33 @@ public class StreamParser extends Thread{
long windSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22)); long windSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
int noBoats = payload[22]; int noBoats = payload[22];
int raceType = payload[23]; int raceType = payload[23];
ArrayList<String> boatStatuses = new ArrayList<>(); // ArrayList<String> boatStatuses = new ArrayList<>();
boatsPos = new TreeMap<>();
for (int i = 0; i < noBoats; i++){ for (int i = 0; i < noBoats; i++){
Long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); Long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20)));
String boatStatus = "SourceID: " + boatStatusSourceID; Yacht boat = boats.get((int)(long) boatStatusSourceID);
boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)]; boat.setBoatStatus((int)payload[28 + (i * 20)]);
boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)]; boat.setLegNumber((int)payload[29 + (i * 20)]);
boatStatus += "\nPenaltiesAwarded: " + (int)payload[29 + (i * 20)]; boat.setPenaltiesAwarded((int)payload[29 + (i * 20)]);
boatStatus += "\nPenaltiesServed: " + (int)payload[30 + (i * 20)]; boat.setPenaltiesServed((int)payload[30 + (i * 20)]);
boatStatus += "\nEstTimeAtNextMark: " + bytesToLong(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20))); Long estTimeAtNextMark = bytesToLong(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20)));
boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20))); boat.setEstimateTimeAtNextMark(estTimeAtNextMark);
boatStatuses.add(boatStatus); Long estTimeAtFinish = bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (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);
}
int pos = 1;
for (Yacht yacht : boatsPos.values()) {
yacht.setPosition(String.valueOf(pos));
pos++;
} }
} }
@@ -478,11 +496,11 @@ public class StreamParser extends Thread{
} }
/** /**
* return list of boats from the server * return a map of boats with sourceID and the boat
* *
* @return list of boats * @return map of boats
*/ */
public static List<XMLParser.BoatXMLObject.Boat> getBoats() { public static Map<Integer, Yacht> getBoats() {
return boats; return boats;
} }
@@ -513,5 +531,14 @@ public class StreamParser extends Thread{
public static String getCurrentTimeString() { public static String getCurrentTimeString() {
return currentTimeString; 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<Long, Yacht> getBoatsPos() {
return boatsPos;
}
} }
@@ -24,6 +24,7 @@ public class StreamReceiver extends Thread {
public StreamReceiver(String hostAddress, int hostPort, String threadName) { public StreamReceiver(String hostAddress, int hostPort, String threadName) {
this.threadName = threadName; this.threadName = threadName;
this.setDaemon(true);
try { try {
host = new Socket(hostAddress, hostPort); host = new Socket(hostAddress, hostPort);
} catch (IOException e) { } catch (IOException e) {
@@ -44,7 +45,7 @@ public class StreamReceiver extends Thread {
} }
public void start () { public void start () {
System.out.println("Starting " + threadName ); System.out.println("[CLIENT] Starting " + threadName );
if (t == null) { if (t == null) {
t = new Thread (this, threadName); t = new Thread (this, threadName);
t.start (); t.start ();
@@ -95,7 +96,6 @@ public class StreamReceiver extends Thread {
} catch (Exception e) { } catch (Exception e) {
moreBytes = false; moreBytes = false;
} }
} }
} }
@@ -4,9 +4,12 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import seng302.models.Yacht;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Class to create an XML object from the XML Packet Messages. * Class to create an XML object from the XML Packet Messages.
@@ -392,9 +395,9 @@ public class XMLParser {
private ArrayList<Double> zoneLimits;// will only contain 5 elements. Limits 1-5 private ArrayList<Double> zoneLimits;// will only contain 5 elements. Limits 1-5
//Boats //Boats
ArrayList<Boat> boats; ArrayList<Yacht> boats;
//Competing boats //Competing boats
List<Boat> competingBoats = new ArrayList<>(); Map<Integer, Yacht> competingBoats = new HashMap<>();
/** /**
* Constructor for a BoatXMLObject. * Constructor for a BoatXMLObject.
@@ -427,10 +430,16 @@ public class XMLParser {
for (int i = 0; i < boatsList.getLength(); i++) { for (int i = 0; i < boatsList.getLength(); i++) {
Node currentBoat = boatsList.item(i); Node currentBoat = boatsList.item(i);
if (currentBoat.getNodeName().equals("Boat")) { if (currentBoat.getNodeName().equals("Boat")) {
Boat boat = new Boat(currentBoat); // 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); this.boats.add(boat);
if (boat.getBoatType().equals("Yacht")) { if (boat.getBoatType().equals("Yacht")) {
competingBoats.add(boat); competingBoats.put(boat.getSourceID(), boat);
} }
} }
//System.out.println(this.getBoats()); //System.out.println(this.getBoats());
@@ -446,37 +455,37 @@ public class XMLParser {
public Double getMarkZoneSize() { return markZoneSize; } public Double getMarkZoneSize() { return markZoneSize; }
public Double getCourseZoneSize() { return courseZoneSize; } public Double getCourseZoneSize() { return courseZoneSize; }
public ArrayList<Double> getZoneLimits() { return zoneLimits; } public ArrayList<Double> getZoneLimits() { return zoneLimits; }
public ArrayList<Boat> getBoats() { return boats; } public ArrayList<Yacht> getBoats() { return boats; }
public List<Boat> getCompetingBoats() { public Map<Integer, Yacht> getCompetingBoats() {
return competingBoats; return competingBoats;
} }
public class Boat { // public class Boat {
//
private String boatType; // private String boatType;
private Integer sourceID; // private Integer sourceID;
private String hullID; //matches HullNum in the XML spec. // private String hullID; //matches HullNum in the XML spec.
private String shortName; // private String shortName;
private String boatName; // private String boatName;
private String country; // private String country;
//
Boat(Node boatNode) { // Boat(Node boatNode) {
this.boatType = getNodeAttributeString(boatNode, "Type"); // this.boatType = getNodeAttributeString(boatNode, "Type");
this.sourceID = getNodeAttributeInt(boatNode, "SourceID"); // this.sourceID = getNodeAttributeInt(boatNode, "SourceID");
this.hullID = getNodeAttributeString(boatNode, "HullNum"); // this.hullID = getNodeAttributeString(boatNode, "HullNum");
this.shortName = getNodeAttributeString(boatNode, "ShortName"); // this.shortName = getNodeAttributeString(boatNode, "ShortName");
this.boatName = getNodeAttributeString(boatNode, "BoatName"); // this.boatName = getNodeAttributeString(boatNode, "BoatName");
this.country = getNodeAttributeString(boatNode, "Country"); // this.country = getNodeAttributeString(boatNode, "Country");
} // }
//
public String getBoatType() { return boatType; } // public String getBoatType() { return boatType; }
public Integer getSourceID() { return sourceID; } // public Integer getSourceID() { return sourceID; }
public String getHullID() { return hullID; } // public String getHullID() { return hullID; }
public String getShortName() { return shortName; } // public String getShortName() { return shortName; }
public String getBoatName() { return boatName; } // public String getBoatName() { return boatName; }
public String getCountry() { return country; } // public String getCountry() { return country; }
//
} // }
} }
+55 -22
View File
@@ -3,26 +3,22 @@ package seng302.server;
import seng302.server.messages.*; import seng302.server.messages.*;
import seng302.server.simulator.Boat; import seng302.server.simulator.Boat;
import seng302.server.simulator.Simulator; import seng302.server.simulator.Simulator;
import sun.misc.IOUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*; import java.util.*;
public class ServerThread implements Runnable, Observer { public class ServerThread implements Runnable, Observer {
private Thread runner;
private StreamingServerSocket server; private StreamingServerSocket server;
private long startTime; private long startTime;
boolean raceStarted = false; private boolean raceStarted = false;
Map<Integer,Boolean> boatsFinished = new HashMap<>(); private Map<Integer,Boolean> boatsFinished = new HashMap<>();
private List<Boat> boats; private List<Boat> boats;
private Simulator raceSimulator; private Simulator raceSimulator;
private boolean sendingRaceFinishedLocationMessages = true;
private final int HEARTBEAT_PERIOD = 5000; private final int HEARTBEAT_PERIOD = 5000;
private final int RACE_STATUS_PERIOD = 1000; private final int RACE_STATUS_PERIOD = 1000/2;
private final int RACE_START_STATUS_PERIOD = 1000; private final int RACE_START_STATUS_PERIOD = 1000;
private final int BOAT_LOCATION_PERIOD = 1000/5; private final int BOAT_LOCATION_PERIOD = 1000/5;
private final int PORT_NUMBER = 8085; private final int PORT_NUMBER = 8085;
@@ -30,7 +26,9 @@ public class ServerThread implements Runnable, Observer {
private static final int LOG_LEVEL = 1; private static final int LOG_LEVEL = 1;
public ServerThread(String threadName){ public ServerThread(String threadName){
runner = new Thread(this, threadName); Thread runner = new Thread(this, threadName);
runner.setDaemon(true);
serverLog("Spawning Server", 0); serverLog("Spawning Server", 0);
raceSimulator = new Simulator(BOAT_LOCATION_PERIOD); raceSimulator = new Simulator(BOAT_LOCATION_PERIOD);
@@ -48,7 +46,7 @@ public class ServerThread implements Runnable, Observer {
raceSimulatorThread.start(); raceSimulatorThread.start();
} }
public static void serverLog(String message, int logLevel){ static void serverLog(String message, int logLevel){
if(logLevel <= LOG_LEVEL){ if(logLevel <= LOG_LEVEL){
System.out.println("[SERVER] " + message); System.out.println("[SERVER] " + message);
} }
@@ -60,7 +58,7 @@ public class ServerThread implements Runnable, Observer {
* @param type The XML Message type * @param type The XML Message type
* @return The XML Message * @return The XML Message
*/ */
public Message getXmlMessage(String fileName, XMLMessageSubType type){ private Message getXmlMessage(String fileName, XMLMessageSubType type){
String fileContents = null; String fileContents = null;
try { try {
@@ -82,8 +80,8 @@ public class ServerThread implements Runnable, Observer {
/** /**
* @return Get a race status message for the current race * @return Get a race status message for the current race
*/ */
public Message getRaceStatusMessage(){ private Message getRaceStatusMessage(){
List<BoatSubMessage> boatSubMessages = new ArrayList<BoatSubMessage>(); List<BoatSubMessage> boatSubMessages = new ArrayList<>();
BoatStatus boatStatus; BoatStatus boatStatus;
RaceStatus raceStatus; RaceStatus raceStatus;
boolean thereAreBoatsNotFinished = false; boolean thereAreBoatsNotFinished = false;
@@ -128,7 +126,7 @@ public class ServerThread implements Runnable, Observer {
raceStatus = RaceStatus.TERMINATED; raceStatus = RaceStatus.TERMINATED;
} }
return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.EAST, return new RaceStatusMessage(1, raceStatus, startTime, WindDirection.SOUTH,
100, boats.size(), RaceType.MATCH_RACE, 1, boatSubMessages); 100, boats.size(), RaceType.MATCH_RACE, 1, boatSubMessages);
} }
@@ -256,12 +254,37 @@ public class ServerThread implements Runnable, Observer {
startSendingRaceStatusMessages(); startSendingRaceStatusMessages();
} }
/**
* Start sending static boat position updates when race has finished
*/
private void startSendingRaceFinishedBoatPostions(){
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
try {
for (Boat b : raceSimulator.getBoats()){
Message m = new BoatLocationMessage(b.getSourceID(), server.getSequenceNumber(), b.getLat(),
b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(),
((long) 0));
server.send(m);
}
} catch (IOException e) {
System.out.print("");
}
}
}, 0, BOAT_LOCATION_PERIOD);
}
/** /**
* Send a boat location message when they are updated by the simulator * Send a boat location message when they are updated by the simulator
* @param o . * @param o .
* @param arg . * @param arg .
*/ */
@Override @Override
@SuppressWarnings("unchecked")
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
// Only send if server started // Only send if server started
// TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17 // TODO: I don't understand why i need to check server is null or not ... confused - haoming 2/5/17
@@ -269,22 +292,32 @@ public class ServerThread implements Runnable, Observer {
return; return;
} }
for (Boat b : (List<Boat>) arg){ int numOfBoatsFinished = 0;
for (Boat boat : (List<Boat>) arg){
try { try {
Message m = new BoatLocationMessage(b.getSourceID(), 1, b.getLat(), if (boat.isFinished()) {
b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(), numOfBoatsFinished ++;
((long) b.getSpeed())); if (!boatsFinished.get(boat.getSourceID())) {
boatsFinished.put(boat.getSourceID(), true);
serverLog("Boat " + boat.getSourceID() + " finished the race", 1);
}
}
Message m = new BoatLocationMessage(boat.getSourceID(), 1, boat.getLat(),
boat.getLng(), boat.getLastPassedCorner().getBearingToNextCorner(),
((long) boat.getSpeed()));
server.send(m); server.send(m);
} catch (IOException e) { } catch (IOException e) {
serverLog("Couldn't send a boat status message", 3); serverLog("Couldn't send a boat status message", 3);
return; return;
} }
catch (NullPointerException e){ catch (NullPointerException e){
//e.printStackTrace(); e.printStackTrace();
//TODO: add a method in boat to check if a boat has finished the race. - haoming 2/5/17
serverLog("Boat " + b.getSourceID() + " finished the race", 1);
boatsFinished.put(b.getSourceID(), true);
} }
} }
if (numOfBoatsFinished == ((List<Boat>) arg).size()) {
startSendingRaceFinishedBoatPostions();
}
} }
} }
@@ -42,6 +42,7 @@ public class BoatLocationMessage extends Message {
* @param boatSpeed The boats speed * @param boatSpeed The boats speed
*/ */
public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, double heading, long boatSpeed){ public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, double heading, long boatSpeed){
boatSpeed /= 10;
messageVersionNumber = 1; messageVersionNumber = 1;
time = System.currentTimeMillis() / 1000L; time = System.currentTimeMillis() / 1000L;
this.sourceId = sourceId; this.sourceId = sourceId;
@@ -54,8 +55,8 @@ public class BoatLocationMessage extends Message {
this.pitch = 0; this.pitch = 0;
this.roll = 0; this.roll = 0;
this.boatSpeed = boatSpeed; this.boatSpeed = boatSpeed;
this.COG = 0; this.COG = 2;
this.SOG = 0; this.SOG = boatSpeed ;
this.apparentWindSpeed = 0; this.apparentWindSpeed = 0;
this.apparentWindAngle = 0; this.apparentWindAngle = 0;
this.trueWindSpeed = 0; this.trueWindSpeed = 0;
@@ -146,7 +147,7 @@ public class BoatLocationMessage extends Message {
putInt(headingToSend, 2); putInt(headingToSend, 2);
putInt((int) pitch, 2); putInt((int) pitch, 2);
putInt((int) roll, 2); putInt((int) roll, 2);
putUnsignedInt((int) boatSpeed, 2); putInt((int) boatSpeed, 2);
putUnsignedInt((int) COG, 2); putUnsignedInt((int) COG, 2);
putUnsignedInt((int) SOG, 2); putUnsignedInt((int) SOG, 2);
putUnsignedInt((int) apparentWindSpeed, 2); putUnsignedInt((int) apparentWindSpeed, 2);
@@ -1,13 +1,9 @@
package seng302.server.messages; package seng302.server.messages;
import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
@@ -185,7 +181,6 @@ public abstract class Message {
* @return * @return
*/ */
public static byte[] intToByteArray(long val, int len){ public static byte[] intToByteArray(long val, int len){
long vor = val;
int index = 0; int index = 0;
byte[] data = new byte[len]; byte[] data = new byte[len];
@@ -209,5 +204,4 @@ public abstract class Message {
data[right] = (byte) (temp & 0xff); data[right] = (byte) (temp & 0xff);
} }
} }
} }
@@ -10,12 +10,14 @@ public class Boat {
private double lng; private double lng;
private double speed; // in mm/sec private double speed; // in mm/sec
private String boatName, shortName, shorterName; private String boatName, shortName, shorterName;
private boolean isFinished;
private Corner lastPassedCorner, headingCorner; private Corner lastPassedCorner, headingCorner;
public Boat(int sourceID, String boatName) { public Boat(int sourceID, String boatName) {
this.sourceID = sourceID; this.sourceID = sourceID;
this.boatName = boatName; this.boatName = boatName;
this.isFinished = false;
} }
/** /**
@@ -106,4 +108,12 @@ public class Boat {
public void setHeadingCorner(Corner headingCorner) { public void setHeadingCorner(Corner headingCorner) {
this.headingCorner = headingCorner; this.headingCorner = headingCorner;
} }
public boolean isFinished() {
return isFinished;
}
public void setFinished(boolean finished) {
isFinished = finished;
}
} }
@@ -37,7 +37,7 @@ public class Simulator extends Observable implements Runnable {
boat.setLng(startLng); boat.setLng(startLng);
boat.setLastPassedCorner(course.get(0)); boat.setLastPassedCorner(course.get(0));
boat.setHeadingCorner(course.get(1)); boat.setHeadingCorner(course.get(1));
boat.setSpeed(ThreadLocalRandom.current().nextInt(400000, 600000 + 1)); boat.setSpeed(ThreadLocalRandom.current().nextInt(40000, 60000 + 1));
} }
} }
@@ -65,6 +65,8 @@ public class Simulator extends Observable implements Runnable {
e.printStackTrace(); e.printStackTrace();
} }
} }
System.out.println("[SERVER] Race simulator has been terminated");
} }
/** /**
@@ -89,7 +91,10 @@ public class Simulator extends Observable implements Runnable {
boat.setHeadingCorner(boat.getLastPassedCorner().getNextCorner()); boat.setHeadingCorner(boat.getLastPassedCorner().getNextCorner());
// heading corner == null means boat has reached the final mark // heading corner == null means boat has reached the final mark
if (boat.getHeadingCorner() == null) return 1; if (boat.getHeadingCorner() == null) {
boat.setFinished(true);
return 1;
}
// move compensate distance for the mark just passed // move compensate distance for the mark just passed
Position pos = GeoUtility.getGeoCoordinate( Position pos = GeoUtility.getGeoCoordinate(
+14 -14
View File
@@ -7,21 +7,21 @@
<?import java.lang.*?> <?import java.lang.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<AnchorPane fx:id="contentPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.Controller"> <AnchorPane fx:id="contentPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.Controller">
<children> <children>
<GridPane nodeOrientation="LEFT_TO_RIGHT" prefHeight="1080.0" prefWidth="1920.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <GridPane nodeOrientation="LEFT_TO_RIGHT" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="170.0" minHeight="170.0" prefHeight="170.0" vgrow="SOMETIMES" /> <RowConstraints percentHeight="10.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="52.0" minHeight="52.0" prefHeight="52.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="52.0" minHeight="52.0" prefHeight="52.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="102.0" minHeight="102.0" prefHeight="102.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="0.0" percentHeight="8.0" prefHeight="0.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="28.0" minHeight="20.0" prefHeight="28.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="28.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="55.0" minHeight="55.0" prefHeight="55.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="55.0" minHeight="55.0" percentHeight="5.0" prefHeight="55.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="318.0" minHeight="318.0" prefHeight="318.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="0.0" minHeight="0.0" percentHeight="23.0" prefHeight="0.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="93.0" minHeight="93.0" prefHeight="93.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="93.0" minHeight="72.0" prefHeight="72.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="262.0" minHeight="262.0" prefHeight="262.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="283.0" minHeight="262.0" prefHeight="283.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<Label alignment="CENTER" text="Welcome to Race Vision" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM"> <Label alignment="CENTER" text="Welcome to Race Vision" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
@@ -46,17 +46,17 @@
</Label> </Label>
<Button fx:id="streamButton" mnemonicParsing="false" onAction="#startStream" text="Click to stream" GridPane.halignment="CENTER" GridPane.rowIndex="4" /> <Button fx:id="streamButton" mnemonicParsing="false" onAction="#startStream" text="Click to stream" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
<Button fx:id="switchToRaceViewButton" disable="true" mnemonicParsing="false" onAction="#switchToRaceView" text="Watch Race" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="TOP" /> <Button fx:id="switchToRaceViewButton" disable="true" mnemonicParsing="false" onAction="#switchToRaceView" text="Watch Race" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="TOP" />
<TableView fx:id="teamList" maxWidth="500.0" prefHeight="200.0" prefWidth="200.0" GridPane.halignment="CENTER" GridPane.rowIndex="5"> <TableView fx:id="teamList" maxWidth="500.0" prefHeight="200.0" prefWidth="210.0" GridPane.halignment="CENTER" GridPane.rowIndex="5">
<columns> <columns>
<TableColumn fx:id="boatNameCol" editable="false" prefWidth="250.0" sortable="false" text="Boat Name" /> <TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
<TableColumn fx:id="shortNameCol" editable="false" prefWidth="125.0" sortable="false" text="Short Name" /> <TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
<TableColumn fx:id="countryCol" editable="false" prefWidth="125.0" sortable="false" text="Country" /> <TableColumn fx:id="shortNameCol" editable="false" maxWidth="107.0" minWidth="107.0" prefWidth="107.0" resizable="false" sortable="false" text="Short Name" />
<TableColumn fx:id="countryCol" editable="false" maxWidth="147.0" minWidth="147.0" prefWidth="147.0" resizable="false" sortable="false" text="Country" />
</columns> </columns>
<GridPane.margin> <GridPane.margin>
<Insets /> <Insets />
</GridPane.margin> </GridPane.margin>
</TableView> </TableView>
<Label text="*Team position in table do not correspond to race position" GridPane.halignment="CENTER" GridPane.rowIndex="6" GridPane.valignment="TOP" />
<Label fx:id="realTime" text="Local time" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="BOTTOM" /> <Label fx:id="realTime" text="Local time" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="BOTTOM" />
</children> </children>
</GridPane> </GridPane>
+1 -1
View File
@@ -60,7 +60,7 @@
<Label layoutX="10.0" layoutY="499.0" text="Annotations" /> <Label layoutX="10.0" layoutY="499.0" text="Annotations" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane fx:id="contentAnchorPane" prefHeight="960.0" prefWidth="1280.0" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP"> <AnchorPane fx:id="contentAnchorPane" prefHeight="960.0" prefWidth="1280.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
<children> <children>
<fx:include fx:id="includedCanvas" source="CanvasView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <fx:include fx:id="includedCanvas" source="CanvasView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children></AnchorPane> </children></AnchorPane>