diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index c2a6b129..742b6032 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -1,11 +1,11 @@ package seng302; import javafx.application.Application; +import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; -import seng302.controllers.Controller; import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamReceiver; import seng302.server.ServerThread; @@ -14,31 +14,47 @@ public class App extends Application { @Override public void start(Stage primaryStage) throws Exception { -// Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); - FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/MainView.fxml")); + Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); primaryStage.setTitle("RaceVision"); - primaryStage.setScene(new Scene(loader.load())); - ((Controller) loader.getController()).setStage(primaryStage); + primaryStage.setScene(new Scene(root)); + primaryStage.setMaximized(true); + primaryStage.show(); + primaryStage.setOnCloseRequest(e -> { + Platform.exit(); + }); + } public static void main(String[] args) { - StreamReceiver sr; + StreamReceiver sr = null; new ServerThread("Racevision Test Server"); + try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } - if (args.length > 1){ - sr = new StreamReceiver("localhost", 8085, "RaceStream"); + 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"); + 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{ - sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"RaceStream"); -// sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); -// sr = new StreamReceiver("localhost", 8085, "RaceStream"); + sr = new StreamReceiver("localhost", 8085, "RaceStream"); } sr.start(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 6fd7d592..d2c04e3c 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -135,7 +135,7 @@ public class CanvasController { } }; for (Mark m : raceViewController.getRace().getCourse()) { - System.out.println(m.getName()); + //System.out.println(m.getName()); } //timer.start(); } diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index 1a4adecd..97ee324e 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -3,11 +3,9 @@ package seng302.controllers; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; -import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; @@ -16,21 +14,17 @@ import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; -import javafx.stage.Stage; +import seng302.models.Yacht; import seng302.models.parsers.StreamParser; import seng302.models.parsers.XMLParser; -import javax.xml.crypto.dsig.XMLObject; import java.io.IOException; import java.net.URL; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; -/** - * Created by michaelrausch on 21/03/17. - */ public class Controller implements Initializable { @FXML private AnchorPane contentPane; @@ -41,26 +35,25 @@ public class Controller implements Initializable { @FXML private Button switchToRaceViewButton; @FXML - private TableView teamList; + private TableView teamList; @FXML - private TableColumn boatNameCol; + private TableColumn boatNameCol; @FXML - private TableColumn shortNameCol; + private TableColumn shortNameCol; @FXML - private TableColumn countryCol; + private TableColumn countryCol; + @FXML + private TableColumn posCol; @FXML private Label realTime; - private Stage stage; + + private XMLParser xmlParser; private void setContentPane(String jfxUrl){ try{ contentPane.getChildren().removeAll(); contentPane.getChildren().clear(); -// 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); + contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); } catch(javafx.fxml.LoadException e){ System.err.println(e.getCause()); @@ -82,7 +75,7 @@ public class Controller implements Initializable { */ public void startStream() { if (StreamParser.isStreamStatus()) { - XMLParser xmlParser = StreamParser.getXmlObject(); + xmlParser = StreamParser.getXmlObject(); streamButton.setVisible(false); realTime.setVisible(true); timeTillLive.setVisible(true); @@ -108,7 +101,7 @@ public class Controller implements Initializable { if (timerSecond.length() == 1) { timerSecond = "0" + timerSecond; } - String timerString = "-" + timerMinute + ":" + timerSecond + " minutes"; + String timerString = "-" + timerMinute + ":" + timerSecond; timeTillLive.setText(timerString); } else { realTime.setText(StreamParser.getCurrentTimeString()); @@ -120,7 +113,7 @@ public class Controller implements Initializable { if (timerSecond.length() == 1) { timerSecond = "0" + timerSecond; } - String timerString = timerMinute + ":" + timerSecond + " minutes"; + String timerString = timerMinute + ":" + timerSecond; timeTillLive.setText(timerString); } }); @@ -137,22 +130,32 @@ public class Controller implements Initializable { } private void updateTeamList() { - ObservableList data = FXCollections.observableArrayList(); + ObservableList data = FXCollections.observableArrayList(); teamList.setItems(data); boatNameCol.setCellValueFactory( - new PropertyValueFactory("BoatName") + new PropertyValueFactory<>("boatName") ); shortNameCol.setCellValueFactory( - new PropertyValueFactory("ShortName") + new PropertyValueFactory<>("shortName") ); countryCol.setCellValueFactory( - new PropertyValueFactory("Country") + new PropertyValueFactory<>("country") ); - for (XMLParser.BoatXMLObject.Boat boat : StreamParser.getBoats()) { - data.add(boat); + 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); + } } - } - public void setStage (Stage stage) { - this.stage = stage; + teamList.refresh(); + +// posCol.setSortType(TableColumn.SortType.ASCENDING); +// teamList.getSortOrder().add(posCol); +// posCol.setSortable(false); } } diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index e38c9454..4bcb9079 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -307,21 +307,21 @@ public class RaceViewController extends Thread{ } private String currentTimer() { - String timerString = "0:00 minutes"; + String timerString = "0:00"; if (StreamParser.getTimeSinceStart() > 0) { String timerMinute = Long.toString(StreamParser.getTimeSinceStart() / 60); String timerSecond = Long.toString(StreamParser.getTimeSinceStart() % 60); if (timerSecond.length() == 1) { timerSecond = "0" + timerSecond; } - timerString = "-" + timerMinute + ":" + timerSecond + " minutes"; + timerString = "-" + timerMinute + ":" + timerSecond; } else { String timerMinute = Long.toString(-1 * StreamParser.getTimeSinceStart() / 60); String timerSecond = Long.toString(-1 * StreamParser.getTimeSinceStart() % 60); if (timerSecond.length() == 1) { timerSecond = "0" + timerSecond; } - timerString = timerMinute + ":" + timerSecond + " minutes"; + timerString = timerMinute + ":" + timerSecond; } return timerString; } diff --git a/src/main/java/seng302/models/Boat.java b/src/main/java/seng302/models/Boat.java index dc3aa7b7..d275091f 100644 --- a/src/main/java/seng302/models/Boat.java +++ b/src/main/java/seng302/models/Boat.java @@ -21,10 +21,6 @@ public class Boat { private int markLastPast; private String shortName; private int id; - // new attributes to boat - private int sourceID; - private String boatName; - private String country; public Boat(String teamName) { this.teamName = teamName; @@ -49,21 +45,6 @@ public class Boat { 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 * @@ -159,16 +140,4 @@ public class Boat { public int getId() { return id; } - - public int getSourceID() { - return sourceID; - } - - public String getBoatName() { - return boatName; - } - - public String getCountry() { - return country; - } } \ No newline at end of file diff --git a/src/main/java/seng302/models/Event.java b/src/main/java/seng302/models/Event.java index df298741..3cef1e6b 100644 --- a/src/main/java/seng302/models/Event.java +++ b/src/main/java/seng302/models/Event.java @@ -21,8 +21,6 @@ public class Event { private final double ORIGIN_LON = -64.857063; private final double SCALE = 16000; - - /** * Event class containing the time of specific event, related team/boat, and * event location such as leg. diff --git a/src/main/java/seng302/models/Race.java b/src/main/java/seng302/models/Race.java index fc7ae0ef..67988cf5 100644 --- a/src/main/java/seng302/models/Race.java +++ b/src/main/java/seng302/models/Race.java @@ -102,8 +102,8 @@ public class Race { events.put(boat, new ArrayList<>(Arrays.asList(event))); } totalDistance += event.getDistanceBetweenMarks(); - System.out.println(totalDistance); - System.out.println(boat.getVelocity()); + //System.out.println(totalDistance); + //System.out.println(boat.getVelocity()); } // There are no more marks after this event diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java new file mode 100644 index 00000000..9a8959c1 --- /dev/null +++ b/src/main/java/seng302/models/Yacht.java @@ -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; + } +} diff --git a/src/main/java/seng302/models/parsers/BoatsParser.java b/src/main/java/seng302/models/parsers/BoatsParser.java deleted file mode 100644 index 8180bde8..00000000 --- a/src/main/java/seng302/models/parsers/BoatsParser.java +++ /dev/null @@ -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 getBoats() { - ArrayList 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; - } - } -} diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index 40ef23aa..642fc608 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -5,6 +5,7 @@ import javafx.geometry.Point3D; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import seng302.models.Yacht; import seng302.models.parsers.packets.BoatPositionPacket; import seng302.models.parsers.packets.StreamPacket; @@ -37,7 +38,8 @@ public class StreamParser extends Thread{ private static boolean raceFinished = false; private static boolean streamStatus = false; private static long timeSinceStart = -1; - private static List boats = new ArrayList<>(); + private static Map boats = new HashMap<>(); + private static Map boatsPos = new TreeMap<>(); private static double windDirection = 0; private static String currentTimeString; @@ -57,7 +59,7 @@ public class StreamParser extends Thread{ */ public void run(){ try { - System.out.println("START OF STREAM"); + System.out.println("[CLIENT] Start of stream"); streamStatus = true; xmlObject = new XMLParser(); while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) { @@ -94,7 +96,7 @@ public class StreamParser extends Thread{ * */ public void start () { - System.out.println("Starting " + threadName ); + System.out.println("[CLIENT] Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); @@ -181,18 +183,18 @@ public class StreamParser extends Thread{ currentTimeString = format.format((new Date (currentTime)).getTime()); if (timeTillStart > 0) { timeSinceStart = timeTillStart; - System.out.println("Time till start: " + timeTillStart + " Seconds"); + //System.out.println("Time till start: " + timeTillStart + " Seconds"); } else { if (raceStatus == 4 || raceStatus == 8){ raceFinished = true; raceStarted = false; - System.out.println("RACE HAS FINISHED"); + System.out.println("[CLIENT] Race has finished"); } else if (!raceStarted){ raceStarted = true; 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; } 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)); int noBoats = payload[22]; int raceType = payload[23]; - ArrayList boatStatuses = new ArrayList<>(); +// ArrayList boatStatuses = new ArrayList<>(); + boatsPos = new TreeMap<>(); for (int i = 0; i < noBoats; i++){ Long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); - 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); + Yacht boat = boats.get((int)(long) boatStatusSourceID); + boat.setBoatStatus((int)payload[28 + (i * 20)]); + boat.setLegNumber((int)payload[29 + (i * 20)]); + boat.setPenaltiesAwarded((int)payload[29 + (i * 20)]); + boat.setPenaltiesServed((int)payload[30 + (i * 20)]); + Long estTimeAtNextMark = bytesToLong(Arrays.copyOfRange(payload,31 + (i * 20),37+ (i * 20))); + boat.setEstimateTimeAtNextMark(estTimeAtNextMark); + 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 getBoats() { + public static Map getBoats() { return boats; } @@ -513,5 +531,14 @@ public class StreamParser extends Thread{ 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 getBoatsPos() { + return boatsPos; + } } diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/parsers/StreamReceiver.java index 4c16991e..5eb66d78 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/parsers/StreamReceiver.java @@ -24,6 +24,7 @@ public class StreamReceiver extends Thread { public StreamReceiver(String hostAddress, int hostPort, String threadName) { this.threadName = threadName; + this.setDaemon(true); try { host = new Socket(hostAddress, hostPort); } catch (IOException e) { @@ -44,7 +45,7 @@ public class StreamReceiver extends Thread { } public void start () { - System.out.println("Starting " + threadName ); + System.out.println("[CLIENT] Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); @@ -95,7 +96,6 @@ public class StreamReceiver extends Thread { } catch (Exception e) { moreBytes = false; } - } } diff --git a/src/main/java/seng302/models/parsers/XMLParser.java b/src/main/java/seng302/models/parsers/XMLParser.java index e9f28a00..f8d30460 100644 --- a/src/main/java/seng302/models/parsers/XMLParser.java +++ b/src/main/java/seng302/models/parsers/XMLParser.java @@ -4,9 +4,12 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import seng302.models.Yacht; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Class to create an XML object from the XML Packet Messages. @@ -392,9 +395,9 @@ public class XMLParser { private ArrayList zoneLimits;// will only contain 5 elements. Limits 1-5 //Boats - ArrayList boats; + ArrayList boats; //Competing boats - List competingBoats = new ArrayList<>(); + Map competingBoats = new HashMap<>(); /** * Constructor for a BoatXMLObject. @@ -427,10 +430,16 @@ public class XMLParser { 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(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.add(boat); + competingBoats.put(boat.getSourceID(), boat); } } //System.out.println(this.getBoats()); @@ -446,37 +455,37 @@ public class XMLParser { public Double getMarkZoneSize() { return markZoneSize; } public Double getCourseZoneSize() { return courseZoneSize; } public ArrayList getZoneLimits() { return zoneLimits; } - public ArrayList getBoats() { return boats; } - public List getCompetingBoats() { + public ArrayList getBoats() { return boats; } + public Map getCompetingBoats() { return competingBoats; } - public class Boat { - - 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(Node boatNode) { - this.boatType = getNodeAttributeString(boatNode, "Type"); - this.sourceID = getNodeAttributeInt(boatNode, "SourceID"); - this.hullID = getNodeAttributeString(boatNode, "HullNum"); - this.shortName = getNodeAttributeString(boatNode, "ShortName"); - this.boatName = getNodeAttributeString(boatNode, "BoatName"); - this.country = getNodeAttributeString(boatNode, "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 class Boat { +// +// 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(Node boatNode) { +// this.boatType = getNodeAttributeString(boatNode, "Type"); +// this.sourceID = getNodeAttributeInt(boatNode, "SourceID"); +// this.hullID = getNodeAttributeString(boatNode, "HullNum"); +// this.shortName = getNodeAttributeString(boatNode, "ShortName"); +// this.boatName = getNodeAttributeString(boatNode, "BoatName"); +// this.country = getNodeAttributeString(boatNode, "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; } +// +// } } diff --git a/src/main/java/seng302/server/ServerThread.java b/src/main/java/seng302/server/ServerThread.java index 22f930d0..d3d6fe00 100644 --- a/src/main/java/seng302/server/ServerThread.java +++ b/src/main/java/seng302/server/ServerThread.java @@ -3,26 +3,22 @@ package seng302.server; import seng302.server.messages.*; import seng302.server.simulator.Boat; import seng302.server.simulator.Simulator; -import sun.misc.IOUtils; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.*; public class ServerThread implements Runnable, Observer { - private Thread runner; private StreamingServerSocket server; private long startTime; - boolean raceStarted = false; - Map boatsFinished = new HashMap<>(); + private boolean raceStarted = false; + private Map boatsFinished = new HashMap<>(); private List boats; private Simulator raceSimulator; + private boolean sendingRaceFinishedLocationMessages = true; 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 BOAT_LOCATION_PERIOD = 1000/5; private final int PORT_NUMBER = 8085; @@ -30,7 +26,9 @@ public class ServerThread implements Runnable, Observer { private static final int LOG_LEVEL = 1; public ServerThread(String threadName){ - runner = new Thread(this, threadName); + Thread runner = new Thread(this, threadName); + runner.setDaemon(true); + serverLog("Spawning Server", 0); raceSimulator = new Simulator(BOAT_LOCATION_PERIOD); @@ -48,7 +46,7 @@ public class ServerThread implements Runnable, Observer { raceSimulatorThread.start(); } - public static void serverLog(String message, int logLevel){ + static void serverLog(String message, int logLevel){ if(logLevel <= LOG_LEVEL){ System.out.println("[SERVER] " + message); } @@ -60,7 +58,7 @@ public class ServerThread implements Runnable, Observer { * @param type The XML Message type * @return The XML Message */ - public Message getXmlMessage(String fileName, XMLMessageSubType type){ + private Message getXmlMessage(String fileName, XMLMessageSubType type){ String fileContents = null; try { @@ -82,8 +80,8 @@ public class ServerThread implements Runnable, Observer { /** * @return Get a race status message for the current race */ - public Message getRaceStatusMessage(){ - List boatSubMessages = new ArrayList(); + private Message getRaceStatusMessage(){ + List boatSubMessages = new ArrayList<>(); BoatStatus boatStatus; RaceStatus raceStatus; boolean thereAreBoatsNotFinished = false; @@ -128,7 +126,7 @@ public class ServerThread implements Runnable, Observer { 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); } @@ -256,12 +254,37 @@ public class ServerThread implements Runnable, Observer { 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 * @param o . * @param arg . */ @Override + @SuppressWarnings("unchecked") public void update(Observable o, Object arg) { // 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 @@ -269,22 +292,32 @@ public class ServerThread implements Runnable, Observer { return; } - for (Boat b : (List) arg){ + int numOfBoatsFinished = 0; + for (Boat boat : (List) arg){ try { - Message m = new BoatLocationMessage(b.getSourceID(), 1, b.getLat(), - b.getLng(), b.getLastPassedCorner().getBearingToNextCorner(), - ((long) b.getSpeed())); + if (boat.isFinished()) { + numOfBoatsFinished ++; + 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); } catch (IOException e) { serverLog("Couldn't send a boat status message", 3); return; } catch (NullPointerException e){ - //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); + e.printStackTrace(); } } + + if (numOfBoatsFinished == ((List) arg).size()) { + startSendingRaceFinishedBoatPostions(); + } + } } diff --git a/src/main/java/seng302/server/messages/BoatLocationMessage.java b/src/main/java/seng302/server/messages/BoatLocationMessage.java index 2bffdc72..6cf0739d 100644 --- a/src/main/java/seng302/server/messages/BoatLocationMessage.java +++ b/src/main/java/seng302/server/messages/BoatLocationMessage.java @@ -42,6 +42,7 @@ public class BoatLocationMessage extends Message { * @param boatSpeed The boats speed */ public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, double heading, long boatSpeed){ + boatSpeed /= 10; messageVersionNumber = 1; time = System.currentTimeMillis() / 1000L; this.sourceId = sourceId; @@ -54,8 +55,8 @@ public class BoatLocationMessage extends Message { this.pitch = 0; this.roll = 0; this.boatSpeed = boatSpeed; - this.COG = 0; - this.SOG = 0; + this.COG = 2; + this.SOG = boatSpeed ; this.apparentWindSpeed = 0; this.apparentWindAngle = 0; this.trueWindSpeed = 0; @@ -146,7 +147,7 @@ public class BoatLocationMessage extends Message { putInt(headingToSend, 2); putInt((int) pitch, 2); putInt((int) roll, 2); - putUnsignedInt((int) boatSpeed, 2); + putInt((int) boatSpeed, 2); putUnsignedInt((int) COG, 2); putUnsignedInt((int) SOG, 2); putUnsignedInt((int) apparentWindSpeed, 2); diff --git a/src/main/java/seng302/server/messages/Message.java b/src/main/java/seng302/server/messages/Message.java index e7dd6f74..6f4c7d4b 100644 --- a/src/main/java/seng302/server/messages/Message.java +++ b/src/main/java/seng302/server/messages/Message.java @@ -1,13 +1,9 @@ package seng302.server.messages; -import java.io.DataOutputStream; import java.io.IOException; -import java.lang.reflect.Array; -import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.SocketChannel; -import java.util.ArrayList; import java.util.Arrays; import java.util.zip.CRC32; @@ -185,7 +181,6 @@ public abstract class Message { * @return */ public static byte[] intToByteArray(long val, int len){ - long vor = val; int index = 0; byte[] data = new byte[len]; @@ -209,5 +204,4 @@ public abstract class Message { data[right] = (byte) (temp & 0xff); } } - } diff --git a/src/main/java/seng302/server/simulator/Boat.java b/src/main/java/seng302/server/simulator/Boat.java index c5b821bf..e4096c55 100644 --- a/src/main/java/seng302/server/simulator/Boat.java +++ b/src/main/java/seng302/server/simulator/Boat.java @@ -10,12 +10,14 @@ public class Boat { private double lng; private double speed; // in mm/sec private String boatName, shortName, shorterName; + private boolean isFinished; private Corner lastPassedCorner, headingCorner; public Boat(int sourceID, String boatName) { this.sourceID = sourceID; this.boatName = boatName; + this.isFinished = false; } /** @@ -106,4 +108,12 @@ public class Boat { public void setHeadingCorner(Corner headingCorner) { this.headingCorner = headingCorner; } + + public boolean isFinished() { + return isFinished; + } + + public void setFinished(boolean finished) { + isFinished = finished; + } } diff --git a/src/main/java/seng302/server/simulator/Simulator.java b/src/main/java/seng302/server/simulator/Simulator.java index 1cce94c2..72d2717a 100644 --- a/src/main/java/seng302/server/simulator/Simulator.java +++ b/src/main/java/seng302/server/simulator/Simulator.java @@ -37,7 +37,7 @@ public class Simulator extends Observable implements Runnable { boat.setLng(startLng); boat.setLastPassedCorner(course.get(0)); 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(); } } + + 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()); // 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 Position pos = GeoUtility.getGeoCoordinate( diff --git a/src/main/resources/views/MainView.fxml b/src/main/resources/views/MainView.fxml index dc275512..ac72bd8c 100644 --- a/src/main/resources/views/MainView.fxml +++ b/src/main/resources/views/MainView.fxml @@ -7,21 +7,21 @@ - + - + - + - - - - - - + + + + + +