Merge remote-tracking branch 'origin/master' into issue_28_36_decoders
# Conflicts: # racevisionGame/src/main/java/mock/app/ConnectionAcceptor.java # racevisionGame/src/main/java/mock/app/Event.java # racevisionGame/src/main/java/visualiser/gameController/ControllerServer.java
This commit is contained in:
@@ -1,20 +1,17 @@
|
|||||||
package mock.app;
|
package mock.app;
|
||||||
|
|
||||||
|
import mock.model.RaceLogic;
|
||||||
import network.Messages.Enums.XMLMessageType;
|
import network.Messages.Enums.XMLMessageType;
|
||||||
import network.Messages.LatestMessages;
|
import network.Messages.LatestMessages;
|
||||||
import network.Messages.XMLMessage;
|
import network.Messages.XMLMessage;
|
||||||
import org.mockito.Mock;
|
|
||||||
import visualiser.gameController.ControllerServer;
|
import visualiser.gameController.ControllerServer;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,6 +40,10 @@ public class ConnectionAcceptor implements Runnable {
|
|||||||
private short boatXMLSequenceNumber;
|
private short boatXMLSequenceNumber;
|
||||||
//regatta xml sequence number
|
//regatta xml sequence number
|
||||||
private short regattaXMLSequenceNumber;
|
private short regattaXMLSequenceNumber;
|
||||||
|
//controller server
|
||||||
|
private ControllerServer controllerServer;
|
||||||
|
//
|
||||||
|
private RaceLogic rl = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection Acceptor Constructor
|
* Connection Acceptor Constructor
|
||||||
@@ -65,6 +66,11 @@ public class ConnectionAcceptor implements Runnable {
|
|||||||
return serverPort;
|
return serverPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setRace(RaceLogic rl){
|
||||||
|
this.rl = rl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the Acceptor
|
* Run the Acceptor
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -94,7 +94,9 @@ public class Event {
|
|||||||
//Create and start race.
|
//Create and start race.
|
||||||
RaceLogic newRace = new RaceLogic(new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.latestMessages, this.boatPolars, Constants.RaceTimeScale), this.latestMessages);
|
RaceLogic newRace = new RaceLogic(new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.latestMessages, this.boatPolars, Constants.RaceTimeScale), this.latestMessages);
|
||||||
|
|
||||||
|
connectionAcceptor.setRace(newRace);
|
||||||
new Thread(newRace, "Event.Start()->RaceLogic thread").start();
|
new Thread(newRace, "Event.Start()->RaceLogic thread").start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class MockBoat extends Boat {
|
|||||||
/**
|
/**
|
||||||
* Stores whether the boat is on autoVMG or not
|
* Stores whether the boat is on autoVMG or not
|
||||||
*/
|
*/
|
||||||
private boolean autoVMG = true;
|
private boolean autoVMG = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
package mock.model;
|
package mock.model;
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
|
import mock.model.commandFactory.Command;
|
||||||
|
import mock.model.commandFactory.CommandFactory;
|
||||||
|
import mock.model.commandFactory.CompositeCommand;
|
||||||
|
import network.Messages.Enums.BoatActionEnum;
|
||||||
import network.Messages.Enums.BoatStatusEnum;
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
import network.Messages.LatestMessages;
|
import network.Messages.LatestMessages;
|
||||||
import shared.model.Race;
|
import visualiser.gameController.ControllerServer;
|
||||||
|
|
||||||
public class RaceLogic implements Runnable {
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class RaceLogic implements Observer, Runnable {
|
||||||
/**
|
/**
|
||||||
* State of current race modified by this object
|
* State of current race modified by this object
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +24,8 @@ public class RaceLogic implements Runnable {
|
|||||||
*/
|
*/
|
||||||
private RaceServer server;
|
private RaceServer server;
|
||||||
|
|
||||||
|
private CompositeCommand commands;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises race loop with state and server message queue
|
* Initialises race loop with state and server message queue
|
||||||
* @param race state of race to modify
|
* @param race state of race to modify
|
||||||
@@ -24,6 +34,7 @@ public class RaceLogic implements Runnable {
|
|||||||
public RaceLogic(MockRace race, LatestMessages messages) {
|
public RaceLogic(MockRace race, LatestMessages messages) {
|
||||||
this.race = race;
|
this.race = race;
|
||||||
this.server = new RaceServer(race, messages);
|
this.server = new RaceServer(race, messages);
|
||||||
|
this.commands = new CompositeCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +134,7 @@ public class RaceLogic implements Runnable {
|
|||||||
|
|
||||||
//If it is still racing, update its position.
|
//If it is still racing, update its position.
|
||||||
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
||||||
|
commands.execute();
|
||||||
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -173,4 +184,13 @@ public class RaceLogic implements Runnable {
|
|||||||
iters++;
|
iters++;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
ControllerServer server = (ControllerServer)o;
|
||||||
|
BoatActionEnum action = server.getAction();
|
||||||
|
MockBoat boat = race.getBoats().get(0);
|
||||||
|
|
||||||
|
commands.addCommand(CommandFactory.createCommand(race, boat, action));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ public class CommandFactory {
|
|||||||
switch(action) {
|
switch(action) {
|
||||||
case AUTO_PILOT: return new VMGCommand(race, boat);
|
case AUTO_PILOT: return new VMGCommand(race, boat);
|
||||||
case TACK_GYBE: return new TackGybeCommand(race, boat);
|
case TACK_GYBE: return new TackGybeCommand(race, boat);
|
||||||
|
case UPWIND: return new WindCommand(race, boat, true);
|
||||||
|
case DOWNWIND: return new WindCommand(race, boat, false);
|
||||||
default: return null; // TODO - please please have discussion over what to default to
|
default: return null; // TODO - please please have discussion over what to default to
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps multiple commands into a composite to execute queued commands during a frame.
|
||||||
|
*/
|
||||||
|
public class CompositeCommand implements Command {
|
||||||
|
private Queue<Command> commands;
|
||||||
|
|
||||||
|
public CompositeCommand() {
|
||||||
|
this.commands = new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCommand(Command command) {
|
||||||
|
commands.add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
while(!commands.isEmpty()) commands.remove().execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ public class TackGybeCommand implements Command {
|
|||||||
//The refactoring of MockRace will require changes to be made
|
//The refactoring of MockRace will require changes to be made
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
|
|
||||||
|
boat.setAutoVMG(false);
|
||||||
|
|
||||||
/*VMG newVMG = boat.getPolars().calculateVMG(
|
/*VMG newVMG = boat.getPolars().calculateVMG(
|
||||||
race.getWindDirection(),
|
race.getWindDirection(),
|
||||||
race.getWindSpeed(),
|
race.getWindSpeed(),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class VMGCommand implements Command {
|
|||||||
//The refactoring of MockRace will require changes to be made
|
//The refactoring of MockRace will require changes to be made
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
|
boat.setAutoVMG(true);
|
||||||
/*VMG newVMG = boat.getPolars().calculateVMG(
|
/*VMG newVMG = boat.getPolars().calculateVMG(
|
||||||
race.getWindDirection(),
|
race.getWindDirection(),
|
||||||
race.getWindSpeed(),
|
race.getWindSpeed(),
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import mock.model.MockBoat;
|
||||||
|
import mock.model.MockRace;
|
||||||
|
import shared.model.Bearing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by connortaylorbrown on 4/08/17.
|
||||||
|
*/
|
||||||
|
public class WindCommand implements Command {
|
||||||
|
private MockRace race;
|
||||||
|
private MockBoat boat;
|
||||||
|
private int direction;
|
||||||
|
|
||||||
|
public WindCommand(MockRace race, MockBoat boat, boolean upwind) {
|
||||||
|
this.race = race;
|
||||||
|
this.boat = boat;
|
||||||
|
this.direction = upwind? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
|
||||||
|
boat.setAutoVMG(false);
|
||||||
|
|
||||||
|
double wind = race.getWindDirection().degrees();
|
||||||
|
double heading = boat.getBearing().degrees();
|
||||||
|
|
||||||
|
double offset = 3.0;
|
||||||
|
|
||||||
|
offset *= direction;
|
||||||
|
double headWindDelta = wind - heading;
|
||||||
|
if ((headWindDelta < 0) || (headWindDelta > 180)) offset *= -1;
|
||||||
|
|
||||||
|
boat.setBearing(Bearing.fromDegrees(heading + offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
import shared.model.Bearing;
|
||||||
|
import shared.model.Wind;
|
||||||
|
import visualiser.model.VisualiserRace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the arrow.fxml view.
|
||||||
|
*/
|
||||||
|
public class ArrowController {
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane compass;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private StackPane arrowStackPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView arrowImage;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Circle circle;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label northLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label windLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label speedLabel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the property our arrow control binds to.
|
||||||
|
*/
|
||||||
|
private Property<Wind> wind;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public ArrowController() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets which wind property the arrow control should bind to.
|
||||||
|
* @param wind The wind property to bind to.
|
||||||
|
*/
|
||||||
|
public void setWindProperty(Property<Wind> wind) {
|
||||||
|
this.wind = wind;
|
||||||
|
|
||||||
|
wind.addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (newValue != null) {
|
||||||
|
Platform.runLater(() -> updateWind(newValue));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the control to use the new wind value.
|
||||||
|
* This updates the arrow direction (due to bearing), arrow length (due to speed), and label (due to speed).
|
||||||
|
* @param wind The wind value to use.
|
||||||
|
*/
|
||||||
|
private void updateWind(Wind wind) {
|
||||||
|
updateWindBearing(wind.getWindDirection());
|
||||||
|
updateWindSpeed(wind.getWindSpeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the control to account for the new wind speed.
|
||||||
|
* This changes the length (height) of the wind arrow, and updates the speed label.
|
||||||
|
* @param speedKnots The new wind speed, in knots.
|
||||||
|
*/
|
||||||
|
private void updateWindSpeed(double speedKnots) {
|
||||||
|
updateWindArrowLength(speedKnots);
|
||||||
|
updateWindSpeedLabel(speedKnots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the length of the wind arrow according to the specified wind speed.
|
||||||
|
* @param speedKnots Wind speed, in knots.
|
||||||
|
*/
|
||||||
|
private void updateWindArrowLength(double speedKnots) {
|
||||||
|
|
||||||
|
//At 2 knots, the arrow reaches its minimum height, and at 30 knots it reaches its maximum height.
|
||||||
|
double minKnots = 2;
|
||||||
|
double maxKnots = 30;
|
||||||
|
double deltaKnots = maxKnots - minKnots;
|
||||||
|
|
||||||
|
double minHeight = 25;
|
||||||
|
double maxHeight = 75;
|
||||||
|
double deltaHeight = maxHeight - minHeight;
|
||||||
|
|
||||||
|
//Clamp speed.
|
||||||
|
if (speedKnots > maxKnots) {
|
||||||
|
speedKnots = maxKnots;
|
||||||
|
} else if (speedKnots < minKnots) {
|
||||||
|
speedKnots = minKnots;
|
||||||
|
}
|
||||||
|
|
||||||
|
//How far between the knots bounds is the current speed?
|
||||||
|
double currentDeltaKnots = speedKnots - minKnots;
|
||||||
|
double currentKnotsScalar = currentDeltaKnots / deltaKnots;
|
||||||
|
|
||||||
|
//Thus, how far between the pixel height bounds should the arrow height be?
|
||||||
|
double newHeight = minHeight + (currentKnotsScalar * deltaHeight);
|
||||||
|
|
||||||
|
arrowImage.setFitHeight(newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the wind speed label according to the specified wind speed.
|
||||||
|
* @param speedKnots Wind speed, in knots.
|
||||||
|
*/
|
||||||
|
private void updateWindSpeedLabel(double speedKnots) {
|
||||||
|
speedLabel.setText(String.format("%.1fkn", speedKnots));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the control to account for a new wind bearing.
|
||||||
|
* This rotates the arrow according to the bearing.
|
||||||
|
* @param bearing The bearing to use to rotate arrow.
|
||||||
|
*/
|
||||||
|
private void updateWindBearing(Bearing bearing) {
|
||||||
|
|
||||||
|
//We need to display wind-from, so add 180 degrees.
|
||||||
|
Bearing fromBearing = Bearing.fromDegrees(bearing.degrees() + 180d);
|
||||||
|
|
||||||
|
//Rotate the wind arrow.
|
||||||
|
arrowStackPane.setRotate(fromBearing.degrees());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import javafx.animation.AnimationTimer;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
@@ -24,10 +23,8 @@ import visualiser.gameController.Keys.ControlKey;
|
|||||||
import visualiser.gameController.Keys.KeyFactory;
|
import visualiser.gameController.Keys.KeyFactory;
|
||||||
import visualiser.model.*;
|
import visualiser.model.*;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,6 +57,11 @@ public class RaceController extends Controller {
|
|||||||
*/
|
*/
|
||||||
private Sparkline sparkline;
|
private Sparkline sparkline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arrow controller.
|
||||||
|
*/
|
||||||
|
@FXML private ArrowController arrowController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for sending keystrokes to server
|
* Service for sending keystrokes to server
|
||||||
*/
|
*/
|
||||||
@@ -67,8 +69,18 @@ public class RaceController extends Controller {
|
|||||||
|
|
||||||
|
|
||||||
@FXML private GridPane canvasBase;
|
@FXML private GridPane canvasBase;
|
||||||
@FXML private Pane arrow;
|
|
||||||
|
|
||||||
@FXML private SplitPane race;
|
@FXML private SplitPane race;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the root node of the arrow control.
|
||||||
|
*/
|
||||||
|
@FXML private Pane arrow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the pane we place the actual arrow control inside of.
|
||||||
|
*/
|
||||||
@FXML private StackPane arrowPane;
|
@FXML private StackPane arrowPane;
|
||||||
@FXML private Label timer;
|
@FXML private Label timer;
|
||||||
@FXML private Label FPS;
|
@FXML private Label FPS;
|
||||||
@@ -118,15 +130,15 @@ public class RaceController extends Controller {
|
|||||||
//Fps display.
|
//Fps display.
|
||||||
initialiseFps(this.visualiserRace);
|
initialiseFps(this.visualiserRace);
|
||||||
|
|
||||||
//Need to add the included arrow pane to the arrowPane container.
|
|
||||||
initialiseArrow();
|
|
||||||
|
|
||||||
//Information table.
|
//Information table.
|
||||||
initialiseInfoTable(this.visualiserRace);
|
initialiseInfoTable(this.visualiserRace);
|
||||||
|
|
||||||
//Sparkline.
|
//Sparkline.
|
||||||
initialiseSparkline(this.visualiserRace);
|
initialiseSparkline(this.visualiserRace);
|
||||||
|
|
||||||
|
//Arrow.
|
||||||
|
initialiseArrow(this.visualiserRace);
|
||||||
|
|
||||||
//Race canvas.
|
//Race canvas.
|
||||||
initialiseRaceCanvas(this.visualiserRace);
|
initialiseRaceCanvas(this.visualiserRace);
|
||||||
|
|
||||||
@@ -294,7 +306,7 @@ public class RaceController extends Controller {
|
|||||||
private void initialiseRaceCanvas(VisualiserRace race) {
|
private void initialiseRaceCanvas(VisualiserRace race) {
|
||||||
|
|
||||||
//Create canvas.
|
//Create canvas.
|
||||||
raceCanvas = new ResizableRaceCanvas(race, arrow.getChildren().get(0));
|
raceCanvas = new ResizableRaceCanvas(race);
|
||||||
|
|
||||||
//Set properties.
|
//Set properties.
|
||||||
raceCanvas.setMouseTransparent(true);
|
raceCanvas.setMouseTransparent(true);
|
||||||
@@ -367,10 +379,11 @@ public class RaceController extends Controller {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the included arrow pane (see arrow.fxml) to the arrowPane (see race.fxml).
|
* Initialises the arrow controller with data from the race to observe.
|
||||||
|
* @param race The race to observe.
|
||||||
*/
|
*/
|
||||||
private void initialiseArrow() {
|
private void initialiseArrow(VisualiserRace race) {
|
||||||
arrowPane.getChildren().add(arrow);
|
arrowController.setWindProperty(race.windProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
package visualiser.gameController;
|
package visualiser.gameController;
|
||||||
|
|
||||||
|
import mock.model.RaceLogic;
|
||||||
import network.BinaryMessageDecoder;
|
import network.BinaryMessageDecoder;
|
||||||
import network.Exceptions.InvalidMessageException;
|
import network.Exceptions.InvalidMessageException;
|
||||||
import network.MessageDecoders.BoatActionDecoder;
|
import network.MessageDecoders.BoatActionDecoder;
|
||||||
import network.Messages.BoatAction;
|
import network.Messages.BoatAction;
|
||||||
import network.Messages.Enums.BoatActionEnum;
|
import network.Messages.Enums.BoatActionEnum;
|
||||||
import visualiser.gameController.Keys.ControlKey;
|
|
||||||
import visualiser.gameController.Keys.KeyFactory;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.Observable;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for dispatching key press data to race from client
|
* Service for dispatching key press data to race from client
|
||||||
*/
|
*/
|
||||||
public class ControllerServer implements Runnable {
|
public class ControllerServer extends Observable implements Runnable {
|
||||||
/**
|
/**
|
||||||
* Socket to client
|
* Socket to client
|
||||||
*/
|
*/
|
||||||
@@ -26,13 +26,23 @@ public class ControllerServer implements Runnable {
|
|||||||
* Wrapper for input from client
|
* Wrapper for input from client
|
||||||
*/
|
*/
|
||||||
private DataInputStream inputStream;
|
private DataInputStream inputStream;
|
||||||
|
/**
|
||||||
|
* Last received boat action
|
||||||
|
*/
|
||||||
|
private BoatActionEnum action;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private RaceLogic rc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise server-side controller with live client socket
|
* Initialise server-side controller with live client socket
|
||||||
* @param socket to client
|
* @param socket to client
|
||||||
*/
|
*/
|
||||||
public ControllerServer(Socket socket) {
|
public ControllerServer(Socket socket, RaceLogic rc) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
this.rc = rc;
|
||||||
|
this.addObserver(rc);
|
||||||
try {
|
try {
|
||||||
this.inputStream = new DataInputStream(this.socket.getInputStream());
|
this.inputStream = new DataInputStream(this.socket.getInputStream());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -40,6 +50,10 @@ public class ControllerServer implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BoatActionEnum getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for controller key input from client and loop.
|
* Wait for controller key input from client and loop.
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +72,11 @@ public class ControllerServer implements Runnable {
|
|||||||
try {
|
try {
|
||||||
boatActionDecoder.decode(encodedMessage.getMessageBody());
|
boatActionDecoder.decode(encodedMessage.getMessageBody());
|
||||||
BoatAction boatAction = boatActionDecoder.getMessage();
|
BoatAction boatAction = boatActionDecoder.getMessage();
|
||||||
System.out.println("Received key: " + boatAction.getBoatAction());
|
action = boatActionDecoder.getBoatAction();
|
||||||
|
|
||||||
|
// Notify observers of most recent action
|
||||||
|
this.notifyObservers();
|
||||||
|
this.setChanged();
|
||||||
|
|
||||||
} catch (InvalidMessageException e) {
|
} catch (InvalidMessageException e) {
|
||||||
Logger.getGlobal().log(Level.WARNING, "Could not decode BoatAction message.", e);
|
Logger.getGlobal().log(Level.WARNING, "Could not decode BoatAction message.", e);
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ public class KeyFactory {
|
|||||||
keyState.put("SPACE", new VMGKey("VMG"));
|
keyState.put("SPACE", new VMGKey("VMG"));
|
||||||
keyState.put("SHIFT", new SailsToggleKey("Toggle Sails"));
|
keyState.put("SHIFT", new SailsToggleKey("Toggle Sails"));
|
||||||
keyState.put("ENTER", new TackGybeKey("Tack/Gybe"));
|
keyState.put("ENTER", new TackGybeKey("Tack/Gybe"));
|
||||||
keyState.put("PAGE_UP", new UpWindKey("Upwind"));
|
keyState.put("UP", new UpWindKey("Upwind"));
|
||||||
keyState.put("PAGE_DOWN", new DownWindKey("Downwind"));
|
keyState.put("DOWN", new DownWindKey("Downwind"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import shared.model.GPSCoordinate;
|
|||||||
import shared.model.Mark;
|
import shared.model.Mark;
|
||||||
import shared.model.RaceClock;
|
import shared.model.RaceClock;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,12 +38,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
*/
|
*/
|
||||||
private VisualiserRace visualiserRace;
|
private VisualiserRace visualiserRace;
|
||||||
|
|
||||||
/**
|
|
||||||
* The background of the race.
|
|
||||||
* We render the background whenever the race boundary changes, or the screen size changes.
|
|
||||||
*/
|
|
||||||
private Image background;
|
|
||||||
|
|
||||||
|
|
||||||
private boolean annoName = true;
|
private boolean annoName = true;
|
||||||
private boolean annoAbbrev = true;
|
private boolean annoAbbrev = true;
|
||||||
@@ -54,22 +47,15 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
private boolean annoTimeSinceLastMark = true;
|
private boolean annoTimeSinceLastMark = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The wind arrow node.
|
|
||||||
*/
|
|
||||||
private Node arrow;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link ResizableRaceCanvas} using a given {@link VisualiserRace}.
|
* Constructs a {@link ResizableRaceCanvas} using a given {@link VisualiserRace}.
|
||||||
* @param visualiserRace The race that data is read from in order to be drawn.
|
* @param visualiserRace The race that data is read from in order to be drawn.
|
||||||
* @param arrow The wind arrow's node.
|
|
||||||
*/
|
*/
|
||||||
public ResizableRaceCanvas(VisualiserRace visualiserRace, Node arrow) {
|
public ResizableRaceCanvas(VisualiserRace visualiserRace) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.visualiserRace = visualiserRace;
|
this.visualiserRace = visualiserRace;
|
||||||
this.arrow = arrow;
|
|
||||||
|
|
||||||
RaceDataSource raceData = visualiserRace.getRaceDataSource();
|
RaceDataSource raceData = visualiserRace.getRaceDataSource();
|
||||||
|
|
||||||
@@ -375,32 +361,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays an arrow representing wind direction on the Canvas.
|
|
||||||
* This function accepts a wind-to bearing, but displays a wind-from bearing.
|
|
||||||
*
|
|
||||||
* @param angle Angle that the arrow is to be facing in degrees 0 degrees = North (Up).
|
|
||||||
* @see GraphCoordinate
|
|
||||||
*/
|
|
||||||
private void displayWindArrow(double angle) {
|
|
||||||
|
|
||||||
//We need to display wind-from, so add 180 degrees.
|
|
||||||
angle += 180d;
|
|
||||||
|
|
||||||
//Get it within [0, 360).
|
|
||||||
while (angle >= 360d) {
|
|
||||||
angle -= 360d;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Rotate the wind arrow.
|
|
||||||
if (arrow != null && arrow.getRotate() != angle) {
|
|
||||||
arrow.setRotate(angle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws all of the {@link Mark}s on the canvas.
|
* Draws all of the {@link Mark}s on the canvas.
|
||||||
*/
|
*/
|
||||||
@@ -440,13 +400,9 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
this.map.setWidth((int) getWidth());
|
this.map.setWidth((int) getWidth());
|
||||||
this.map.setHeight((int) getHeight());
|
this.map.setHeight((int) getHeight());
|
||||||
|
|
||||||
//Redraw the boundary.
|
|
||||||
redrawBoundaryImage();
|
|
||||||
|
|
||||||
//Draw the race.
|
//Draw the race.
|
||||||
drawRace();
|
drawRace();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -459,15 +415,13 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the race boundary, and saves the image to {@link #background}.
|
* Draws the race boundary.
|
||||||
* You should call {@link #clear()} before calling this.
|
|
||||||
*/
|
*/
|
||||||
private void redrawBoundaryImage() {
|
private void drawBoundary() {
|
||||||
|
|
||||||
//Prepare to draw.
|
//Prepare to draw.
|
||||||
gc.setLineWidth(1);
|
gc.setLineWidth(1);
|
||||||
gc.setFill(Color.AQUA);
|
gc.setFill(Color.AQUA);
|
||||||
gc.drawImage(new Image(getClass().getClassLoader().getResourceAsStream("images/WaterBackground.png")), 0, 0);
|
|
||||||
|
|
||||||
|
|
||||||
//Calculate the screen coordinates of the boundary.
|
//Calculate the screen coordinates of the boundary.
|
||||||
@@ -487,9 +441,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
//Draw the boundary.
|
//Draw the boundary.
|
||||||
gc.fillPolygon(xpoints, ypoints, xpoints.length);
|
gc.fillPolygon(xpoints, ypoints, xpoints.length);
|
||||||
|
|
||||||
//Render boundary to image.
|
|
||||||
this.background = snapshot(null, null);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -511,18 +462,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
//Marks.
|
//Marks.
|
||||||
drawMarks();
|
drawMarks();
|
||||||
|
|
||||||
//Wind arrow. This rotates the wind arrow node.
|
|
||||||
displayWindArrow(this.visualiserRace.getWindDirection().degrees());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the race boundary image onto the canvas.
|
|
||||||
* See {@link #background}.
|
|
||||||
*/
|
|
||||||
private void drawBoundary() {
|
|
||||||
gc.drawImage(this.background, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -51,3 +51,5 @@
|
|||||||
.scroll-bar > .decrement-button:pressed > .decrement-arrow {
|
.scroll-bar > .decrement-button:pressed > .decrement-arrow {
|
||||||
-fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255);
|
-fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -52,3 +52,8 @@
|
|||||||
.scroll-bar > .decrement-button:pressed > .decrement-arrow {
|
.scroll-bar > .decrement-button:pressed > .decrement-arrow {
|
||||||
-fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255);
|
-fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#arrowImage {
|
||||||
|
-fx-image: url("/visualiser/images/arrowLight.png");
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -1,34 +1,58 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.paint.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.text.*?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.image.Image?>
|
||||||
<?import javafx.scene.shape.*?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.image.*?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
<?import java.lang.*?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<Pane fx:id="compass" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0" prefWidth="125.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
|
<GridPane fx:id="arrowGridPane" maxHeight="-Infinity" maxWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.ArrowController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<StackPane fx:id="arrow" prefHeight="125.0" prefWidth="125.0">
|
<Pane fx:id="compass" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0" prefWidth="125.0">
|
||||||
<children>
|
<children>
|
||||||
<ImageView fitHeight="75.0" fitWidth="75.0">
|
<StackPane fx:id="arrowStackPane" prefHeight="125.0" prefWidth="125.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="arrowImage" fitHeight="75.0" fitWidth="75.0">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../images/arrow.png" />
|
<Image url="@../images/arrow.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</children>
|
</children>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<Circle fill="#1f93ff00" layoutX="63.0" layoutY="63.0" radius="60.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="3.0" />
|
<Circle fx:id="circle" fill="#1f93ff00" layoutX="63.0" layoutY="63.0" radius="60.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="3.0" />
|
||||||
<Label layoutX="55.0" layoutY="1.0" text="N">
|
<Label fx:id="northLabel" layoutX="55.0" layoutY="1.0" text="N">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="18.0" />
|
<Font name="System Bold" size="18.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Label layoutX="42.0" layoutY="99.0" text="Wind">
|
<Label fx:id="windLabel" layoutX="42.0" layoutY="95.0" text="Wind">
|
||||||
<font>
|
<font>
|
||||||
<Font name="System Bold" size="16.0" />
|
<Font name="System Bold" size="16.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
</children>
|
</children>
|
||||||
</Pane>
|
</Pane>
|
||||||
|
<Label fx:id="speedLabel" text="SPEED" GridPane.halignment="CENTER" GridPane.hgrow="NEVER" GridPane.rowIndex="1">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="16.0" />
|
||||||
|
</font>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<fx:include fx:id="arrow" source="arrow.fxml" />
|
|
||||||
<Pane prefHeight="200.0" prefWidth="400.0" GridPane.halignment="LEFT" GridPane.valignment="TOP">
|
<Pane prefHeight="200.0" prefWidth="400.0" GridPane.halignment="LEFT" GridPane.valignment="TOP">
|
||||||
<children>
|
<children>
|
||||||
<Accordion>
|
<Accordion>
|
||||||
@@ -76,7 +75,11 @@
|
|||||||
<Font name="System Bold" size="15.0" />
|
<Font name="System Bold" size="15.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<StackPane fx:id="arrowPane" alignment="TOP_RIGHT" mouseTransparent="true" prefHeight="150.0" prefWidth="150.0" snapToPixel="false" />
|
<StackPane fx:id="arrowPane" alignment="TOP_RIGHT" mouseTransparent="true" prefHeight="150.0" prefWidth="150.0" snapToPixel="false">
|
||||||
|
<children>
|
||||||
|
<fx:include fx:id="arrow" source="arrow.fxml" />
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
</children>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
<AnchorPane layoutX="450.0" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="200.0" GridPane.columnIndex="1">
|
<AnchorPane layoutX="450.0" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="200.0" GridPane.columnIndex="1">
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package mock.model;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import shared.model.Bearing;
|
||||||
|
import shared.model.Wind;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class WindGeneratorTest {
|
||||||
|
|
||||||
|
|
||||||
|
private WindGenerator windGenerator;
|
||||||
|
|
||||||
|
private Bearing windBaselineBearing;
|
||||||
|
private Bearing windBearingLowerBound;
|
||||||
|
private Bearing windBearingUpperBound;
|
||||||
|
private double windBaselineSpeed;
|
||||||
|
private double windSpeedLowerBound;
|
||||||
|
private double windSpeedUpperBound;
|
||||||
|
|
||||||
|
private double speedKnotsEpsilon;
|
||||||
|
private double bearingDegreeEpsilon;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
|
||||||
|
//Bounds.
|
||||||
|
this.windBaselineBearing = Bearing.fromDegrees(88.3);
|
||||||
|
this.windBearingLowerBound = Bearing.fromDegrees(66.5);
|
||||||
|
this.windBearingUpperBound = Bearing.fromDegrees(248.6);
|
||||||
|
this.windBaselineSpeed = 13;
|
||||||
|
this.windSpeedLowerBound = 7;
|
||||||
|
this.windSpeedUpperBound = 20;
|
||||||
|
|
||||||
|
this.windGenerator = new WindGenerator(
|
||||||
|
windBaselineBearing,
|
||||||
|
windBearingLowerBound,
|
||||||
|
windBearingUpperBound,
|
||||||
|
windBaselineSpeed,
|
||||||
|
windSpeedLowerBound,
|
||||||
|
windSpeedUpperBound );
|
||||||
|
|
||||||
|
this.bearingDegreeEpsilon = 0.001;
|
||||||
|
this.speedKnotsEpsilon = 0.001;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the baseline wind generated it accurate.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void generateBaselineWindTest() {
|
||||||
|
|
||||||
|
Wind wind = windGenerator.generateBaselineWind();
|
||||||
|
|
||||||
|
assertEquals(windBaselineSpeed, wind.getWindSpeed(), speedKnotsEpsilon);
|
||||||
|
assertEquals(windBaselineBearing.degrees(), wind.getWindDirection().degrees(), bearingDegreeEpsilon);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the random wind generated is inside the bounds.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void generateRandomWindTest() {
|
||||||
|
|
||||||
|
int randomWindCount = 1000;
|
||||||
|
|
||||||
|
for (int i = 0; i < randomWindCount; i++) {
|
||||||
|
|
||||||
|
Wind wind = windGenerator.generateRandomWind();
|
||||||
|
|
||||||
|
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
|
||||||
|
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
|
||||||
|
|
||||||
|
assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees());
|
||||||
|
assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the next wind generated is inside the bounds.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void generateNextWindTest() {
|
||||||
|
|
||||||
|
Wind wind = windGenerator.generateBaselineWind();
|
||||||
|
|
||||||
|
int randomWindCount = 1000;
|
||||||
|
|
||||||
|
for (int i = 0; i < randomWindCount; i++) {
|
||||||
|
|
||||||
|
wind = windGenerator.generateNextWind(wind);
|
||||||
|
|
||||||
|
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
|
||||||
|
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
|
||||||
|
|
||||||
|
assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees());
|
||||||
|
assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package mock.model.commandFactory;
|
||||||
|
|
||||||
|
import mock.model.MockBoat;
|
||||||
|
import mock.model.MockRace;
|
||||||
|
import network.Messages.Enums.BoatActionEnum;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import shared.model.Bearing;
|
||||||
|
import shared.model.Boat;
|
||||||
|
import shared.model.Race;
|
||||||
|
import visualiser.model.VisualiserRace;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by connortaylorbrown on 4/08/17.
|
||||||
|
*/
|
||||||
|
public class WindCommandTest {
|
||||||
|
private MockRace race;
|
||||||
|
private MockBoat boat;
|
||||||
|
private Command upwind;
|
||||||
|
private Command downwind;
|
||||||
|
private double initial;
|
||||||
|
|
||||||
|
private double offset = 3.0;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
race = mock(MockRace.class);
|
||||||
|
boat = new MockBoat(0, "Bob", "NZ", null);
|
||||||
|
|
||||||
|
when(race.getWindDirection()).thenReturn(Bearing.fromDegrees(0.0));
|
||||||
|
boat.setBearing(Bearing.fromDegrees(45.0));
|
||||||
|
|
||||||
|
upwind = CommandFactory.createCommand(race, boat, BoatActionEnum.UPWIND);
|
||||||
|
downwind = CommandFactory.createCommand(race, boat, BoatActionEnum.DOWNWIND);
|
||||||
|
|
||||||
|
initial = boat.getBearing().degrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the difference between initial and final angle is 3 degrees
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void upwindCommandDecreasesAngle() {
|
||||||
|
upwind.execute();
|
||||||
|
assertEquals(initial - boat.getBearing().degrees(), -offset, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void downwindCommandIncreasesAngle() {
|
||||||
|
downwind.execute();
|
||||||
|
assertEquals(initial - boat.getBearing().degrees(), offset, 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user