package mock.app; import mock.dataInput.PolarParser; import mock.exceptions.EventConstructionException; import mock.model.*; import mock.model.commandFactory.CompositeCommand; import mock.model.wind.RandomWindGenerator; import mock.model.wind.ShiftingWindGenerator; import mock.model.wind.WindGenerator; import mock.xml.RaceXMLCreator; import network.Messages.LatestMessages; import org.xml.sax.SAXException; import shared.dataInput.*; import shared.enums.XMLFileType; import shared.exceptions.InvalidBoatDataException; import shared.exceptions.InvalidRaceDataException; import shared.exceptions.InvalidRegattaDataException; import shared.exceptions.XMLReaderException; import shared.model.Bearing; import shared.model.Constants; import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; /** * A Race Event, this holds all of the race's information as well as handling the connection to its clients. */ public class Event { /** * Contents of the various xml files. */ private String raceXML; private String regattaXML; private String boatXML; private XMLFileType xmlFileType; private Polars boatPolars; /** * Data sources containing data from the xml files. */ RaceDataSource raceDataSource; BoatDataSource boatDataSource; RegattaDataSource regattaDataSource; private ConnectionAcceptor connectionAcceptor; private LatestMessages latestMessages; private CompositeCommand compositeCommand; /** * This is used to allocate source IDs. */ private SourceIdAllocator sourceIdAllocator; private Thread raceThread; private Thread connectionThread; /** * Constructs an event, using various XML files. * @param gameType Type of game to create. Determines which XML files to send * @throws EventConstructionException Thrown if we cannot create an Event for any reason. */ public Event(int gameType) throws EventConstructionException { String raceXMLFile = "mock/mockXML/raceTest.xml"; String boatsXMLFile = "mock/mockXML/boatTest.xml"; String regattaXMLFile = "mock/mockXML/regattaTest.xml"; if (gameType == 1) { raceXMLFile = "mock/mockXML/raceSinglePlayer.xml"; boatsXMLFile = "mock/mockXML/boatsSinglePlayer.xml"; } if (gameType == 2){ raceXMLFile = "mock/mockXML/raceTutorial.xml"; boatsXMLFile = "mock/mockXML/boatTutorial.xml"; regattaXMLFile = "mock/mockXML/regattaTutorial.xml"; } //Read XML files. try { //this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90); this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8); this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8)); this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8); this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8); } catch (XMLReaderException e) { throw new EventConstructionException("Could not read XML files.", e); } this.xmlFileType = XMLFileType.Contents; this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv"); PolarParser.parseNewPolars("mock/polars/acc_polars.csv"); //Parse the XML files into data sources. try { this.raceDataSource = new RaceXMLReader(this.raceXML, this.xmlFileType); this.boatDataSource = new BoatXMLReader(this.boatXML, this.xmlFileType); this.regattaDataSource = new RegattaXMLReader(this.regattaXML, this.xmlFileType); } catch (XMLReaderException | InvalidRaceDataException | InvalidRegattaDataException | InvalidBoatDataException e) { throw new EventConstructionException("Could not parse XML files.", e); } this.compositeCommand = new CompositeCommand(); this.latestMessages = new LatestMessages(); WindGenerator windGenerator = new ShiftingWindGenerator( Bearing.fromDegrees(225), 12 ); RaceLogic newRace = new RaceLogic( new MockRace( boatDataSource, raceDataSource, regattaDataSource, this.boatPolars, Constants.RaceTimeScale, windGenerator ), this.latestMessages, this.compositeCommand); this.raceThread = new Thread(newRace, "Event.Start()->RaceLogic thread"); raceThread.start(); //Create connection acceptor. this.sourceIdAllocator = new SourceIdAllocator(newRace.getRace()); try { this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, newRace); } catch (IOException e) { throw new EventConstructionException("Could not create ConnectionAcceptor.", e); } this.connectionThread = new Thread(connectionAcceptor, "Event.Start()->ConnectionAcceptor thread"); connectionThread.start(); } public void endEvent() throws IOException { this.connectionThread.interrupt(); this.connectionAcceptor.closeConnection(); this.raceThread.interrupt(); } //TODO remove this after demo on 18th august! /** * Sets the xml description of the race to show the race was created now, and starts in 4 minutes * @param raceXML The race.xml contents. * @return String containing edited xml */ public static String setRaceXMLAtCurrentTimeToNow(String raceXML) { //The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute. long millisecondsToAdd = Constants.RacePreStartTime + Constants.RacePreparatoryTime; long secondsToAdd = millisecondsToAdd / 1000; //Scale the time using our time scalar. secondsToAdd = secondsToAdd / Constants.RaceTimeScale; DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); ZonedDateTime creationTime = ZonedDateTime.now(); raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime)); raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd))); return raceXML; } }