You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
9.2 KiB
262 lines
9.2 KiB
package seng302.Mock;
|
|
|
|
import javafx.animation.AnimationTimer;
|
|
import javafx.application.Platform;
|
|
import javafx.collections.FXCollections;
|
|
import javafx.collections.ObservableList;
|
|
import seng302.Controllers.FinishController;
|
|
import seng302.Controllers.RaceController;
|
|
import seng302.GPSCoordinate;
|
|
import seng302.Model.Boat;
|
|
import seng302.Model.Leg;
|
|
import seng302.Model.Marker;
|
|
import seng302.Networking.Messages.BoatLocation;
|
|
import seng302.Networking.Messages.BoatStatus;
|
|
import seng302.Networking.Messages.Enums.BoatStatusEnum;
|
|
import seng302.VisualiserInput;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* The Class used to view the race streamed.
|
|
*/
|
|
public class StreamedRace implements Runnable {
|
|
private final VisualiserInput visualiserInput;
|
|
private final ObservableList<Boat> startingBoats;
|
|
private final ObservableList<Marker> boatMarkers;
|
|
private final List<Leg> legs;
|
|
private RaceController controller;
|
|
protected FinishController finishController;
|
|
private int boatsFinished = 0;
|
|
private long totalTimeElapsed;
|
|
|
|
private int lastFPS = 20;
|
|
|
|
public StreamedRace(VisualiserInput visualiserInput, RaceController controller) {
|
|
StreamedCourse course = visualiserInput.getCourse();
|
|
|
|
this.startingBoats = FXCollections.observableArrayList(course.getBoats());
|
|
this.boatMarkers = FXCollections.observableArrayList(course.getMarkers());
|
|
this.legs = course.getLegs();
|
|
this.legs.add(new Leg("Finish", this.legs.size()));
|
|
this.controller = controller;
|
|
if (startingBoats != null && startingBoats.size() > 0) {
|
|
initialiseBoats();
|
|
}
|
|
this.visualiserInput = visualiserInput;
|
|
}
|
|
|
|
private void initialiseBoats() {
|
|
Leg officialStart = legs.get(0);
|
|
String name = officialStart.getName();
|
|
Marker endCompoundMark = officialStart.getEndMarker();
|
|
|
|
for (Boat boat : startingBoats) {
|
|
if (boat != null) {
|
|
Leg startLeg = new Leg(name, 0);
|
|
startLeg.setEndMarker(endCompoundMark);
|
|
boat.setCurrentLeg(startLeg, controller.getRaceClock());
|
|
boat.setTimeSinceLastMark(controller.getRaceClock().getTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the boat cannot finish the race
|
|
* @return True if boat cannot finish the race
|
|
*/
|
|
protected boolean doNotFinish() {
|
|
// DNF is no longer random and is now determined by a dnf packet
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks the position of the boat.
|
|
*
|
|
* @param boat Boat that the position is to be updated for.
|
|
* @param timeElapsed Time that has elapse since the start of the the race.
|
|
*/
|
|
private void checkPosition(Boat boat, long timeElapsed) {
|
|
boolean legChanged = false;
|
|
StreamedCourse raceData = visualiserInput.getCourse();
|
|
BoatStatus boatStatusMessage = visualiserInput.getBoatStatusMap().get(boat.getSourceID());
|
|
if (boatStatusMessage != null) {
|
|
BoatStatusEnum boatStatusEnum = BoatStatusEnum.fromByte(boatStatusMessage.getBoatStatus());
|
|
|
|
int legNumber = boatStatusMessage.getLegNumber();
|
|
|
|
|
|
if (legNumber >= 1 && legNumber < legs.size()) {
|
|
if (boat.getCurrentLeg() != legs.get(legNumber)){
|
|
boat.setCurrentLeg(legs.get(legNumber), controller.getRaceClock());
|
|
legChanged = true;
|
|
}
|
|
}
|
|
|
|
if (boatStatusEnum == BoatStatusEnum.RACING) {
|
|
boat.addTrackPoint(boat.getCurrentPosition());
|
|
} else if (boatStatusEnum == BoatStatusEnum.DNF) {
|
|
boat.setDnf(true);
|
|
} else if (boatStatusEnum == BoatStatusEnum.FINISHED || legNumber == raceData.getLegs().size()) {
|
|
boatsFinished++;
|
|
boat.setTimeFinished(timeElapsed);
|
|
boat.setFinished(true);
|
|
}
|
|
}
|
|
if (legChanged) {
|
|
//Update the boat display table in the GUI to reflect the leg change
|
|
updatePositions();
|
|
controller.updateSparkline(startingBoats);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the boat's gps coordinates
|
|
*
|
|
* @param boat to be updated
|
|
* @param millisecondsElapsed time since last update
|
|
*/
|
|
private void updatePosition(Boat boat, int millisecondsElapsed) {
|
|
int sourceID = boat.getSourceID();
|
|
BoatLocation boatLocation = visualiserInput.getBoatLocationMessage(sourceID);
|
|
if(boatLocation != null) {
|
|
double lat = boatLocation.getLatitudeDouble();
|
|
double lon = boatLocation.getLongitudeDouble();
|
|
boat.setCurrentPosition(new GPSCoordinate(lat, lon));
|
|
boat.setHeading(boatLocation.getHeadingDegrees());
|
|
double MMPS_TO_KN = 0.001944;
|
|
boat.setVelocity(boatLocation.getBoatSOG() * MMPS_TO_KN);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the boat's gps coordinates
|
|
*
|
|
* @param mark to be updated
|
|
*/
|
|
private void updateMarker(Marker mark) {
|
|
int sourceID = mark.getSourceId1();
|
|
BoatLocation boatLocation1 = visualiserInput.getBoatLocationMessage(sourceID);
|
|
if(boatLocation1 != null) {
|
|
double lat = boatLocation1.getLatitudeDouble();
|
|
double lon = boatLocation1.getLongitudeDouble();
|
|
mark.setCurrentPosition1(new GPSCoordinate(lat, lon));
|
|
}
|
|
int sourceID2 = mark.getSourceId2();
|
|
BoatLocation boatLocation2 = visualiserInput.getBoatLocationMessage(sourceID2);
|
|
if(boatLocation2 != null) {
|
|
double lat = boatLocation2.getLatitudeDouble();
|
|
double lon = boatLocation2.getLongitudeDouble();
|
|
mark.setCurrentPosition2(new GPSCoordinate(lat, lon));
|
|
}
|
|
}
|
|
|
|
public void setController(RaceController controller) {
|
|
this.controller = controller;
|
|
}
|
|
|
|
|
|
/**
|
|
* Runnable for the thread.
|
|
*/
|
|
public void run() {
|
|
setControllerListeners();
|
|
Platform.runLater(() -> controller.createSparkLine(startingBoats));
|
|
initialiseBoats();
|
|
startRaceStream();
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the calculated fps to the fps label
|
|
*
|
|
* @param fps The new calculated fps value
|
|
*/
|
|
private void updateFPS(int fps) {
|
|
Platform.runLater(() -> controller.setFrames("FPS: " + fps));
|
|
}
|
|
|
|
/**
|
|
* Starts the Race Simulation, playing the race start to finish with the timescale.
|
|
* This prints the boats participating, the order that the events occur in time order, and the respective information of the events.
|
|
*/
|
|
private void startRaceStream() {
|
|
|
|
System.setProperty("javafx.animation.fullspeed", "true");
|
|
|
|
for (Boat boat : startingBoats) {
|
|
boat.setStarted(true);
|
|
}
|
|
|
|
new AnimationTimer() {
|
|
|
|
final long timeRaceStarted = System.currentTimeMillis(); //start time of loop
|
|
int fps = 0; //init fps value
|
|
long timeCurrent = System.currentTimeMillis(); //current time
|
|
|
|
@Override
|
|
public void handle(long arg0) {
|
|
totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted;
|
|
|
|
for (Boat boat : startingBoats) {
|
|
if (boat != null && !boat.isFinished()) {
|
|
updatePosition(boat, Math.round(1000 / lastFPS) > 20 ? 15 : Math.round(1000 / lastFPS));
|
|
checkPosition(boat, totalTimeElapsed);
|
|
}
|
|
}
|
|
for (Marker mark: boatMarkers){
|
|
if (mark != null){
|
|
updateMarker(mark);
|
|
}
|
|
}
|
|
//System.out.println(boatsFinished + ":" + startingBoats.size());
|
|
if (visualiserInput.getRaceStatus().isFinished()){
|
|
controller.finishRace(startingBoats);
|
|
stop();
|
|
}
|
|
controller.updateMap(startingBoats, boatMarkers);
|
|
fps++;
|
|
if ((System.currentTimeMillis() - timeCurrent) > 1000) {
|
|
updateFPS(fps);
|
|
lastFPS = fps;
|
|
fps = 0;
|
|
timeCurrent = System.currentTimeMillis();
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
/**
|
|
* Update position of boats in race, no position if on starting leg or DNF.
|
|
*/
|
|
private void updatePositions() {
|
|
FXCollections.sort(startingBoats, (a, b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber());
|
|
for(Boat boat: startingBoats) {
|
|
if(boat != null) {
|
|
boat.setPosition(Integer.toString(startingBoats.indexOf(boat) + 1));
|
|
if (boat.isDnf() || !boat.isStarted() || boat.getCurrentLeg().getLegNumber() < 0)
|
|
boat.setPosition("-");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update call for the controller.
|
|
*/
|
|
private void setControllerListeners() {
|
|
if (controller != null) controller.setInfoTable(this);
|
|
}
|
|
|
|
/**
|
|
* Returns the boats that have started the race.
|
|
*
|
|
* @return ObservableList of Boat class that participated in the race.
|
|
* @see ObservableList
|
|
* @see Boat
|
|
*/
|
|
public ObservableList<Boat> getStartingBoats() {
|
|
return startingBoats;
|
|
}
|
|
|
|
}
|