@ -1,18 +1,15 @@
package mock.model ;
package mock.model ;
import mock.model.commandFactory.ActiveObserverCommand ;
import mock.model.commandFactory.ObserverCommand ;
import mock.model.wind.WindGenerator ;
import mock.model.wind.WindGenerator ;
import javafx.animation.AnimationTimer ;
import mock.model.collider.ColliderRegistry ;
import mock.model.collider.ColliderRegistry ;
import mock.xml.* ;
import network.Messages.BoatLocation ;
import network.Messages.BoatStatus ;
import network.Messages.Enums.BoatStatusEnum ;
import network.Messages.Enums.BoatStatusEnum ;
import network.Messages.Enums.RaceStatusEnum ;
import network.Messages.Enums.RaceStatusEnum ;
import shared.dataInput.BoatDataSource ;
import shared.dataInput.BoatDataSource ;
import shared.dataInput.RaceDataSource ;
import shared.dataInput.RaceDataSource ;
import shared.dataInput.RegattaDataSource ;
import shared.dataInput.RegattaDataSource ;
import shared.exceptions.BoatNotFoundException ;
import shared.exceptions.BoatNotFoundException ;
import shared.enums.RoundingType ;
import shared.model.* ;
import shared.model.* ;
import shared.model.Bearing ;
import shared.model.Bearing ;
@ -64,7 +61,11 @@ public class MockRace extends RaceState {
* /
* /
private Polars polars ;
private Polars polars ;
private ActiveObserverCommand activeObserverCommand ;
private long racePreStartTime = Constants . RacePreStartTime ;
private long racePreparatoryTime = Constants . RacePreparatoryTime ;
/ * *
/ * *
* Constructs a race object with a given RaceDataSource , BoatDataSource , and RegattaDataSource and sends events to the given mockOutput .
* Constructs a race object with a given RaceDataSource , BoatDataSource , and RegattaDataSource and sends events to the given mockOutput .
@ -81,6 +82,7 @@ public class MockRace extends RaceState {
this . setRaceDataSource ( raceDataSource ) ;
this . setRaceDataSource ( raceDataSource ) ;
this . setRegattaDataSource ( regattaDataSource ) ;
this . setRegattaDataSource ( regattaDataSource ) ;
this . activeObserverCommand = new ActiveObserverCommand ( ) ;
this . polars = polars ;
this . polars = polars ;
this . scaleFactor = timeScale ;
this . scaleFactor = timeScale ;
@ -119,6 +121,7 @@ public class MockRace extends RaceState {
//Construct a MockBoat using the Boat and Polars.
//Construct a MockBoat using the Boat and Polars.
MockBoat mockBoat = new MockBoat ( boat , polars ) ;
MockBoat mockBoat = new MockBoat ( boat , polars ) ;
mockBoat . setCurrentLeg ( this . getLegs ( ) . get ( 0 ) ) ;
mockBoat . setCurrentLeg ( this . getLegs ( ) . get ( 0 ) ) ;
mockBoat . setEstimatedTimeAtNextMark ( this . getRaceClock ( ) . getCurrentTime ( ) ) ;
//Update participant list.
//Update participant list.
getRaceDataSource ( ) . getParticipants ( ) . add ( sourceID ) ;
getRaceDataSource ( ) . getParticipants ( ) . add ( sourceID ) ;
@ -163,15 +166,15 @@ public class MockRace extends RaceState {
long timeToStart = - this . getRaceClock ( ) . getDurationMilli ( ) ;
long timeToStart = - this . getRaceClock ( ) . getDurationMilli ( ) ;
if ( timeToStart > Constants. R acePreStartTime) {
if ( timeToStart > r acePreStartTime) {
//Time > 3 minutes is the prestart period.
//Time > 3 minutes is the prestart period.
this . setRaceStatusEnum ( RaceStatusEnum . PRESTART ) ;
this . setRaceStatusEnum ( RaceStatusEnum . PRESTART ) ;
} else if ( ( timeToStart < = Constants. R acePreStartTime) & & ( timeToStart > = Constants. R acePreparatoryTime) ) {
} else if ( ( timeToStart < = r acePreStartTime) & & ( timeToStart > = r acePreparatoryTime) ) {
//Time between [1, 3] minutes is the warning period.
//Time between [1, 3] minutes is the warning period.
this . setRaceStatusEnum ( RaceStatusEnum . WARNING ) ;
this . setRaceStatusEnum ( RaceStatusEnum . WARNING ) ;
} else if ( ( timeToStart < = Constants. R acePreparatoryTime) & & ( timeToStart > 0 ) ) {
} else if ( ( timeToStart < = r acePreparatoryTime) & & ( timeToStart > 0 ) ) {
//Time between (0, 1] minutes is the preparatory period.
//Time between (0, 1] minutes is the preparatory period.
this . setRaceStatusEnum ( RaceStatusEnum . PREPARATORY ) ;
this . setRaceStatusEnum ( RaceStatusEnum . PREPARATORY ) ;
@ -184,6 +187,13 @@ public class MockRace extends RaceState {
}
}
public void setRacePreStartTime ( long racePreStartTime ) {
this . racePreStartTime = racePreStartTime ;
}
public void setRacePreparatoryTime ( long racePreparatoryTime ) {
this . racePreparatoryTime = racePreparatoryTime ;
}
/ * *
/ * *
* Sets the status of all boats in the race to RACING .
* Sets the status of all boats in the race to RACING .
@ -356,70 +366,24 @@ public class MockRace extends RaceState {
//Checks if the current boat has finished the race or not.
//Checks if the current boat has finished the race or not.
boolean finish = this . isLastLeg ( boat . getCurrentLeg ( ) ) ;
boolean finish = this . isLastLeg ( boat . getCurrentLeg ( ) ) ;
if ( ! finish & & totalElapsedMilliseconds > = updatePeriodMilliseconds & & boat . isSailsOut ( ) ) {
if ( ! finish & & totalElapsedMilliseconds > = updatePeriodMilliseconds ) {
checkPosition ( boat , totalElapsedMilliseconds ) ;
if ( boat . getCurrentSpeed ( ) = = 0 ) {
newOptimalVMG ( boat ) ;
boat . setBearing ( boat . calculateBearingToNextMarker ( ) ) ;
}
setBoatSpeed ( boat ) ;
if ( boat . isVelocityDefault ( ) ) setBoatSpeed ( boat ) ;
//Calculates the distance travelled, in meters, in the current timeslice.
//Calculates the distance travelled, in meters, in the current timeslice.
double distanceTravelledMeters = boat . calculateMetersTravelled ( updatePeriodMilliseconds ) ;
double distanceTravelledMeters = boat . calculateMetersTravelled ( updatePeriodMilliseconds ) * this . scaleFactor ;
//Scale it.
distanceTravelledMeters = distanceTravelledMeters * this . scaleFactor ;
checkPosition ( boat , totalElapsedMilliseconds ) ;
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
boat . moveForwards ( distanceTravelledMeters ) ;
boat . moveForwards ( distanceTravelledMeters ) ;
boat . setTimeSinceTackChange ( boat . getTimeSinceTackChange ( ) + updatePeriodMilliseconds ) ;
boat . setTimeSinceTackChange ( boat . getTimeSinceTackChange ( ) + updatePeriodMilliseconds ) ;
if ( boat . getAutoVMG ( ) ) {
newOptimalVMG ( boat ) ;
boat . setAutoVMG ( false ) ;
}
} else {
boat . setCurrentSpeed ( 0 ) ;
}
}
this . updateEstimatedTime ( boat ) ;
this . updateEstimatedTime ( boat ) ;
}
}
private void newOptimalVMG ( MockBoat boat ) {
long tackPeriod = 1000 ;
if ( boat . getTimeSinceTackChange ( ) > tackPeriod ) {
//System.out.println("optim called");
//Calculate the new VMG.
// VMG newVMG = boat.getPolars().calculateVMG(
// this.getWindDirection(),
// this.getWindSpeed(),
// boat.calculateBearingToNextMarker(),
// Bearing.fromDegrees(0d),
// Bearing.fromDegrees(359.99999d));
VMG newVMG = NewPolars . setBestVMG ( this . getWindDirection ( ) , this . getWindSpeed ( ) , boat . getBearing ( ) ) ;
//System.out.println(newVMG);
//If the new vmg improves velocity, use it.
/ * if ( improvesVelocity ( boat , newVMG ) ) {
} * /
boat . setVMG ( newVMG ) ;
}
}
private void setBoatSpeed ( MockBoat boat ) {
private void setBoatSpeed ( MockBoat boat ) {
// VMG vmg = boat.getPolars().calculateVMG(
// this.getWindDirection(),
// this.getWindSpeed(),
// boat.getBearing(),
// Bearing.fromDegrees(boat.getBearing().degrees() - 1),
// Bearing.fromDegrees(boat.getBearing().degrees() + 1));
//VMG vmg = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
VMG vmg = new VMG ( NewPolars . calculateSpeed (
VMG vmg = new VMG ( NewPolars . calculateSpeed (
this . getWindDirection ( ) ,
this . getWindDirection ( ) ,
this . getWindSpeed ( ) ,
this . getWindSpeed ( ) ,
@ -515,22 +479,28 @@ public class MockRace extends RaceState {
/ * *
/ * *
* Checks to be run on boats rounding marks on the port side
* Checks to be run on boats rounding marks on the port side
* @param boat the boat that is rounding a mark
* @param boat the boat that is rounding a mark
* @param roundingChecks the checks to run
* @param roundingData The data for the current leg ' s rounding .
* @param legBearing the direction of the leg
* /
* /
private void boatRoundingCheckPort ( MockBoat boat , List< GPSCoordinate > roundingChecks , Bearing legBearing ) {
private void boatRoundingCheckPort ( MockBoat boat , MarkRoundingData roundingData ) {
//boats must pass all checks in order to round a mark
//boats must pass all checks in order to round a mark
//boolean for if boat has to/needs to pass through a gate
//boolean for if boat has to/needs to pass through a gate
boolean gateCheck = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMark2 ( ) = = null | | boat . isBetweenGate ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) ) ;
boolean gateCheck = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMark2 ( ) = = null | | boat . isBetweenGate ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) ) ;
Mark roundingMark = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMarkForRounding ( legBearing ) ;
switch ( boat . getRoundingStatus ( ) ) {
switch ( boat . getRoundingStatus ( ) ) {
case 0 : //hasn't started rounding
case 0 : //hasn't started rounding
if ( boat . isPortSide ( roundingMark ) & &
if ( boat . isPortSide ( roundingData . getMarkToRound ( ) ) & &
GPSCoordinate . passesLine ( roundingMark . getPosition ( ) ,
GPSCoordinate . passesLine (
roundingChecks . get ( 0 ) , boat . getPosition ( ) , legBearing ) & &
roundingData . getMarkToRound ( ) . getPosition ( ) ,
gateCheck & & boat . isBetweenGate ( roundingMark , Mark . tempMark ( roundingChecks . get ( 0 ) ) ) ) {
roundingData . getRoundCheck1 ( ) ,
boat . getPosition ( ) ,
roundingData . getLegBearing ( ) ) & &
gateCheck & &
boat . isBetweenGate (
roundingData . getMarkToRound ( ) ,
Mark . tempMark ( roundingData . getRoundCheck1 ( ) ) ) ) {
boat . increaseRoundingStatus ( ) ;
boat . increaseRoundingStatus ( ) ;
if ( boat . getCurrentLeg ( ) . getLegNumber ( ) + 2 > = getLegs ( ) . size ( ) ) {
if ( boat . getCurrentLeg ( ) . getLegNumber ( ) + 2 > = getLegs ( ) . size ( ) ) {
//boat has finished race
//boat has finished race
@ -539,11 +509,18 @@ public class MockRace extends RaceState {
}
}
break ;
break ;
case 1 : //has been parallel to the mark;
case 1 : //has been parallel to the mark;
if ( boat . isPortSide ( roundingMark ) & &
if ( boat . isPortSide ( roundingData . getMarkToRound ( ) ) & &
GPSCoordinate . passesLine ( roundingMark . getPosition ( ) ,
GPSCoordinate . passesLine (
roundingChecks . get ( 1 ) , boat . getPosition ( ) ,
roundingData . getMarkToRound ( ) . getPosition ( ) ,
Bearing . fromDegrees ( legBearing . degrees ( ) - 90 ) ) & & //negative 90 from bearing because of port rounding
roundingData . getRoundCheck2 ( ) ,
boat . isBetweenGate ( roundingMark , Mark . tempMark ( roundingChecks . get ( 1 ) ) ) ) {
boat . getPosition ( ) ,
Bearing . fromDegrees (
GPSCoordinate . calculateBearing (
roundingData . getMarkToRound ( ) . getPosition ( ) ,
roundingData . getRoundCheck2 ( ) ) . degrees ( ) - 90 ) ) & & //negative 90 from bearing because of port rounding
boat . isBetweenGate (
roundingData . getMarkToRound ( ) ,
Mark . tempMark ( roundingData . getRoundCheck2 ( ) ) ) ) {
boat . increaseRoundingStatus ( ) ;
boat . increaseRoundingStatus ( ) ;
}
}
break ;
break ;
@ -559,23 +536,27 @@ public class MockRace extends RaceState {
/ * *
/ * *
* Checks to be run on boats rounding marks on the starboard side
* Checks to be run on boats rounding marks on the starboard side
* @param boat the boat that is rounding a mark
* @param boat the boat that is rounding a mark
* @param roundingChecks the checks to run
* @param roundingData The data for the current leg ' s rounding .
* @param legBearing the direction of the leg
* /
* /
private void boatRoundingCheckStarboard ( MockBoat boat , List< GPSCoordinate > roundingChecks , Bearing legBearing ) {
private void boatRoundingCheckStarboard ( MockBoat boat , MarkRoundingData roundingData ) {
//boats must pass all checks in order to round a mark
//boats must pass all checks in order to round a mark
//boolean for if boat has to/needs to pass through a gate
//boolean for if boat has to/needs to pass through a gate
boolean gateCheck = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMark2 ( ) = = null | | boat . isBetweenGate ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) ) ;
boolean gateCheck = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMark2 ( ) = = null | | boat . isBetweenGate ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) ) ;
Mark roundingMark = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMarkForRounding ( legBearing ) ;
switch ( boat . getRoundingStatus ( ) ) {
switch ( boat . getRoundingStatus ( ) ) {
case 0 : //hasn't started rounding
case 0 : //hasn't started rounding
if ( boat . isStarboardSide ( roundingMark ) & &
if ( boat . isStarboardSide ( roundingData . getMarkToRound ( ) ) & &
GPSCoordinate . passesLine ( roundingMark . getPosition ( ) ,
GPSCoordinate . passesLine (
roundingChecks . get ( 0 ) , boat . getPosition ( ) , legBearing ) & &
roundingData . getMarkToRound ( ) . getPosition ( ) ,
roundingData . getRoundCheck1 ( ) ,
boat . getPosition ( ) ,
roundingData . getLegBearing ( ) ) & &
gateCheck & &
gateCheck & &
boat . isBetweenGate ( roundingMark , Mark . tempMark ( roundingChecks . get ( 0 ) ) ) ) {
boat . isBetweenGate (
roundingData . getMarkToRound ( ) ,
Mark . tempMark ( roundingData . getRoundCheck1 ( ) ) ) ) {
boat . increaseRoundingStatus ( ) ;
boat . increaseRoundingStatus ( ) ;
if ( boat . getCurrentLeg ( ) . getLegNumber ( ) + 2 > = getLegs ( ) . size ( ) ) {
if ( boat . getCurrentLeg ( ) . getLegNumber ( ) + 2 > = getLegs ( ) . size ( ) ) {
//boat has finished race
//boat has finished race
@ -584,10 +565,18 @@ public class MockRace extends RaceState {
}
}
break ;
break ;
case 1 : //has been parallel to the mark
case 1 : //has been parallel to the mark
if ( boat . isStarboardSide ( roundingMark ) & &
if ( boat . isStarboardSide ( roundingData . getMarkToRound ( ) ) & &
GPSCoordinate . passesLine ( roundingMark . getPosition ( ) ,
GPSCoordinate . passesLine (
roundingChecks . get ( 1 ) , boat . getPosition ( ) , Bearing . fromDegrees ( legBearing . degrees ( ) + 90 ) ) & & //positive 90 from bearing because of starboard rounding
roundingData . getMarkToRound ( ) . getPosition ( ) ,
boat . isBetweenGate ( roundingMark , Mark . tempMark ( roundingChecks . get ( 1 ) ) ) ) {
roundingData . getRoundCheck2 ( ) ,
boat . getPosition ( ) ,
Bearing . fromDegrees (
GPSCoordinate . calculateBearing (
roundingData . getMarkToRound ( ) . getPosition ( ) ,
roundingData . getRoundCheck2 ( ) ) . degrees ( ) + 90 ) ) & & //positive 90 from bearing because of starboard rounding
boat . isBetweenGate (
roundingData . getMarkToRound ( ) ,
Mark . tempMark ( roundingData . getRoundCheck2 ( ) ) ) ) {
boat . increaseRoundingStatus ( ) ;
boat . increaseRoundingStatus ( ) ;
}
}
break ;
break ;
@ -606,67 +595,29 @@ public class MockRace extends RaceState {
* @param timeElapsed The total time , in milliseconds , that has elapsed since the race started .
* @param timeElapsed The total time , in milliseconds , that has elapsed since the race started .
* /
* /
protected void checkPosition ( MockBoat boat , long timeElapsed ) {
protected void checkPosition ( MockBoat boat , long timeElapsed ) {
//The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker.
double epsilonNauticalMiles = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getRoundingDistance ( ) ; //250 meters.
if ( boat . calculateDistanceToNextMarker ( ) < epsilonNauticalMiles ) {
//Boat is within an acceptable distance from the mark.
GPSCoordinate startDirectionLinePoint = boat . getCurrentLeg ( ) . getStartCompoundMark ( ) . getMark1Position ( ) ;
GPSCoordinate endDirectionLinePoint = boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getMark1Position ( ) ;
Bearing bearingOfDirectionLine = GPSCoordinate . calculateBearing ( startDirectionLinePoint , endDirectionLinePoint ) ;
//use the direction line to create three invisible points that act as crossover lines a boat must cross
//to round a mark
double bearingToAdd ;
if ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getRoundingType ( ) = = RoundingType . Port | |
boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getRoundingType ( ) = = RoundingType . SP ) {
bearingToAdd = 90 ;
} else {
bearingToAdd = - 90 ;
}
GPSCoordinate roundCheck1 = GPSCoordinate . calculateNewPosition ( endDirectionLinePoint ,
epsilonNauticalMiles , Azimuth . fromDegrees ( bearingOfDirectionLine . degrees ( ) + bearingToAdd ) ) ;
GPSCoordinate roundCheck2 ;
try {
Leg nextLeg = getLegs ( ) . get ( getLegs ( ) . indexOf ( boat . getCurrentLeg ( ) ) + 1 ) ;
GPSCoordinate startNextDirectionLinePoint = nextLeg . getStartCompoundMark ( ) . getMark1Position ( ) ;
GPSCoordinate endNextDirectionLinePoint = nextLeg . getEndCompoundMark ( ) . getMark1Position ( ) ;
Bearing bearingOfNextDirectionLine = GPSCoordinate . calculateBearing ( startNextDirectionLinePoint , endNextDirectionLinePoint ) ;
roundCheck2 = GPSCoordinate . calculateNewPosition ( endDirectionLinePoint ,
epsilonNauticalMiles , Azimuth . fromDegrees ( bearingOfNextDirectionLine . degrees ( ) + bearingToAdd ) ) ;
} catch ( NullPointerException e ) {
//this is caused by the last leg not being having a leg after it
roundCheck2 = roundCheck1 ;
}
List < GPSCoordinate > roundingChecks = new ArrayList < GPSCoordinate > ( Arrays . asList ( roundCheck1 , roundCheck2 ) ) ;
switch ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getRoundingType ( ) ) {
case SP : //Not yet implemented so these gates will be rounded port side
case Port :
boatRoundingCheckPort ( boat , roundingChecks , bearingOfDirectionLine ) ;
break ;
case PS : //not yet implemented so these gates will be rounded starboard side
case Starboard :
boatRoundingCheckStarboard ( boat , roundingChecks , bearingOfDirectionLine ) ;
break ;
}
switch ( boat . getCurrentLeg ( ) . getEndCompoundMark ( ) . getRoundingType ( ) ) {
case SP : //Not yet implemented so these gates will be rounded port side
case Port :
boatRoundingCheckPort (
boat ,
getMarkRoundingSequence ( ) . getRoundingData ( boat . getCurrentLeg ( ) ) ) ;
break ;
case PS : //not yet implemented so these gates will be rounded starboard side
case Starboard :
boatRoundingCheckStarboard (
boat ,
getMarkRoundingSequence ( ) . getRoundingData ( boat . getCurrentLeg ( ) ) ) ;
break ;
}
//Check if the boat has finished or stopped racing.
if ( this . isLastLeg ( boat . getCurrentLeg ( ) ) ) {
//Boat has finished.
boat . setTimeFinished ( timeElapsed ) ;
boat . setCurrentSpeed ( 0 ) ;
boat . setStatus ( BoatStatusEnum . FINISHED ) ;
}
//Check if the boat has finished or stopped racing.
if ( this . isLastLeg ( boat . getCurrentLeg ( ) ) ) {
//Boat has finished.
boat . setTimeFinished ( timeElapsed ) ;
boat . setCurrentSpeed ( 0 ) ;
boat . setStatus ( BoatStatusEnum . FINISHED ) ;
}
}
@ -755,4 +706,18 @@ public class MockRace extends RaceState {
}
}
/ * *
* Made public , so race logic can control it
* /
public void setChanged ( ) {
super . setChanged ( ) ;
}
public void addVelocityCommand ( ObserverCommand c ) {
this . activeObserverCommand . changeVelocityCommand ( this , c ) ;
}
public void addAngularCommand ( ObserverCommand c ) {
this . activeObserverCommand . changeAngularCommand ( this , c ) ;
}
}
}