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.
322 lines
10 KiB
322 lines
10 KiB
package visualiser.Controllers;
|
|
|
|
import javafx.animation.AnimationTimer;
|
|
import javafx.application.Platform;
|
|
import javafx.collections.ObservableList;
|
|
import javafx.fxml.FXML;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.TableColumn;
|
|
import javafx.scene.control.TableView;
|
|
import javafx.scene.layout.AnchorPane;
|
|
import javafx.scene.layout.GridPane;
|
|
import javafx.scene.paint.Color;
|
|
import network.Messages.Enums.RaceStatusEnum;
|
|
import network.Messages.LatestMessages;
|
|
import shared.dataInput.*;
|
|
import shared.enums.XMLFileType;
|
|
import shared.exceptions.InvalidBoatDataException;
|
|
import shared.exceptions.InvalidRaceDataException;
|
|
import shared.exceptions.InvalidRegattaDataException;
|
|
import shared.exceptions.XMLReaderException;
|
|
import visualiser.app.VisualiserInput;
|
|
import visualiser.model.VisualiserBoat;
|
|
import visualiser.model.VisualiserRace;
|
|
|
|
import java.io.IOException;
|
|
import java.net.Socket;
|
|
import java.net.URL;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Controller to for waiting for the race to start.
|
|
*/
|
|
public class StartController extends Controller implements Observer {
|
|
|
|
@FXML private GridPane start;
|
|
@FXML private AnchorPane startWrapper;
|
|
|
|
/**
|
|
* The name of the race/regatta.
|
|
*/
|
|
@FXML private Label raceTitleLabel;
|
|
|
|
/**
|
|
* The time the race starts at.
|
|
*/
|
|
@FXML private Label raceStartLabel;
|
|
|
|
/**
|
|
* The current time at the race location.
|
|
*/
|
|
@FXML private Label timeZoneTime;
|
|
|
|
/**
|
|
* Time until the race starts.
|
|
*/
|
|
@FXML private Label timer;
|
|
|
|
@FXML private TableView<VisualiserBoat> boatNameTable;
|
|
@FXML private TableColumn<VisualiserBoat, String> boatNameColumn;
|
|
@FXML private TableColumn<VisualiserBoat, String> boatCodeColumn;
|
|
|
|
/**
|
|
* The status of the race.
|
|
*/
|
|
@FXML private Label raceStatusLabel;
|
|
|
|
|
|
|
|
/**
|
|
* The object used to read packets from the connected server.
|
|
*/
|
|
private VisualiserInput visualiserInput;
|
|
|
|
/**
|
|
* The race object which describes the currently occurring race.
|
|
*/
|
|
private VisualiserRace visualiserRace;
|
|
|
|
/**
|
|
* An array of colors used to assign colors to each boat - passed in to the VisualiserRace constructor.
|
|
*/
|
|
List<Color> colors = new ArrayList<>(Arrays.asList(
|
|
Color.BLUEVIOLET,
|
|
Color.BLACK,
|
|
Color.RED,
|
|
Color.ORANGE,
|
|
Color.DARKOLIVEGREEN,
|
|
Color.LIMEGREEN,
|
|
Color.PURPLE,
|
|
Color.DARKGRAY,
|
|
Color.YELLOW
|
|
));
|
|
|
|
|
|
|
|
/**
|
|
* Ctor.
|
|
*/
|
|
public StartController() {
|
|
}
|
|
|
|
@Override
|
|
public void initialize(URL location, ResourceBundle resources) {
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts the race.
|
|
* Called once we have received all XML files from the server.
|
|
* @param latestMessages The set of latest race messages to use for race.
|
|
* @throws XMLReaderException Thrown if XML file cannot be parsed.
|
|
* @throws InvalidRaceDataException Thrown if XML file cannot be parsed.
|
|
* @throws InvalidBoatDataException Thrown if XML file cannot be parsed.
|
|
* @throws InvalidRegattaDataException Thrown if XML file cannot be parsed.
|
|
*/
|
|
private void startRace(LatestMessages latestMessages) throws XMLReaderException, InvalidRaceDataException, InvalidBoatDataException, InvalidRegattaDataException {
|
|
|
|
//Create data sources from latest messages for the race.
|
|
RaceDataSource raceDataSource = new RaceXMLReader(latestMessages.getRaceXMLMessage().getXmlMessage(), XMLFileType.Contents);
|
|
BoatDataSource boatDataSource = new BoatXMLReader(latestMessages.getBoatXMLMessage().getXmlMessage(), XMLFileType.Contents);
|
|
RegattaDataSource regattaDataSource = new RegattaXMLReader(latestMessages.getRegattaXMLMessage().getXmlMessage(), XMLFileType.Contents);
|
|
|
|
//Create race.
|
|
this.visualiserRace = new VisualiserRace(boatDataSource, raceDataSource, regattaDataSource, latestMessages, this.colors);
|
|
new Thread(this.visualiserRace).start();
|
|
|
|
|
|
//Initialise the boat table.
|
|
initialiseBoatTable(this.visualiserRace);
|
|
|
|
//Initialise the race name.
|
|
initialiseRaceName(this.visualiserRace);
|
|
|
|
//Initialises the race clock.
|
|
initialiseRaceClock(this.visualiserRace);
|
|
|
|
//Starts the race countdown timer.
|
|
countdownTimer();
|
|
}
|
|
|
|
|
|
|
|
|
|
public AnchorPane startWrapper(){
|
|
return startWrapper;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialises the boat table that is to be shown on the pane.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseBoatTable(VisualiserRace visualiserRace) {
|
|
|
|
//Get the boats.
|
|
ObservableList<VisualiserBoat> boats = visualiserRace.getBoats();
|
|
|
|
//Populate table.
|
|
boatNameTable.setItems(boats);
|
|
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
|
boatCodeColumn.setCellValueFactory(cellData -> cellData.getValue().countryProperty());
|
|
}
|
|
|
|
/**
|
|
* Initialises the race name which is shown on the pane.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseRaceName(VisualiserRace visualiserRace) {
|
|
|
|
raceTitleLabel.setText(visualiserRace.getRegattaName());
|
|
|
|
}
|
|
|
|
/**
|
|
* Initialises the race clock/timer labels for the start time, current time, and remaining time.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseRaceClock(VisualiserRace visualiserRace) {
|
|
|
|
//Start time.
|
|
initialiseRaceClockStartTime(visualiserRace);
|
|
|
|
//Current time.
|
|
initialiseRaceClockCurrentTime(visualiserRace);
|
|
|
|
//Remaining time.
|
|
initialiseRaceClockDuration(visualiserRace);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialises the race current time label.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseRaceClockStartTime(VisualiserRace visualiserRace) {
|
|
|
|
raceStartLabel.setText(visualiserRace.getRaceClock().getStartingTimeString());
|
|
|
|
visualiserRace.getRaceClock().startingTimeProperty().addListener((observable, oldValue, newValue) -> {
|
|
Platform.runLater(() -> {
|
|
raceStartLabel.setText(newValue);
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialises the race current time label.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseRaceClockCurrentTime(VisualiserRace visualiserRace) {
|
|
|
|
visualiserRace.getRaceClock().currentTimeProperty().addListener((observable, oldValue, newValue) -> {
|
|
Platform.runLater(() -> {
|
|
timeZoneTime.setText(newValue);
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Initialises the race duration label.
|
|
* @param visualiserRace The race to get data from.
|
|
*/
|
|
private void initialiseRaceClockDuration(VisualiserRace visualiserRace) {
|
|
|
|
visualiserRace.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
|
Platform.runLater(() -> {
|
|
timer.setText(newValue);
|
|
});
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Countdown timer until race starts.
|
|
*/
|
|
private void countdownTimer() {
|
|
new AnimationTimer() {
|
|
@Override
|
|
public void handle(long arg0) {
|
|
|
|
//TODO instead of having an AnimationTimer checking the race status, we could provide a Property<RaceStatusEnum>, and connect a listener to that.
|
|
//Get the current race status.
|
|
RaceStatusEnum raceStatus = visualiserRace.getRaceStatusEnum();
|
|
|
|
//Display it.
|
|
raceStatusLabel.setText("Race Status: " + raceStatus.name());
|
|
|
|
//If the race has reached the preparatory phase, or has started...
|
|
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
|
|
//Stop this timer.
|
|
stop();
|
|
|
|
//Hide this, and display the race controller.
|
|
startWrapper.setVisible(false);
|
|
start.setVisible(false);
|
|
|
|
parent.beginRace(visualiserInput, visualiserRace);
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Function to handle changes in objects we observe.
|
|
* We observe LatestMessages.
|
|
* @param o The observed object.
|
|
* @param arg The {@link Observable#notifyObservers(Object)} parameter.
|
|
*/
|
|
@Override
|
|
public void update(Observable o, Object arg) {
|
|
|
|
//Check that we actually have LatestMessages.
|
|
if (o instanceof LatestMessages) {
|
|
LatestMessages latestMessages = (LatestMessages) o;
|
|
|
|
//If we've received all of the xml files, start the race. Only start it if it hasn't already been created.
|
|
if (latestMessages.hasAllXMLMessages() && this.visualiserRace == null) {
|
|
|
|
//Need to handle it in the javafx thread.
|
|
Platform.runLater(() -> {
|
|
try {
|
|
this.startRace(latestMessages);
|
|
|
|
} catch (XMLReaderException | InvalidBoatDataException | InvalidRaceDataException | InvalidRegattaDataException e) {
|
|
//We currently don't handle this in meaningful way, as it should never occur.
|
|
//If we reach this point it means that malformed XML files were sent.
|
|
e.printStackTrace();
|
|
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Show starting information for a race given a socket.
|
|
* @param socket network source of information
|
|
*/
|
|
public void enterLobby(Socket socket) {
|
|
startWrapper.setVisible(true);
|
|
try {
|
|
//Begin reading packets from the socket/server.
|
|
this.visualiserInput = new VisualiserInput(socket);
|
|
//Store a reference to latestMessages so that we can observe it.
|
|
LatestMessages latestMessages = this.visualiserInput.getLatestMessages();
|
|
latestMessages.addObserver(this);
|
|
new Thread(this.visualiserInput).start();
|
|
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
}
|