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.

379 lines
12 KiB

package mock.model;
import network.AckSequencer;
import network.Messages.*;
import network.Messages.Enums.BoatLocationDeviceEnum;
import network.Messages.Enums.YachtEventEnum;
import network.Messages.Enums.XMLMessageType;
import shared.model.Bearing;
import shared.model.CompoundMark;
import shared.model.Mark;
import shared.xml.Race.RaceDataSourceToXML;
import shared.xml.boats.BoatDataSourceToXML;
import shared.xml.regatta.RegattaDataSourceToXML;
import javax.xml.bind.JAXBException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by connortaylorbrown on 2/08/17.
*/
public class RaceServer {
private MockRace race;
private LatestMessages latestMessages;
private static RaceServer server;
private List<YachtEvent> collisionEvents = new ArrayList<>();
/**
* The sequence number of the latest BoatLocation message sent or received.
*/
private int boatLocationSequenceNumber = 1;
/**
* The sequence number of race XML messages.
*/
private int raceXMLSeqNumber = -1;
/**
* The sequence number of boat XML messages.
*/
private int boatXMLSeqNumber = -1;
/**
* The sequence number of regatta XML messages.
*/
private int regattaXMLSeqNumber = -1;
public RaceServer(MockRace race, LatestMessages latestMessages) {
server = this;
this.race = race;
this.latestMessages = latestMessages;
}
public static void staticUpdateXML() {
if (server != null) {
server.updateXMLFiles();
}
}
/**
* Parses the race to create a snapshot, and places it in latestMessages.
*/
public void parseSnapshot() {
List<AC35Data> snapshotMessages = new ArrayList<>();
//Parse the boat locations.
snapshotMessages.addAll(parseBoatLocations());
//Parse the boat states
snapshotMessages.addAll(parseBoatStates());
//Parse the marks.
snapshotMessages.addAll(parseMarks());
//Parse the race status.
snapshotMessages.add(parseRaceStatus());
//Parse collisions
if(collisionEvents.size()>0){
snapshotMessages.addAll(collisionEvents);
}
latestMessages.setSnapshot(snapshotMessages);
updateXMLFiles();
//Reset collision list
collisionEvents.clear();
//System.out.println(collisionEvents.size());
}
/**
* Checks if the race/boat/regatta data sources have changed, and if they have, update their xml representations.
*/
private void updateXMLFiles() {
updateRaceXMLFile();
updateBoatXMLFile();
updateRegattaXMLFile();
}
/**
* Checks if the race data source has changed, and if it has, updates LatestMessages' race xml message.
*/
private void updateRaceXMLFile() {
if (raceXMLSeqNumber != race.getRaceDataSource().getSequenceNumber()) {
raceXMLSeqNumber = race.getRaceDataSource().getSequenceNumber();
try {
String raceXMLString = RaceDataSourceToXML.toString(race.getRaceDataSource());
XMLMessage message = createXMLMessage(raceXMLString, XMLMessageType.RACE);
latestMessages.setXMLMessage(message);
} catch (JAXBException e) {
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getRaceDataSource(), e);
}
}
}
/**
* Checks if the boat data source has changed, and if it has, updates LatestMessages' boat xml message.
*/
private void updateBoatXMLFile() {
if (boatXMLSeqNumber != race.getBoatDataSource().getSequenceNumber()) {
boatXMLSeqNumber = race.getBoatDataSource().getSequenceNumber();
try {
String boatXMLString = BoatDataSourceToXML.toString(race.getBoatDataSource());
XMLMessage message = createXMLMessage(boatXMLString, XMLMessageType.BOAT);
latestMessages.setXMLMessage(message);
} catch (JAXBException e) {
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getBoatDataSource(), e);
}
}
}
/**
* Checks if the regatta data source has changed, and if it has, updates LatestMessages' regatta xml message.
*/
private void updateRegattaXMLFile() {
if (regattaXMLSeqNumber != race.getRegattaDataSource().getSequenceNumber()) {
regattaXMLSeqNumber = race.getRegattaDataSource().getSequenceNumber();
try {
String regattaXMLString = RegattaDataSourceToXML.toString(race.getRegattaDataSource());
XMLMessage message = createXMLMessage(regattaXMLString, XMLMessageType.REGATTA);
latestMessages.setXMLMessage(message);
} catch (JAXBException e) {
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getRegattaDataSource(), e);
}
}
}
/**
* Parses an individual marker boat, and returns it.
* @param mark The marker boat to parse.
* @return The BoatLocation message.
*/
private BoatLocation parseIndividualMark(Mark mark) {
//Create message.
BoatLocation boatLocation = new BoatLocation(
mark.getSourceID(),
mark.getPosition().getLatitude(),
mark.getPosition().getLongitude(),
this.boatLocationSequenceNumber,
BoatLocationDeviceEnum.Mark,
Bearing.fromDegrees(0),
0,
race.getRaceClock().getCurrentTimeMilli());
//Iterates the sequence number.
this.boatLocationSequenceNumber++;
return boatLocation;
}
/**
* Parse the compound marker boats, and returns a list of BoatLocation messages.
* @return BoatLocation messages for each mark.
*/
private List<BoatLocation> parseMarks() {
List<BoatLocation> markLocations = new ArrayList<>(race.getCompoundMarks().size());
for (CompoundMark compoundMark : race.getCompoundMarks()) {
//Get the individual marks from the compound mark.
Mark mark1 = compoundMark.getMark1();
Mark mark2 = compoundMark.getMark2();
//If they aren't null, parse them (some compound marks only have one mark).
if (mark1 != null) {
markLocations.add(this.parseIndividualMark(mark1));
}
if (mark2 != null) {
markLocations.add(this.parseIndividualMark(mark2));
}
}
return markLocations;
}
/**
* Parse the boats in the race, and returns all of their BoatLocation messages.
* @return List of BoatLocation messages, for each boat.
*/
private List<BoatLocation> parseBoatLocations() {
List<BoatLocation> boatLocations = new ArrayList<>(race.getBoats().size());
//Parse each boat.
for (MockBoat boat : race.getBoats()) {
boatLocations.add(this.parseIndividualBoatLocation(boat));
}
return boatLocations;
}
/**
* Parses an individual boat, and returns it.
* @param boat The boat to parse.
* @return The BoatLocation message.
*/
private BoatLocation parseIndividualBoatLocation(MockBoat boat) {
BoatLocation boatLocation = new BoatLocation(
boat.getSourceID(),
boat.getPosition().getLatitude(),
boat.getPosition().getLongitude(),
this.boatLocationSequenceNumber,
BoatLocationDeviceEnum.RacingYacht,
boat.getBearing(),
boat.getCurrentSpeed(),
race.getRaceClock().getCurrentTimeMilli());
//Iterates the sequence number.
this.boatLocationSequenceNumber++;
return boatLocation;
}
/**
* Generates BoatState messages for every boat in the race
* @return list of BoatState messages
*/
private List<BoatState> parseBoatStates() {
List<BoatState> boatStates = new ArrayList<>();
for(MockBoat boat: race.getBoats()) {
boatStates.add(parseIndividualBoatState(boat));
}
return boatStates;
}
/**
* Creates a BoatState message for the current state of the given boat
* @param boat to generate message for
* @return BoatState message
*/
private BoatState parseIndividualBoatState(MockBoat boat) {
return new BoatState(
boat.getSourceID(),
boat.getHealth()
);
}
/**
* Parses the race status, and returns it.
* @return The race status message.
*/
private RaceStatus parseRaceStatus() {
//A race status message contains a list of boat statuses.
List<BoatStatus> boatStatuses = new ArrayList<>();
//Add each boat status to the status list.
for (MockBoat boat : race.getBoats()) {
BoatStatus boatStatus = new BoatStatus(
boat.getSourceID(),
boat.getStatus(),
boat.getCurrentLeg().getLegNumber(),
boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() );
boatStatuses.add(boatStatus);
}
//Create race status object, and send it.
RaceStatus raceStatus = new RaceStatus(
RaceStatus.currentMessageVersionNumber,
System.currentTimeMillis(),
race.getRaceId(),
race.getRaceStatusEnum(),
race.getRaceClock().getStartingTimeMilli(),
race.getWindDirection(),
race.getWindSpeed(),
race.getRaceType(),
boatStatuses);
return raceStatus;
}
/**
* Creates an XMLMessage of a specified subtype using the xml contents string.
* @param xmlString The contents of the xml file.
* @param messageType The subtype of xml message (race, regatta, boat).
* @return The created XMLMessage object.
*/
private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) {
//Get the correct sequence number to use.
int sequenceNumber = 0;
if (messageType == XMLMessageType.RACE) {
sequenceNumber = this.raceXMLSeqNumber;
} else if (messageType == XMLMessageType.BOAT) {
sequenceNumber = this.boatXMLSeqNumber;
} else if (messageType == XMLMessageType.REGATTA) {
sequenceNumber = this.regattaXMLSeqNumber;
}
//Create the message.
XMLMessage message = new XMLMessage(
XMLMessage.currentVersionNumber,
AckSequencer.getNextAckNum(),
System.currentTimeMillis(),
messageType,
sequenceNumber,
xmlString);
return message;
}
/**
* Parse the yacht event and return it
* @param boat yacht with event
* @param event event that happened
* @return yacht event
*/
private YachtEvent parseYachtEvent(MockBoat boat, YachtEventEnum event){
YachtEvent yachtEvent = new YachtEvent(
System.currentTimeMillis(),
this.boatLocationSequenceNumber,
race.getRaceId(),
boat.getSourceID(),
1337,
event);
return yachtEvent;
}
public void parseBoatCollisions(ArrayList<MockBoat> boats){
for (MockBoat boat : boats){
collisionEvents.add(parseYachtEvent(boat, YachtEventEnum.COLLISION));
}
}
}